WordPressには、同一サイト内に多言語(他の言語)のページを作成するプラグインがいくつもあり、どれも結構便利に使えるものの、どうなってるの?を知りたいのであえてプラグインなしで構築した時の備忘録です。
最初にお断りしておきますが、代替ページを作成するにあたっては、結構いろいろなことをしなければならないですし、管理側で便利な機構(例えば日本語の編集画面と英語の編集画面を即時に切り替えたりなど)については触らずにおくので、管理側では結構アナログな対応が必要になるので、特にセルフメイドが必要でなければ素直に専用プラグインを使った方が無難だと思います。
なお、本ページは作りながらの作業メモですので、内容が分かりにくいところや、不備のあるところなどがあるかも知れませんが、承知の上で参考にしていただければと思います。
現在、実際にこのサイトでもこの方法で実装しており、以下のページなどで実際に英語ページが表示できます。表示後は画面右上の言語切り替えボタンをクリックしてどんなふうになるのかを確認してみてください。
本ページ掲載のコードを使用するときは
本ページで掲載しているコードは、以下に了承した上で使用ください
- コードは商用・非商用問わず自由に使っていただいて構いませんが、コード追加による不具合やトラブルが発生しても当方では一切責任を負いません
- コードは有効化しているテーマのfunctions.php、style.cssなどへ追加することで機能します。それらのファイルへの変更を行うことに不安のある方は使用しないでください
- コードは本ページの公開日時点で私の環境において動作したものです。WordPressバージョン他環境の違いによって動作しないことがあります
- コードは、セキュリティ、コードの正確さなどにおいて完全なものではありません。中には紹介するコードを簡略化するために省略している部分があるものもありますので、ご自身でコードを十分に検証し、必要な部分の編集を行った上で使用するようにしてください
- 掲載しているのは参考コードです。自身の環境に合わせるための編集はご自身で対応いただく必要があります(コメント欄等から質問いただいても基本回答は致しません)
- 掲載しているコードの転載を禁じます(SNSで紹介いただいたり、本ページへのリンクを張っていただくことは大歓迎です)
必要な機構や仕様
サイト内に他の言語(今回は英語の代替ページ)を作るには、以下の要件が必要かと思われます。
- 英語ページを作成・管理するカスタム投稿タイプを作る
- 英語のページでは、内部の言語設定(ロケール)を英語にし、日本語翻訳がされないようにする
- 代替ページの有無を判断する項目を作る
- 他の言語ページが存在することをHTMLソース(head内)に出力する
- 英語ページのHTMLタグの言語属性を英語にする
- hreflang属性を持つlinkタグで日本語ページ・英語ページ・デフォルトページのURLを出力する
- canonical属性を持つlinkタグを出力し、日本語ページを主ページと宣言する(重複対策)
- ブロックテーマで英語用のテンプレート・パーツを作る
- ページ内に日本語→英語、英語→日本語ページへ遷移するリンクを表示する
- 編集画面内に日本語→英語、英語→日本語ページへ遷移するリンクを表示する
なお、今回はあくまでも日本語サイト内で、個別投稿に対する英語の代替ページを作成することが目的ですので、アーカイブ(英語ページの一覧や日本語と混ぜての一覧)は考慮に入れず、検索フォームからの検索も対象外としていますので、そのあたりの機能が必要な方はご自身でさらにカスタマイズを行ってください。
本ページの仕組みを追加した後、どのように運営するのかについては後述の「運営方法」をご覧ください(こちらを先に見てから導入するかの判断をした方がいいかも知れません)。
コード類の実装
以下に紹介するコードでは、後々改修を行う際に分かりやすくする目的で、あえて処理を分けたりしている部分があります。気になる方は自身で改修してください
英語ページを作成・管理するカスタム投稿タイプを作る
英語の代替ページ群を投稿とは異なるページの集合として管理・運営するため、以下のコードを使って、「pwcn_en」という名前を持つカスタム投稿タイプを作成します。
本ページでは英語の代替ページに「pwcn_en」というカスタム投稿タイプ名を使用し、以降のコードではすべて「pwcn_en」投稿タイプを作成した前提で進めます。サイト内に既にpwcn_enというスラグを持つページがあったり、サブディレクトリなどに「pwcn_en」という階層や単独ページがある場合は別の名前にして置き換えてください
カスタム投稿タイプを登録するコード
以下のコードを有効化しているテーマのfunctions.phpへ追加します。
/*** en投稿タイプを登録 ***/
function pwcn_register_my_cpts_en() {
$labels = [
"name" => esc_html__( "英語ページ", "hatwentytwentythree" ),
"singular_name" => esc_html__( "英語ページ", "hatwentytwentythree" ),
];
$args = [
"label" => esc_html__( "英語ページ", "hatwentytwentythree" ),
"labels" => $labels,
"description" => "",
"public" => true,
"publicly_queryable" => true,
"show_ui" => true,
"show_in_rest" => true,
"rest_base" => "",
"rest_controller_class" => "WP_REST_Posts_Controller",
"rest_namespace" => "wp/v2",
"has_archive" => false,
"show_in_menu" => true,
"show_in_nav_menus" => true,
"delete_with_user" => false,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => false,
"can_export" => false,
"rewrite" => [ "slug" => "en", "with_front" => true ],
"query_var" => true,
"menu_icon" => "dashicons-admin-site",
"supports" => [ "title", "editor", "thumbnail", "excerpt", "trackbacks", "custom-fields", "comments", "revisions", "author" ],
"show_in_graphql" => false,
];
register_post_type( "pwcn_en", $args );
}
add_action( 'init', 'pwcn_register_my_cpts_en' );
このコードでは以下のように指定しています。
- 「pwcn_en」という名前の投稿タイプを作る
- フロントエンドでは「en」というスラグになるようにリライト
- アーカイブは生成しない
- サイト内検索の対象から除外
補足しておくと、1はサイト内部で認識されるのに必要な名前で、これはサイト内で一意(他と競合しない)になるようにしなければならないため、「pwcn_en」という名前にしています(恐らく競合することはないと思います)。
一方、カスタム投稿タイプは標準ではカスタム投稿タイプ名がそのままスラグ(表示URLの一部)になるようになっていて、2を行わない場合には「ドメイン名/pwcn_en/ページスラグ」になります。これでもまあ問題はないのですが、なんとなく見た目的に「pwcn_en」よりも「en」の方がいいかなということで、rewriteパラメーターでそのように指定しています。
また、あくまでも英語ページは日本語ページの代替というサイト内での扱いにしたいため、フロントエンドでの英語ページの一覧や、サイト内検索からの除外をしているので、必要であれば有効にして調整してください。
上記コードを追加して保存した後、必ずパーマリンクのフラッシュ(「設定」→「パーマリンク」を開き、何もしないで更新)を行ってください
【補足】投稿タイプ名(スラグ)をコールバック関数化しておく
今回のカスタマイズでは、「日本語ページ(post)」だったら..「英語ページ(pwcn_en)」だったら..というのがかなり出てきます。もしも英語ページ用のカスタム投稿タイプ名(スラグ)をen以外のものにした時に、すべてのコードを書き換えるのはとても面倒なので、以下のコードをコールバック関数として追加して使いまわすようにしておきます。
/*** en投稿タイプを使いまわす(以降の関数でenを変更しなくてもよくする) ***/
function pwcn_set_en_posttype_callback(){
$posttype = 'pwcn_en';
return $posttype;
}
こうしておくことで、異なるカスタム投稿タイプ名を使用する際に、このコールバック関数の「en」の部分だけを替えれば以降のコードは何も変更する必要がなくなって便利です
以降のコードはこのコールバック関数を使用した前提で紹介していきます。このコールバック関数を使用しない場合は、各コード中の以下の記述にある「pwcn_set_en_posttype_callback()」の部分を「’pwcn_en’」へ変更してください
$posttype = pwcn_set_en_posttype_callback();
【補足】パーマリンクを投稿IDにする(使用には注意が必要)
カスタム投稿タイプは、パーマリンク構造が標準で投稿名になるので、自動でIDがスラグになるようにしたいという場合のコードです。
/*** en投稿タイプのパーマリンクをIDにする ***/
/* カスタム投稿名が"en"の投稿のパーマリンクを「/en/投稿ID/」の形に書き換え */
function pwcn_custom_post_link($link, $post) {
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
if($post -> post_type === $posttype ) {
return home_url('/en/'.$post->ID);
} else {
return $link;
}
}
add_filter('post_type_link', 'pwcn_custom_post_link', 1, 2);
//書き換えたパーマリンクに対応したリライトルールを追加
function pwcn_custom_post_link_rewrite($rules) {
$rewrite_rules = array(
'en/([0-9]+)/?$' => 'index.php?post_type='.$posttype.'&p=$matches[1]',
);
return $rewrite_rules + $rules;
}
add_filter('rewrite_rules_array', 'pwcn_custom_post_link_rewrite');
コード自体は、「【WordPress】カスタム投稿のパーマリンクを投稿IDに変更する」を参考にさせていただきました。
コード挿入したら「設定」→「パーマリンク設定」で何もせずに更新(パーマリンクのフラッシュ)をする必要がありますので忘れずに!
このコードを使った場合に、他のページへの埋め込みで「埋め込みが見つかりません。」という表示になっている現象が発生し、いろいろと試行錯誤したり他の参考コードなどを試してみましたが、不具合は解消できませんでした。もしも英語ページへのリンクをWordPress標準のembedカード表示させたい場合は、このコードを使用せず、手動でパーマリンク(スラグ)を見直してから公開するようにしてください
英語のページでは、内部の言語設定(ロケール)を英語にする
以下のコードを追加して、WordPress内部で英語ページとして扱われるようにします。これにより、他のプラグインなどで出力される文字列がWordPressの基本言語(英語)になります。
/*** 特定の投稿タイプを英語(en_US)に切り替える ***/
function pwcn_specific_post_type_change_locale($template){
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
if ( is_singular( $posttype ) ) {
switch_to_locale('en_US');
}
return $template;
}
add_filter( 'template_include','pwcn_specific_post_type_change_locale');
この措置だけではテーマで日本語として書いている部分は英語にはなりません。テーマの編集方法については後述します
代替ページの有無を判断する項目を作る
日本語ページ(投稿)、英語ページ(pwcn_en)それぞれの編集画面に、以下のような入力ができる項目を作り、カスタムフィールドとして保存させます。
- 日本語(投稿)の編集画面には、英語ページのIDを入力する窓を設ける
- 英語(pwcn_en)の編集画面には、日本語ページのIDを入力する窓を設ける
以下のコードを追加することで、入力窓の表示とカスタムフィールドとしての保存・変更ができるようになります。
/*** 編集画面に日本語・英語それぞれのIDを入力する ***/
/** 日本語ページ用 **/
/* パネルを追加 */
function pwcn_add_en_metabox() {
add_meta_box(
'pwcn_en_slug_panel', // 編集画面セクションのHTML要素のID
'英語ページのID', // 編集画面セクションのタイトル
'pwcn_add_en_metabox_inner', // 編集画面セクションにHTML出力する関数
'post', // 投稿タイプ
'normal',// 編集画面セクションが表示される部分
'high'//位置
);
}
add_action('admin_menu', 'pwcn_add_en_metabox');
/* パネルの内容 */
function pwcn_add_en_metabox_inner(){
//英語ページのID
$en_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-english-page-id', true));
//フィールドの表示
?>
<table class="ha-japanize-panel-fields">
<tbody>
<!-- 英語ページのID -->
<tr>
<td><?php _e('英語ページのID:',''); ?></td>
<td>
<input name="pwcn-english-page-id" type="text" id="pwcn-english-page-id" value="<?php echo esc_attr($en_data); ?>" size="50" style="width:100%;">
</td>
</tr>
</tbody>
</table>
<?php
}
/* 値を保存する処理 */
function pwcn_en_metabox_data_save($post_id){
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
//自動保存では更新しない
if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
return $post_id;
//一括更新では更新しない
if(isset($_POST['action']) && $_POST['action'] == 'inline-save')
return $post_id;
//英語ページのID
if(isset($_POST['pwcn-english-page-id'])){
update_post_meta($post_id, 'pwcn-english-page-id', $_POST['pwcn-english-page-id']);
} else {
delete_post_meta($post_id, 'pwcn-english-page-slug');
}
}
add_action('save_post', 'pwcn_en_metabox_data_save');
/** 英語ページ用 **/
/* パネルを追加 */
function pwcn_add_jp_metabox() {
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
add_meta_box(
'pwcn_jp_slug_panel', // 編集画面セクションのHTML要素のID
'日本語ページのID', // 編集画面セクションのタイトル
'pwcn_add_jp_metabox_inner', // 編集画面セクションにHTML出力する関数
$posttype, // 投稿タイプ
'normal',// 編集画面セクションが表示される部分
'high'//位置
);
}
add_action('admin_menu', 'pwcn_add_jp_metabox');
/* パネルの内容 */
function pwcn_add_jp_metabox_inner(){
//日本語ページのID
$jp_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-japan-page-id', true));
//フィールドの表示
?>
<table class="ha-japanize-panel-fields">
<tbody>
<!-- 日本語ページのID -->
<tr>
<td><?php _e('日本語ページのID:',''); ?></td>
<td>
<input name="pwcn-japan-page-id" type="text" id="pwcn-japan-page-id" value="<?php echo esc_attr($jp_data); ?>" size="50">
</td>
</tr>
</tbody>
</table>
<?php
}
/* 値を保存する処理 */
function pwcn_jp_metabox_data_save($post_id){
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
//自動保存では更新しない
if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
return $post_id;
//一括更新では更新しない
if(isset($_POST['action']) && $_POST['action'] == 'inline-save')
return $post_id;
//日本語ページのスラグ
if(isset($_POST['pwcn-japan-page-id'])){
update_post_meta($post_id, 'pwcn-japan-page-id', $_POST['pwcn-japan-page-id']);
} else {
delete_post_meta($post_id, 'pwcn-japan-page-id');
}
}
add_action('save_post', 'pwcn_jp_metabox_data_save');
ここまで行った段階で、一度以下の点を管理画面で確認しておきましょう。もしも何か1つでもできないようであれば、ここよりも上の内容を再確認してください。
- 管理画面に「英語ページ」というメニューがありますか?
- 「英語ページ」メニューをクリックすると、投稿と同じような一覧画面が表示されますか?
- 「英語ページ」一覧から新規追加をクリックすると、コンテンツの編集画面が開きますか?
- 「投稿」の編集画面の本文下に「英語ページのID」という入力窓が表示されますか?
- 「英語ページのID」という窓に何かを入力して更新(下書き保存など)はできますか?
- 「英語ページ」の編集画面の本文下に「日本語ページのID」という入力窓が表示されますか?
- 「日本語ページのID」という窓に何かを入力して更新(下書き保存など)はできますか?
OKであれは次へ進みます...。カスタマイズはまだまだ続きます。ここまででちょっと大変かもと感じた方は、これまで追加したコードを削除して、素直にプラグインに頼った方がいいと思います..。
他の言語ページが存在することをHTMLソース(head内)に出力する
前述の「英語のページでは、内部の言語設定(ロケール)を英語にし、日本語翻訳がされないようにする」のコードを追加した段階で、今回作成した「pwcn_en」というカスタム投稿タイプの個別ページを開くと、HTMLヘッダーに出力される「html」タグの「lang」属性は自動で「en_US」(英語)になるので、ここではその他の出力部分を実装していきます。
以下のコードを追加します。
/*** hreflangタグの出力 ***/
/* 英語ページ */
function pwcn_sp_posttype_hreflang_en_in_head(){
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
$en_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-english-page-id', true));
$jp_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-japan-page-id', true));
$home = esc_url(home_url());
$output = '';
//英語ページの処理
if(is_singular($posttype)){
$jp_url = esc_url(get_permalink($jp_data));
$en_url = esc_url(get_permalink());
//get_post_status()で日本語ページが存在しなければ出力しない
//ただし、単純にIDを持つページが存在しているかだけを判断しているので注意
if(!empty($jp_data) && get_post_status( $jp_data )){
//英語ページのURL
$output .= '<link rel="alternate" hreflang="en" href="'.$en_url.'">';
//日本語ページのURL
$output .= '<link rel="alternate" hreflang="ja" href="'.$jp_url.'">';
//基本となるURL(日本語のURLと同じ)
$output .= '<link rel="alternate" hreflang="x-default" href="'.$jp_url.'">';
}else{}//日本語ページのスラグがなければ出力しない
}
//日本語ページの処理
if(is_singular('post')){
$en_url = esc_url(get_permalink($en_data));
$jp_url = esc_url(get_permalink());
//get_post_status()で英語ページが存在しなければ出力しない
//ただし、単純にIDを持つページが存在しているかだけを判断しているので注意
if(!empty($en_data) && get_post_status( $en_data )){
//英語ページのURL
$output .= '<link rel="alternate" hreflang="en" href="'.$en_url.'">';
//日本語ページのURL
$output .= '<link rel="alternate" hreflang="ja" href="'.$jp_url.'">';
//基本となるURL(日本語のURLと同じ)
$output .= '<link rel="alternate" hreflang="x-default" href="'.$jp_url.'">';
}else{}//英語ページのスラグがなければ出力しない
}
echo $output;
}
add_action('wp_head','pwcn_sp_posttype_hreflang_en_in_head');
このコードでは以下のような処理を行っています。
- 日本語ページに「英語ページのID」への入力があり、そのIDを持つページが存在すれば、hreflang属性を持つlinkタグをヘッダー内へ出力する。入力がない、もしくはIDを持つページが存在しなければ、日本語ページのみが存在すると判断し、linkタグは出力しない。
- 英語ページに「日本語ページのID」への入力があり、そのIDを持つページが存在すれば、hreflang属性を持つlinkタグをヘッダー内へ出力する。入力がない、もしくはIDを持つページが存在しなければ、英語ページのみが存在すると判断し、linkタグは出力しない。
これにより、日本語・英語両方のページが存在すれば、別言語(英語)の代替ページがあることと、基本となるページが日本語ページであることを検索エンジン等に伝えることができます。
また、日本語、または英語のページしか存在しない場合には、linkタグを出力しないので、代替ページは存在しないという形(要するに通常の投稿と同じ扱い)になります。
canonical属性を持つlinkタグの出力
hreflang属性を持つlinkタグを適切に出力しておけば他の言語ページが存在することを明示できるとは思いますが、念のため、このページが正規ページ(基のページ)であることをより明確にするため、以下のコードでcanonical属性を持つlinkタグの出力を行っておきます。
/*** canonicalタグの出力 ***/
/* WordPress標準のcanonicalタグの出力を停止 */
remove_action('wp_head', 'rel_canonical');
function pwcn_output_canonical_tag(){
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
$en_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-english-page-id', true));
$jp_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-japan-page-id', true));
$home = esc_url(home_url());
$output = '';
//日本語ページ・英語ページの処理
if(is_singular('post') || is_singular($posttype)){
$canonical_url = esc_url(get_permalink());
$output = '<link rel="canonical" href="'.$canonical_url.'" />';
}
//ついでにその他のページでも出力
//フロントページ
if ( is_home() || is_front_page() ) {
$canonical_url = esc_url(home_url('/'));
$output = '<link rel="canonical" href="'.$canonical_url.'" />';
}
//カテゴリーアーカイブ
if (is_category()) {
$canonical_url = esc_url(get_category_link(get_query_var('cat')));
$output = '<link rel="canonical" href="'.$canonical_url.'" />';
}
//タグアーカイブ
if (is_tag()) {
$canonical_url = esc_url(get_tag_link(get_query_var('tag_id')));
$output = '<link rel="canonical" href="'.$canonical_url.'" />';
}
//作者アーカイブ
if (is_author()) {
$canonical_url = esc_url(get_author_posts_url(get_query_var('author')));
$output = '<link rel="canonical" href="'.$canonical_url.'" />';
}
//トップページ以外の固定ページ
if (is_page() && !is_front_page()){
//} else if (is_page() || haup_is_single()) {
$canonical_url = esc_url(get_permalink());
$output = '<link rel="canonical" href="'.$canonical_url.'" />';
}
echo $output;
}
add_action('wp_head', 'pwcn_output_canonical_tag');
上記コードでは、一旦WordPressが標準で出力するcanonical属性を持つlinkタグを無効にした上で、改めて出力しなおしていますので、日本語・英語ページ以外のページ(トップページやアーカイブなど)でも適切なlinkタグを出力するようにしています。
日本語ページと英語ページでは書いてある言語が違うだけなので重複になってしまうのでは?と思われるかもしれませんが、先の措置で出力しているhreflang属性によって、異なるページであることを明確にしている(つまりどちらかがどちらかのサブページではない)ので、それぞれのURLをcanonical属性として出力するのが適正だと考え、そのようにコードを書いています。あくまでも日本語を正、英語を副と捉えるなら、そのようにコードを書きなおしてください
もしもSEO系?のプラグイン等でcanonical属性を持つlinkタグの出力を行っている場合は停止しないと2重に出力されたり、異なるURLが出力されたりしますので実際に出力されているソースを確認し、調整してください
ブロックテーマで英語用のテンプレート・パーツ・メニューなどを作る
ここまで行ったロケール変更などの措置で、ページ内にある翻訳対象の文字列はすべて英語に戻るものの、ヘッダーやフッターなどの内容は日本語になってしまって具合が悪いので、英語用のテンプレートを作ります。
これはブロックテーマ用の内容ですので、クラシックテーマの場合にはテーマ構造を変更するなどの措置が必要です
サイトエディター(ブロックテーマ用のテンプレート編集エディター)を開き、テンプレートの追加を選択し、「英語ページ」用のテンプレートを追加します(きちんとここまでのコードが追加できていれば、選択肢に出てくるはずです)。



