目的
CPT news の各記事・アーカイブに対して、対応する /en と /tc のURLを AIOSEO(All in One SEO)のサイトマップへ確実に載せる方法をまとめます。
この記事では プラグイン(mu-plugins)版 と functions.php に追記する版 の両方を掲載します。
以降のURLはすべてダミー例です。
- 例(単一記事)
https://example.com/news/1286/→https://example.com/en/news/1286//https://example.com/tc/news/1286/ - 例(アーカイブ)
https://example.com/news/→https://example.com/en/news//https://example.com/tc/news/
ページング:/news/page/2/→/en/news/page/2///tc/news/page/2/
実現方針(2レイヤーで対応)
- 単一記事(CPT
news)
AIOSEO のaioseo_sitemap_postフィルタで、各記事のサイトマップエントリに 翻訳URL(/en, /tc)を“直接”埋め込みます(hreflang対応)。 - アーカイブ(
/news/と/news/page/N/)
AIOSEO の Additional Pages(追加ページ) 機能を使って、addl-sitemap.xmlに /en, /tc を登録します。
追加ページはサイトマップインデックス(/sitemap.xml)に自動で紐づきます。
前提条件
- 多言語URLは ルート直下プレフィックスで到達可能:
/en/...(英語)、/tc/...(繁体字)
※異なる規則の場合は後述ヘルパー関数内の生成ロジックを調整 - AIOSEO v4 系を利用
- CPT
newsがアーカイブを持つ(/news/が表示可能)
実装A:プラグイン版(mu-plugins/推奨)
メリット:テーマ変更の影響を受けず、確実に読み込まれます。
ファイルを作成:wp-content/mu-plugins/aioseo-news-localized-sitemaps.php
<?php
/**
* Plugin Name: AIOSEO - CPT news の多言語URL(/en, /tc)をサイトマップへ
* Description: 単一記事は各ポストタイプのサイトマップに翻訳URLとして直付け。アーカイブは addl-sitemap.xml に“追加ページ”として登録。
* Version: 1.0.0
*/
if (!defined('ABSPATH')) exit;
/** 先頭に /{locale} を付与してURLを生成(既に /en,/tc 配下なら何もしない) */
function ns_loc_inject(string $url, string $locale): string {
$p = wp_parse_url($url);
if (empty($p['scheme']) || empty($p['host'])) return $url;
$path = $p['path'] ?? '/';
// 既に同ロケール、または他ロケール配下なら何もしない
if (preg_match('#^/'.preg_quote($locale,'#').'(/|$)#', $path)) return $url;
if (preg_match('#^/(en|tc)(/|$)#', $path)) return $url;
$newPath = rtrim('/'.$locale.'/'.ltrim($path,'/'), '/').'/';
$built = $p['scheme'].'://'.$p['host'].(!empty($p['port'])?':'.$p['port']:'').$newPath;
if (!empty($p['query'])) $built .= '?'.$p['query'];
if (!empty($p['fragment'])) $built .= '#'.$p['fragment'];
return $built;
}
/**
* 1) 単一投稿(news):そのポストタイプのサイトマップへ“翻訳URL”として直接埋め込む
* hreflang キーはサイト方針に合わせて調整可(例:en, zh-Hant / zh-TW など)
*/
add_filter('aioseo_sitemap_post', function ($entry, $postId, $postType) {
if ($postType !== 'news') return $entry;
$origin = get_permalink($postId);
if (!$origin) return $entry;
$path = wp_parse_url($origin, PHP_URL_PATH) ?: '/';
if (preg_match('#^/(en|tc)(/|$)#', $path)) return $entry; // 多言語配下は起点にしない
$entry['translations'] = array_filter([
'en' => ns_loc_inject($origin, 'en'), // 例: https://example.com/en/news/1286/
'zh-Hant' => ns_loc_inject($origin, 'tc'), // 例: https://example.com/tc/news/1286/
]);
return $entry;
}, 10, 3);
/**
* 2) アーカイブ(/news/, /news/page/N/):“追加ページ”として addl-sitemap.xml に登録
*/
add_filter('aioseo_sitemap_additional_pages', function (array $additional): array {
$locales = ['en','tc'];
// 既存の追加URLを控えて重複回避
$existing = [];
foreach ($additional as $it) {
if (!empty($it['loc'])) $existing[$it['loc']] = true;
}
$archive_base = get_post_type_archive_link('news');
if (!$archive_base) return $additional;
// 総ページ数(公開済み件数 / posts_per_page)
$counts = wp_count_posts('news');
$publish = $counts && isset($counts->publish) ? (int) $counts->publish : 0;
$ppp = max(1, (int) get_option('posts_per_page', 10));
$total_pages = max(1, (int) ceil($publish / $ppp));
// 代表 lastmod(最新更新の news)
$q_last = new WP_Query([
'post_type' => ['news'],
'post_status' => 'publish',
'posts_per_page' => 1,
'orderby' => 'modified',
'order' => 'DESC',
'no_found_rows' => true,
]);
$archive_lastmod = gmdate('c');
if ($q_last->have_posts()) {
$q_last->the_post();
$archive_lastmod = get_post_modified_time('c', true, get_the_ID());
}
wp_reset_postdata();
// 1ページ目 + ページング
$urls = [trailingslashit($archive_base)];
if ($total_pages > 1) {
for ($i = 2; $i <= $total_pages; $i++) {
$urls[] = trailingslashit($archive_base).'page/'.$i.'/';
}
}
foreach ($urls as $url) {
$path = wp_parse_url($url, PHP_URL_PATH) ?: '/';
if (preg_match('#^/(en|tc)(/|$)#', $path)) continue; // 多言語側が起点になるケースは想定外
foreach ($locales as $lc) {
$loc = ns_loc_inject($url, $lc);
if (isset($existing[$loc])) continue;
$additional[] = [
'loc' => $loc,
'lastmod' => $archive_lastmod,
'changefreq' => 'weekly',
'priority' => 0.5,
];
$existing[$loc] = true;
}
}
return $additional;
});
設置後の操作
- 管理画面 → All in One SEO → Sitemaps → General Sitemap → Additional Pages を ON
- ページ/オブジェクト/サーバ/CDN の各キャッシュを削除
https://example.com/sitemap.xml(インデックス)から該当XMLを辿って反映確認- 単一記事:
newsのサイトマップ内で 翻訳URL が付与されていること - アーカイブ:
addl-sitemap.xml内に/en/tcのアーカイブURLがあること
実装B:functions.php に追記(手早いがテーマ依存)
メリット:すぐ試せる。
デメリット:テーマ更新/切替で消えるリスク。安定運用になったらプラグイン版へ移行推奨。
<?php
// 有効テーマの functions.php に追記
if (!defined('ABSPATH')) exit;
function ns_loc_inject(string $url, string $locale): string {
$p = wp_parse_url($url);
if (empty($p['scheme']) || empty($p['host'])) return $url;
$path = $p['path'] ?? '/';
if (preg_match('#^/'.preg_quote($locale,'#').'(/|$)#', $path)) return $url;
if (preg_match('#^/(en|tc)(/|$)#', $path)) return $url;
$newPath = rtrim('/'.$locale.'/'.ltrim($path,'/'), '/').'/';
$built = $p['scheme'].'://'.$p['host'].(!empty($p['port'])?':'.$p['port']:'').$newPath;
if (!empty($p['query'])) $built .= '?'.$p['query'];
if (!empty($p['fragment'])) $built .= '#'.$p['fragment'];
return $built;
}
// 単一投稿(news): 翻訳URLを直付け
add_filter('aioseo_sitemap_post', function ($entry, $postId, $postType) {
if ($postType !== 'news') return $entry;
$origin = get_permalink($postId);
if (!$origin) return $entry;
$path = wp_parse_url($origin, PHP_URL_PATH) ?: '/';
if (preg_match('#^/(en|tc)(/|$)#', $path)) return $entry;
$entry['translations'] = array_filter([
'en' => ns_loc_inject($origin, 'en'),
'zh-Hant' => ns_loc_inject($origin, 'tc'),
]);
return $entry;
}, 10, 3);
// アーカイブ: 追加ページで addl-sitemap.xml に
add_filter('aioseo_sitemap_additional_pages', function (array $additional): array {
$locales = ['en','tc'];
$existing = [];
foreach ($additional as $it) { if (!empty($it['loc'])) $existing[$it['loc']] = true; }
$archive_base = get_post_type_archive_link('news');
if (!$archive_base) return $additional;
$counts = wp_count_posts('news');
$publish = $counts && isset($counts->publish) ? (int) $counts->publish : 0;
$ppp = max(1, (int) get_option('posts_per_page', 10));
$total_pages = max(1, (int) ceil($publish / $ppp));
$q_last = new WP_Query([
'post_type' => ['news'],
'post_status' => 'publish',
'posts_per_page' => 1,
'orderby' => 'modified',
'order' => 'DESC',
'no_found_rows' => true,
]);
$archive_lastmod = gmdate('c');
if ($q_last->have_posts()) { $q_last->the_post(); $archive_lastmod = get_post_modified_time('c', true, get_the_ID()); }
wp_reset_postdata();
$urls = [trailingslashit($archive_base)];
if ($total_pages > 1) {
for ($i = 2; $i <= $total_pages; $i++) {
$urls[] = trailingslashit($archive_base).'page/'.$i.'/';
}
}
foreach ($urls as $url) {
$path = wp_parse_url($url, PHP_URL_PATH) ?: '/';
if (preg_match('#^/(en|tc)(/|$)#', $path)) continue;
foreach ($locales as $lc) {
$loc = ns_loc_inject($url, $lc);
if (isset($existing[$loc])) continue;
$additional[] = [
'loc' => $loc,
'lastmod' => $archive_lastmod,
'changefreq' => 'weekly',
'priority' => 0.5,
];
$existing[$loc] = true;
}
}
return $additional;
});
設定・確認手順
- Additional Pages を有効化
管理画面 → All in One SEO → Sitemaps → General Sitemap → Additional Pages を ON - キャッシュ削除
プラグイン/オブジェクト/サーバ/CDN のキャッシュを削除。可能なら*.xmlサイトマップはキャッシュ除外に。 - 反映確認
https://example.com/sitemap.xml(インデックス)→
newsのサイトマップ(CPT用)で 各記事に翻訳URL(/en, /tc) が付いているかaddl-sitemap.xmlに アーカイブの /en, /tc が含まれているか
注意点・運用メモ
- URL規則の差異
英語・繁体字のURLが/en//tc/以外のルールなら、ns_loc_inject()の生成ロジックを調整してください。 - hreflang の言語コード
ここでは例として'en'と'zh-Hant'を使用。サイト方針に合わせて'zh-TW'などへ変更可能。 - 重複回避
既に/en/tc配下のURLは“起点”にしません。フィルタ内で正規表現チェックしてスキップしています。 - 大規模サイトのパフォーマンス
追加ページ側は比較的軽量ですが、必要に応じてlastmod取得(最新1件クエリ)を Transients で短期キャッシュすると安全です。 - テーマ依存の回避
安定運用になったら mu-plugins(プラグイン版) へ移行を推奨。wp-content/mu-plugins/に置くだけで常時読み込みされます。 - アーカイブのページング規則
標準は/news/page/N/。独自構成を使っている場合は該当箇所を調整してください。
トラブルシュート
- 反映されない
まずフック実行をログで確認:
add_filter('aioseo_sitemap_post', function($e){ error_log('[AIOSEO] post hook'); return $e; }, 10, 3);
add_filter('aioseo_sitemap_additional_pages', function($a){ error_log('[AIOSEO] addl hook'); return $a; });
ログが出ない → コードが読まれていない/別環境
ログは出るのにXMLが変わらない → キャッシュ/見ているXMLが違う(インデックスから辿って確認)
addl-sitemap.xmlが増えた
追加ページをONにすると AIOSEO がaddl-sitemap.xmlを生成します。仕様どおりで問題ありません。
まとめ
- 単一記事(CPT
news):aioseo_sitemap_postで 翻訳URL(/en・/tc)を直接埋め込み - アーカイブ:
aioseo_sitemap_additional_pagesでaddl-sitemap.xmlに登録 - プラグイン版(mu-plugins) が安定運用に最適、試験導入なら functions.php でも可
- 実装後は Additional Pages をON、キャッシュ削除、インデックスから辿って確認 がポイント


コメント