2026年10月22日
2026年10月22日
WordPressで多言語サイトを構築する方法【WPML・Polylang・手動実装】
はじめに
WordPressを多言語対応にすることで、グローバルな読者層へリーチできます。WPML・Polylangなどのプラグインを使う方法と、カスタムコードで実装する方法があります。それぞれの特徴と実装方法を解説します。
症状・原因
- 日本語と英語の両方のコンテンツを同じWordPressで管理したい
- 言語切り替えボタンを設置して自動的に言語を変更したい
- Google検索で各言語のページが正しくインデックスされない
- hreflangタグが設定されておらずSEOで損している
解決手順
ステップ1:言語管理システムを設計する
// functions.php: シンプルな多言語管理クラス
class Simple_Multilingual {
const SUPPORTED_LANGUAGES = [
'ja' => ['label' => '日本語', 'dir' => 'ltr', 'locale' => 'ja'],
'en' => ['label' => 'English', 'dir' => 'ltr', 'locale' => 'en_US'],
'zh' => ['label' => '中文', 'dir' => 'ltr', 'locale' => 'zh_CN'],
];
// 現在の言語を取得
public static function get_current_lang(): string {
// URLのパスから言語を判定(例: /en/about-us/)
$uri = $_SERVER['REQUEST_URI'] ?? '/';
foreach (array_keys(self::SUPPORTED_LANGUAGES) as $lang) {
if (str_starts_with($uri, "/{$lang}/") || $uri === "/{$lang}") {
return $lang;
}
}
// デフォルトは日本語
return 'ja';
}
// 投稿の翻訳IDを取得
public static function get_translation_id(int $post_id, string $lang): int {
$translation_id = (int) get_post_meta($post_id, "_translation_{$lang}", true);
return $translation_id ?: $post_id;
}
// 言語切り替えURL一覧を生成
public static function get_language_switcher_urls(int $post_id = 0): array {
if (!$post_id) $post_id = get_the_ID();
$urls = [];
foreach (self::SUPPORTED_LANGUAGES as $lang => $config) {
$translated_id = self::get_translation_id($post_id, $lang);
$url = $lang === 'ja'
? get_permalink($translated_id)
: home_url("/{$lang}/") . get_post_field('post_name', $translated_id) . '/';
$urls[$lang] = [
'url' => $url,
'label' => $config['label'],
];
}
return $urls;
}
}
ステップ2:翻訳投稿管理メタボックスを追加する
// 翻訳管理メタボックス
add_action('add_meta_boxes', function(): void {
add_meta_box(
'multilingual_translations',
'翻訳設定',
function(WP_Post $post): void {
$translations = [
'en' => (int) get_post_meta($post->ID, '_translation_en', true),
'zh' => (int) get_post_meta($post->ID, '_translation_zh', true),
];
echo '<table>';
foreach (['en' => '英語', 'zh' => '中国語(簡体)'] as $lang => $label) {
$trans_id = $translations[$lang] ?? 0;
$trans_url = $trans_id ? get_edit_post_link($trans_id) : '';
printf(
'<tr><th>%s</th><td><input type="number" name="translation_%s" value="%d" placeholder="翻訳投稿ID"></td>',
esc_html($label),
esc_attr($lang),
$trans_id
);
if ($trans_url) {
printf('<td><a href="%s" target="_blank">編集</a></td>', esc_url($trans_url));
}
echo '</tr>';
}
echo '</table>';
wp_nonce_field('save_translations', 'translation_nonce');
},
'post',
'side'
);
});
// 翻訳IDを保存
add_action('save_post', function(int $post_id): void {
if (!wp_verify_nonce($_POST['translation_nonce'] ?? '', 'save_translations')) return;
if (!current_user_can('edit_post', $post_id)) return;
foreach (['en', 'zh'] as $lang) {
$value = (int) ($_POST["translation_{$lang}"] ?? 0);
if ($value) {
update_post_meta($post_id, "_translation_{$lang}", $value);
} else {
delete_post_meta($post_id, "_translation_{$lang}");
}
}
});
ステップ3:hreflangタグを出力する
// SEO: hreflangタグをheadに出力
add_action('wp_head', function(): void {
if (!is_singular()) return;
$post_id = get_the_ID();
// 現在の言語のURL
$current_lang = Simple_Multilingual::get_current_lang();
$switcher_urls = Simple_Multilingual::get_language_switcher_urls($post_id);
echo "\n<!-- hreflang tags for multilingual SEO -->\n";
foreach ($switcher_urls as $lang => $data) {
printf(
'<link rel="alternate" hreflang="%s" href="%s">' . "\n",
esc_attr($lang),
esc_url($data['url'])
);
}
// x-default(デフォルト言語)
printf(
'<link rel="alternate" hreflang="x-default" href="%s">' . "\n",
esc_url(get_permalink($post_id))
);
});
// 言語切り替えウィジェット
add_shortcode('language_switcher', function(): string {
$post_id = get_the_ID();
if (!$post_id) return '';
$urls = Simple_Multilingual::get_language_switcher_urls($post_id);
$current_lang = Simple_Multilingual::get_current_lang();
$output = '<ul class="language-switcher">';
foreach ($urls as $lang => $data) {
$class = $lang === $current_lang ? ' class="current"' : '';
$output .= sprintf(
'<li%s><a href="%s" hreflang="%s">%s</a></li>',
$class,
esc_url($data['url']),
esc_attr($lang),
esc_html($data['label'])
);
}
$output .= '</ul>';
return $output;
});
ステップ4:Polylangの基本設定(プラグイン使用時)
// Polylangプラグイン使用時の統合コード
// functions.php に追加
// Polylangで翻訳投稿を取得
function get_polylang_translation(int $post_id, string $lang): int {
if (!function_exists('pll_get_post')) return $post_id;
$translated_id = pll_get_post($post_id, $lang);
return $translated_id ?: $post_id;
}
// 現在の言語を取得
function get_current_language(): string {
if (function_exists('pll_current_language')) {
return pll_current_language('slug') ?: 'ja';
}
return Simple_Multilingual::get_current_lang();
}
// 言語別のWP_Queryクエリ最適化
add_action('pre_get_posts', function(WP_Query $query): void {
if (!function_exists('pll_current_language')) return;
if (!$query->is_main_query() || is_admin()) return;
// Polylangが自動的に言語フィルターを適用するため
// 追加の処理は不要(参考として記載)
});
// WPMLと互換性のある翻訳取得
function get_wpml_translation(int $post_id, string $lang): int {
if (!function_exists('wpml_object_id_filter')) return $post_id;
return (int) apply_filters('wpml_object_id', $post_id, 'post', true, $lang);
}
ステップ5:言語別サイトマップを設定する
# WP-CLIで言語別の設定確認
wp option get WPLANG # 現在の言語設定
wp rewrite flush # リライトルールをリフレッシュ
# Polylangプラグインをインストール
wp plugin install polylang --activate
# Polylangの設定確認
wp pll languages # 登録済み言語リスト
# hreflangタグの確認
curl -s https://example.com/about/ | grep hreflang
注意事項
- URL構造: 多言語サイトのURL構造(
/en/page/vsexample.en/page/vsen.example.com/page/)はSEOに影響します。サブディレクトリ方式(/en/)が最も管理しやすいです - コンテンツ管理: 翻訳コンテンツは別々の投稿として管理します。自動翻訳APIとの連携も可能ですが、品質管理に注意してください
- キャッシュ: 言語別にキャッシュを分けることが重要です。CloudflareやWP Rocketでは言語ごとのキャッシュ設定が可能です
まとめ
多言語サイトの実装は「URL設計→翻訳投稿管理→hreflangタグ出力→言語切り替えUI→言語別サイトマップ」の5ステップです。小規模サイトには手動実装、中〜大規模サイトにはPolylang(無料)またはWPML(有料)の使用を推奨します。関連記事:WordPressのスキーママークアップ実装