次にテンプレートを選択する画面が表示され、通常の投稿と同じレイアウトが選択できるようになっていますので、一旦それを選択してテンプレートを作成します。

以降、「英語ページ」のコンテンツは作成したテンプレートを通じて表示されるようになります。この時点で試しに英語ページを1つサンプルとして作成し、テンプレート内に「これは英語用のテンプレートだよ」などと文字を入れた状態で表示してみると、このテンプレートを通じて表示されることを確認できます
テンプレートが作成できたら、そのままではヘッダーやフッターなどが日本語のままなので、ヘッダー・フッターのテンプレートパーツを作り、英語表記に変え(サイトタイトルやキャッチフレーズは段落ブロックで代替)、英語ページテンプレートのヘッダー・フッターを作成した英語のものへ差し替えます。
さらに、メニュー項目があるサイトでは表記が日本語なので、英語でのメニューを作成して差し替えます(表記を替えてもメニューをクリックした遷移先が日本語のページになるので、メニューを表示しなくてもいいのでは?と個人的には思います)。
これで英語のページはすべて英語表記になります(さらっと書きましたが、この部分が実は大変かも..ただクラシックテーマのようにテンプレートファイルを追加してリネームしてPHPコード書き換えてという難題がブロックテーマではGUI上でできるので、作業はかなり軽減されると思います)。
ページ上に別言語のページへ遷移するリンクを表示する
最後に、必要であれば、代替ページがある際に、日本語ページには「View English Page」という英語ページへ遷移させるためのリンクを、英語ページには「日本語ページを開く」という日本語ページへ遷移させるリンクを表示するためのショートコードを作ります。
以下のコードを追加します。
/*** 日本語・英語ページのリンクをショートコードで出力 ***/
/* ショートコード */
function pwcn_change_jp_en_page_button(){
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
//英語ページのID
$en_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-english-page-id', true));
//日本語ページのID
$jp_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-japan-page-id', true));
//英語ページのURL
$en_url = esc_url(get_permalink($en_data));
//日本語ページのURL
$jp_url = esc_url(get_permalink($jp_data));
$output = '';
//日本語ページのボタン出力(英語IDがあり、かつ、そのIDの投稿が存在する場合のみ出力)
if(is_singular('post') && !empty($en_data) && get_post_status( $en_data )){
$output .= '<div class="pwcn-lang-change-wrapper">';
$output .= '<a href="'.$en_url.'">View English Page</a>';
$output .= '</div>';
}
//英語ページのボタン出力(日本語IDがあり、かつ、そのIDの投稿が存在する場合のみ出力)
if(is_singular($posttype) && !empty($jp_data) && get_post_status( $jp_data )){
$output .= '<div class="pwcn-lang-change-wrapper">';
$output .= '<a href="'.$jp_url.'">日本語ページを見る</a>';
$output .= '</div>';
}
return $output;
}
add_shortcode('change-lang','pwcn_change_jp_en_page_button');
/* リンクのスタイルを出力 */
function pwcn_change_jp_en_page_button_style(){
?>
<style>
.pwcn-lang-change-wrapper {text-align: center;border: 1px solid #777;border-radius: 4px;padding: 8px;}
.pwcn-lang-change-wrapper a {color: #000;text-decoration:none;}
.pwcn-lang-change-wrapper:hover{background:#777;}
.pwcn-lang-change-wrapper a:hover {color:#fff;}
</style>
<?php
}
add_action('wp_head','pwcn_change_jp_en_page_button_style');
コード挿入後、以下のショートコードを投稿テンプレートと英語テンプレートの任意の場所へ挿入すれば、リンクが表示されます。
[change-lang]
日本語ページなのか英語ページなのかによって自動で遷移先URLと文字列が変わるようにしていますので、双方のテンプレートには上記の同じショートコードを挿入すれば大丈夫です
お疲れさまでした。かなりの工数になりましたが、ここまでで日本語ページ・英語ページを同一サイト内で構成する機能は出来上がりました。
ここからは、管理を楽にするためのカスタマイズを追加していきます。
編集画面内に別言語の編集画面へのリンクを表示する
投稿編集画面には英語ページの編集画面を、英語ページの編集画面には日本語(投稿)の編集画面を開くリンクを表示させ、双方の編集画面をすぐに開けるようにして管理しやすくします。
以下のコードを追加します。
/*** 編集画面へ投稿→英語、英語→投稿への編集リンクを作る ***/
/* 投稿編集画面の処理 */
/* パネルを追加 */
function pwcn_add_en_edit_link_metabox() {
add_meta_box(
'pwcn_en_edit_link_panel', // 編集画面セクションのHTML要素のID
'英語ページの編集', // 編集画面セクションのタイトル
'pwcn_add_en_edit_link_metabox_inner', // 編集画面セクションにHTML出力する関数
'post', // 投稿タイプ
'side',// 編集画面セクションが表示される部分
'high'//位置
);
}
add_action('admin_menu', 'pwcn_add_en_edit_link_metabox');
function pwcn_add_en_edit_link_metabox_inner(){
//英語ページのID
$en_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-english-page-id', true));
//ホームURL
$home = esc_url(home_url());
//編集画面のURL
$edit_url = $home.'/wp-admin/post.php?post='.$en_data.'&action=edit';
//get_post_status()で英語ページが存在しなければ出力しない
//ただし、単純にIDを持つページが存在しているかだけを判断しているので注意
if(!empty($en_data) && get_post_status( $en_data )){
//フィールドの表示
?>
<a href="<?php echo $edit_url; ?>" target="_blank">英語ページの編集画面を開く</a>
<?php
}else{
?>
<p>英語ページはありません</p>
<?php
}
}
/* 英語編集画面の処理 */
/* パネルを追加 */
function pwcn_add_jp_edit_link_metabox() {
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
add_meta_box(
'pwcn_jp_edit_link_panel', // 編集画面セクションのHTML要素のID
'日本語ページの編集', // 編集画面セクションのタイトル
'pwcn_add_jp_edit_link_metabox_inner', // 編集画面セクションにHTML出力する関数
$posttype, // 投稿タイプ
'side',// 編集画面セクションが表示される部分
'high'//位置
);
}
add_action('admin_menu', 'pwcn_add_jp_edit_link_metabox');
function pwcn_add_jp_edit_link_metabox_inner(){
//日本語ページのID
$jp_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-japan-page-id', true));
//ホームURL
$home = esc_url(home_url());
//編集画面のURL
$edit_url = $home.'/wp-admin/post.php?post='.$jp_data.'&action=edit';
//get_post_status()で英語ページが存在しなければ出力しない
//ただし、単純にIDを持つページが存在しているかだけを判断しているので注意
if(!empty($jp_data) && get_post_status( $jp_data )){
//フィールドの表示
?>
<a href="<?php echo $edit_url; ?>" target="_blank">日本語ページの編集画面を開く</a>
<?php
}else{
?>
<p>日本語ページはありません</p>
<?php
}
}
コードを追加すると、双方の編集画面の右サイドパネル内にリンクが表示されるようになります(別言語のページがない場合には存在しない旨が表示されます)。
各一覧へ、ページ表示と編集画面表示を行うリンクを表示する
投稿一覧画面へ英語ページの「表示」と「編集」画面を開くリンクを、英語ページ一覧へ日本語ページの「表示」と「編集」画面を開くリンクをそれぞれ表示させ、一覧から管理をしやすくします。
以下のコードを追加します。
/*** 一覧へ投稿→英語、英語→投稿へのリンクを作る ***/
/* カラムを追加 */
function pwcn_add_jp_en_page_link_column( $posts_columns ) {
global $post;
$posttype = pwcn_set_en_posttype_callback();//投稿タイプの呼び出し
if (get_post_type( $post ) == 'post'){//投稿のみに列を表示
$posts_columns['pwcn_en_link'] = __( '英語ページ', '' );
}
if (get_post_type( $post ) == $posttype){//投稿のみに列を表示
$posts_columns['pwcn_jp_link'] = __( '日本語ページ', '' );
}
return $posts_columns;
}
add_filter( 'manage_posts_columns', 'pwcn_add_jp_en_page_link_column' );
/* カラム内に表示するデータ */
function pwcn_jp_en_page_link_column_data( $column_name, $post_id ) {
//英語ページのID
$en_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-english-page-id', true));
//日本語ページのID
$jp_data = esc_attr(get_post_meta(get_the_ID(), 'pwcn-japan-page-id', true));
//ホームURL
$home = esc_url(home_url());
//編集画面のURL
$edit_url = $home.'/wp-admin/post.php?post='.$en_data.'&action=edit';
//データの出力
switch ( $column_name ) {
case 'pwcn_en_link':
$front_link = get_permalink($en_data);
$edit_link = $home.'/wp-admin/post.php?post='.$en_data.'&action=edit';
$output = '';
if(!empty($en_data) && get_post_status( $en_data )){
$output .= '<a href="'.$front_link.'" target="_blank">表示</a>';
$output .= '<br>';
$output .= '<a href="'.$edit_link.'" target="_blank">編集</a>';
}
echo $output;
break; // caseの終了
case 'pwcn_jp_link':
$front_link = get_permalink($jp_data);
$edit_link = $home.'/wp-admin/post.php?post='.$jp_data.'&action=edit';
$output = '';
if(!empty($jp_data) && get_post_status( $jp_data )){
$output .= '<a href="'.$front_link.'" target="_blank">表示</a>';
$output .= '<br>';
$output .= '<a href="'.$edit_link.'" target="_blank">編集</a>';
}
echo $output;
break; // caseの終了
}
}
add_action( 'manage_posts_custom_column', 'pwcn_jp_en_page_link_column_data', 5, 2 );
/* 管理画面のパネル内に収めるためのスタイル */
function pwcn_jp_en_page_link_column_style(){
?>
<style>
.fixed .column-pwcn_en_link,
.fixed .column-pwcn_jp_link
{
width:14%;
text-align:center;
}
</style>
<?php
}
add_action('admin_head','pwcn_jp_en_page_link_column_style');
コード挿入後、各一覧を表示すると、別言語のページが存在する場合のみ「表示」「編集」のリンクが表示されます。この欄が不要な場合はコードを削除するか、一覧右上の「表示オプション」でチェックを外します。
作者プロフィール表示用のショートコードを作る
WordPress標準の投稿者ブロックや作者名ブロックは、ユーザーごとのプロフィール画面の「ブログ上の表示名」や「プロフィール情報」を引っ張ってきて表示させるものなので、この項目が日本語の場合は、英語テンプレートへこのブロックを挿入しても日本語のプロフィールになってしまいます。
まあ、これらの項目に英語表記の部分をちょろっと書いておけば済む感じでもあるのですが、できればきちんと行っておきたいということで、プロフィール画面へ英語表記用の項目を設け、投稿者ブロックと同じようなスタイルで出力できるショートコードを作って、英語テンプレートではこのショートコードでプロフィール表示させるようにします。
プロフィール設定画面に英語表記用の項目を作る
まずは英語ページの作者情報に表示する「表示名」と「プロフィール情報」を入力する欄を追加します。
以下のコードを追加します。
/* 管理画面のプロフィールへ、英語テンプレート用の「名前」「プロフィール」欄を追加 */
function pwcn_add_profile_for_en_fields( $user ) {
?>
<h2><?php echo __('Data for English Page Author Bio','' ); ?></h2>
<table class="pwcn_en-userdata-table">
<tr>
<th style="text-align:left;">
<label for="pwcn-en-nicename">
<?php echo __( 'En Page NiceName','' ); ?>
</label>
</th>
<td>
<input type="text" name="pwcn-en-usernicename" id="pwcn-en-usernicename" value="<?php echo esc_attr( get_the_author_meta( 'pwcn-en-usernicename', $user->ID ) ); ?>" size="50" />
</td>
</tr>
<tr>
<th style="text-align:left;">
<label for="pwcn-en-bio">
<?php echo __( 'En Page Author Bio','' ); ?>
</label>
</th>
<td>
<textarea name="pwcn-en-author-bio" id="pwcn-en-author-bio" cols="100" rows="3" ><?php echo get_the_author_meta( 'pwcn-en-author-bio', $user->ID ); ?></textarea>
</td>
</tr>
</table>
<?php
}
add_action( 'show_user_profile', 'pwcn_add_profile_for_en_fields' );
add_action( 'edit_user_profile', 'pwcn_add_profile_for_en_fields' );
/* プロフィールに追加した項目を保存 */
function pwcn_update_profile_en_profile_fields( $user_id ) {
// 現在のユーザーに[$user_id]を編集する権限があることを確認
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
// 追加した任意の各フィールドを保存用に配列化(カスタムフィールドのキー名と値)
$meta_keys = array(
'pwcn-en-usernicename' => $_POST['pwcn-en-usernicename'],
'pwcn-en-author-bio' => $_POST['pwcn-en-author-bio'],
);
// [$user_id]のユーザーメタを作成または更新
foreach( $meta_keys as $key => $value ) {
update_user_meta( $user_id, $key, $value );
}
return true;
}
add_action( 'personal_options_update', 'pwcn_update_profile_en_profile_fields' );
add_action( 'edit_user_profile_update', 'pwcn_update_profile_en_profile_fields' );
これで、プロフィール設定画面へ以下のような項目が追加され、入力した情報が保存できるようになります。

表示されたら、入力して保存できるかを試してください。
英語テンプレート用のプロフィールを表示するショートコードを作成する
前項で追加した項目に入力した英語の情報を出力するためのショートコードを作ります。
以下のコードを追加します。
/* 作者情報を表示するショートコード */
function pwcn_author_info_shortcode() {
$post_id= get_the_ID();
$author_id = get_post_field ('post_author',$post_id);
$author_name = get_the_author_meta('pwcn-en-usernicename',$author_id);
$author_description = get_the_author_meta('pwcn-en-author-bio',$author_id);
$author_avatar = get_avatar($author_id, 48); // 150はアバターのサイズです
$author_url = get_author_posts_url($author_id);
$output = '';
$output = '<div style="padding-top:8px;padding-bottom:8px;padding-left:8px;padding-right:8px;margin-top:20px;margin-bottom:20px;" class="wp-block-post-author">';
$output .= '<div class="wp-block-post-author__avatar">'.$author_avatar.'</div>';
$output .= '<div class="wp-block-post-author__content">';
$output .= '<p class="wp-block-post-author__name">';
$output .= '<a href="'.$author_url.'">'.$author_name.'</a></p>';
$output .= '<p class="wp-block-post-author__bio">'.$author_description.'</p>';
$output .= '</div>';
$output .= '</div>';
return $output;
}
add_shortcode('pwcn-en-author', 'pwcn_author_info_shortcode');
コード追加後、英語テンプレート内に以下のショートコードを挿入すれば、WordPress標準の投稿者ブロックによく似たスタイルのプロフィール情報が表示されるはずです。
[pwcn-en-author]
もしもうまく表示されない場合は以下のコードを追加してみてください(私の環境では、標準のブロックのスタイルが当たらない部分があり、以下のコードを補完しました)。
/* スタイルを出力 */
function pwcn_author_info_shortcode_style(){
?>
<style>
.wp-block-post-author__name {margin: 0;}
.wp-block-post-author__avatar {margin-right: 1em;}
.wp-block-post-author {display: flex;flex-wrap: unset;}
</style>
<?php
}
add_action('wp_head','pwcn_author_info_shortcode_style');
本ページで行うカスタマイズは以上です。
この機能(機構)での注意点、懸念すべき点など
これまで紹介した一連のコードで、サイト内へ別言語(今回は日本語サイトへ英語ページを追加)の機能を備えることはできましたが、現段階で私が考え得る以下のような注意事項や懸念点があります。
ページの存在確認が確実なものではない
一応コードで何かを判断する必要がある場合には、「カスタムフィールド(入力窓へのID入力)があるかどうか」「そのIDを持つコンテンツがサイト内に存在するか」という2点で判定するようにしています。
これにより、IDが存在しないと判断した場合にはリンクを表示しないようにする等の措置をしています。
ただ、判断基準に関しては「英語ページの」「投稿の」という条件を設けていないので、誤って入力したIDが投稿や固定ページ、メディアに存在している場合には不具合が出ることがあるかも知れません。
SEOに関する懸念が残るかも
この機能では、hreflang属性を持つlinkタグで確実に別言語のページがあることを知らせており、かつ、canonical属性を持つlinkタグを出力することで、そのページが独立したものであることを明示しています。
いろいろと調べた上でこの措置で十分と判断したものの、「確実に別言語のページが存在すると認識されるか」「別言語はそれぞれ独立したページなので、canonical属性を持つlinkタグはそれぞれのページを指せば問題ないのか」という懸念が残ります。
この点については、ご自身でお調べいただき、より確実な方法で実装されることをおすすめします。
なお、日本語ページと英語ページが存在していて、検索エンジン等で多言語を持つページと認識されているかどうかについては、Hreflang Checkerなどで確認してください(現在で確認したところではGoogle Search Consoleのツールは廃止されたようです)。
プラグインやテーマによってはさらにカスタマイズが必要
今回の機能で、「en」というカスタム投稿タイプでは内部的に英語に戻す(en_USとして認識する)措置を最初の方で行っており、ある程度の環境ではうまく動作すると思います。
ただし、例えばテーマやプラグインで任意の文字列へ変更するような措置のあるものでは、前述したプロフィールコンテンツの場合のように、日本語のまま出力されてしまう(書き換えの措置をした文字列はそのまま出力される)ので、これらに対するさらなるカスタマイズが必要となるでしょう。
この点については、今回のように無理にサイト内へ英語のページを追加するよりも、別の英語サイトをサブディレクトリへ作ってそのサイトと連携を図った方が簡単かも知れません。
ただ、その場合には、2つのサイトの維持が必要となること、双方のページの存在チェックをどうするのかといった別の問題や懸念事項が出てくるので何とも言えないところだと思います。
運営方法
本ページの仕組みを導入した場合の運営イメージを最後に紹介しておきます。
英語ページの作成
管理画面のメニューに「英語ページ」というリンクが追加されます(これが英語ページの集まりになります)。
基本的な操作は通常の投稿と同じですので、英語版のページを作成します。
英語ページの内容が日本語ページと同じであれば、日本語の編集画面を開き、すべてのブロックをコピーして貼り付け後に英文に直していくと、より簡単に作成できます
英語ページの編集画面には、本文最下部に下図のようなパネルが追加されます。

ここへ元の投稿(元の日本語の投稿)のIDを入力します。
英語ページのみ(日本語ページが存在しない)の場合は空欄のままにしておきます
IDは管理画面の一覧上で目的のページのタイトルをマウスオーバーした際に画面下部へ表示されるURL内に書かれていますが、簡単に目視で調べたいという場合は以下のページを参考に、一覧上へIDを表示させる措置を行ってください。
日本語ページの操作
投稿編集画面の本文最下部に下図のようなパネルが追加されます。

英語ページが存在する場合には、英語ページのIDをこの欄に入力します。
ペアとなるページへのリンク
この仕組みでは、英語側の編集画面の「日本語ページのID」、日本語側の編集画面の「英語ページのID」の項目に入力があれば、下図のように編集画面のサイドパネルへそれぞれの編集画面へのリンクが表示されるので、そこをクリックすれば双方の編集画面を簡単に開くことができます。


一覧画面上のリンク
投稿(または英語ページ)の一覧画面上には、以下のようなリンクが追加表示されます(画像は割愛します)。
- 投稿一覧で英語の代替ページがあれば英語ページの「表示」と「編集」のリンクが表示される
- 英語ページ一覧で日本語の代替ページがあれば日本語ページの「表示」と「編集」のリンクが表示される
後述
今回は英語の代替ページを作るというものを紹介してきましたが、その他の言語を追加するとなると、必要な代替言語ページを作る分だけ今回の方法を流用して追加していくことになります。
当然その分コード量も増えてきますから、2か国以上の言語ページを使いたい場合は、テーマ内で別フォルダを作ってプログラムの管理をするか、自作プラグインとして独立させるかというのが妥当かと思います。
そう考えると、冒頭で述べたように、わざわざ自作で実装するよりも、数ある秀逸なプラグインを使った方が無難なのかも知れませんね。
プラグインだと更新されなくなるかも..とお考えかも知れませんが、ユーザー数の多い(有効化数の多い)プラグインはおいそれと廃止することはできないでしょうからね。
ただし、昨今のプラグインは結構本当に基礎部分だけを無料で公式リポジトリへ公開し、その他の機能は有料というパターンが増えていますし、あるバージョンからそのように変更となるものもありますから、その点は留意しておく必要があるのかも知れません。
コメントを残す