言語タブ — inLanguage と多言語連携

md サブドメインで配信される Markdown の inLanguage プロパティの値を管理するタブです。WordPress locale (例 ja_JP) を BCP 47 形式 (例 ja) に自動変換するか、手動で上書き値を指定するかを選びます。WPML / Polylang の検出状況も表示。

概要

「言語」タブは WordPress 管理画面 → LLMO Markdown → 言語 でアクセスできるタブです (URL: /wp-admin/admin.php?page=kashiwazaki-llmo-md&tab=i18n)。Markdown インライン Schema.org の inLanguage プロパティをこのタブで決めます。

このタブは以下の 2 つのセクションで構成されます:

  1. Schema.org inLanguage の値 — 自動検出値の表示 + 手動上書きフィールド
  2. 多言語プラグイン連携 — WPML / Polylang の検出結果表示 (現状は情報のみ)

画面構成

言語タブの全景
図: 言語タブの全景。現在の WordPress locale と BCP 47 形式への変換結果が表示され、その下に上書き値の入力欄。
言語タブの概念図
図: WP locale → BCP 47 自動変換と、上書き値の優先関係を示す概念図。

UI 部品の対応表

セクション UI 部品 option key 役割
現在の WordPress locale 表示のみ (read-only) get_locale() の結果と BCP 47 化結果
上書き値 (任意) text input default_in_language (string) 空欄なら自動検出。値があれば優先。
WPML / Polylang 検出 表示のみ 有効化されているかの情報表示

WordPress locale と BCP 47 の違い

WordPress 内部の get_locale() は POSIX ロケール風の アンダースコア区切り 形式 (例: ja_JP) を返します。一方 Schema.org の inLanguage プロパティ、HTML lang 属性、HTTP Content-Language ヘッダはすべてBCP 47 (RFC 5646) に従い、ハイフン区切り形式 (例: ja, ja-JP, en-US) を使います。

変換ロジック

本プラグインは単純な置換で変換します:

// admin/sections/i18n.php (抜粋)
$current_locale = function_exists( 'get_locale' ) ? get_locale() : 'en';
$bcp47          = str_replace( '_', '-', $current_locale );

つまりアンダースコアをハイフンに置き換えるだけです。多くのケースでこれで十分ですが、例外もあります。例: nl_NL_formalfr_FR_formal のような変種ロケール、zh_CN (これは BCP 47 では zh-Hans-CN が望ましい場合あり) などは手動で上書きが必要です。

典型的な変換例

WordPress locale (get_locale()) 自動変換後 BCP 47 標準的な BCP 47 調整が必要か
ja ja ja 不要
ja_JP ja-JP ja-JP または ja 多くの場合不要
en_US en-US en-US 不要
en_GB en-GB en-GB 不要
zh_CN zh-CN zh-Hans-CN 推奨 必要 (上書き値に zh-Hans-CN)
zh_TW zh-TW zh-Hant-TW 推奨 必要 (上書き値に zh-Hant-TW)
nl_NL_formal nl-NL-formal nl-NL 必要 (上書き値に nl-NL)
de_DE_formal de-DE-formal de-DE 必要
pt_BR pt-BR pt-BR 不要

上書き値 (任意) フィールド

このフィールドに値が入っていればその値が常に使われ、空欄なら自動検出 (BCP 47 化された WP locale) が使われます。

使うべき場面

入力時の注意

多言語プラグイン連携

このセクションは WPMLPolylang の検出結果を表示します。

検出ロジック

// admin/sections/i18n.php (抜粋)
$wpml_active     = function_exists( 'icl_object_id' ) || class_exists( 'SitePress' );
$polylang_active = function_exists( 'pll_get_post_translations' );

現バージョンの対応状況

📌 Note

WPML / Polylang の自動連携 (hreflang・翻訳済 URL の canonical 連動) は次バージョンで対応予定です。現バージョン (v1.0.1) では検出のみ表示し、各サイトが filter ksmd_route_schema_header_lines で個別に拡張する必要があります。

WPML / Polylang での独自連携 (filter)

WPML / Polylang から翻訳ペアを取得し、Markdown インライン Schema.org の hreflang 情報として注入する例:

// WPML
add_filter( 'ksmd_route_schema_header_lines', function( $lines, $type, $headline, $url, $context ) {
    if ( ! function_exists( 'icl_get_languages' ) ) {
        return $lines;
    }
    if ( empty( $context['post'] ) ) {
        return $lines;
    }
    $post_id = (int) $context['post']->ID;
    $langs = apply_filters( 'wpml_active_languages', null );
    foreach ( $langs as $code => $info ) {
        $translated_id = apply_filters( 'wpml_object_id', $post_id, 'post', false, $code );
        if ( $translated_id ) {
            $tr_url = get_permalink( $translated_id );
            $lines[] = '> **inLanguage** (' . esc_html( $code ) . '): ' . esc_html( $tr_url );
        }
    }
    return $lines;
}, 10, 5 );
// Polylang
add_filter( 'ksmd_route_schema_header_lines', function( $lines, $type, $headline, $url, $context ) {
    if ( ! function_exists( 'pll_get_post_translations' ) ) {
        return $lines;
    }
    if ( empty( $context['post'] ) ) {
        return $lines;
    }
    $translations = pll_get_post_translations( $context['post']->ID );
    foreach ( $translations as $lang => $tr_id ) {
        $tr_url = get_permalink( $tr_id );
        $lines[] = '> **inLanguage** (' . esc_html( $lang ) . '): ' . esc_html( $tr_url );
    }
    return $lines;
}, 10, 5 );

内部挙動 — inLanguage が読まれる場所

singular renderer での読み込み

// 概念フロー
$opts = get_option( 'ksmd_settings', array() );
$override = isset( $opts['default_in_language'] )
    ? trim( (string) $opts['default_in_language'] )
    : '';
$lang = $override !== ''
    ? $override
    : str_replace( '_', '-', get_locale() );
// → Markdown インライン Schema.org header の inLanguage プロパティに使用

archive route での読み込み

すべての route renderer が同じ inLanguage 解決ロジックを使います。 route ごとに上書きしたい場合は ksmd_route_schema_header_lines filter で blockquote 行を差し替えます:

add_filter( 'ksmd_route_schema_header_lines', function( $lines, $type, $headline, $url, $context ) {
    // search route だけ inLanguage を und (undefined) にする
    if ( isset( $context['route_type'] ) && $context['route_type'] === 'search' ) {
        // 既存の inLanguage 行を差し替え
        $lines = array_map( function( $line ) {
            if ( strpos( $line, '**inLanguage**' ) !== false ) {
                return '> **inLanguage**: und';
            }
            return $line;
        }, $lines );
    }
    return $lines;
}, 10, 5 );

関連 filter

ksmd_route_schema_header_lines

言語タブが直接持つ filter ではないが、inLanguage を route 別に上書きする際に使う最も重要な拡張ポイントです。

/**
 * @param array  $lines    blockquote 行の配列
 * @param string $type     Schema.org @type
 * @param string $headline 見出し
 * @param string $url      canonical URL
 * @param array  $context  route_type / term / author / post 等
 */
add_filter( 'ksmd_route_schema_header_lines', $callback, 10, 5 );

typical configurations

パターン A — 単一言語サイト (日本語のみ)

パターン B — 中国語サイト (簡体)

パターン C — WPML 多言語サイト

パターン D — 英国英語強制 (WP は en_US)

よくある質問

Q. jaja-JP どちらが正しい?

A. 両方有効ですが、Schema.org / Google 向けには地域コード付き (ja-JP) が望ましいとされています。日本語コンテンツは事実上日本国内向けが多いため、get_locale()ja_JP を返してくれれば自動変換で十分です。一部の WP インストールで ja しか返さない場合は手動で ja-JP に上書き推奨。

Q. get_locale()get_user_locale() はどちらが使われる?

A. 本プラグインは get_locale() (サイト全体の locale) を使います。ユーザー個別 locale ではありません。フロントエンドのコンテンツは admin user の言語設定とは独立だからです。

Q. WPML の hreflang を出力したい

A. v1.0.0 では自動連携未対応。上記の filter サンプル参照。次バージョンで自動連携予定。

Q. 単一サイトに混在言語がある場合は?

A. 個別記事の言語が異なる場合 (例: 投稿の半分が英語、半分が日本語)、ksmd_schema_type や独自 filter で inLanguage を post meta から動的に決定する実装が必要です。本タブのサイト全体設定だけでは対応できません。

Q. inLanguage が間違っていると AI への影響は?

A. LLM は本文から言語を自動検出できるため、致命的ではありません。しかしSchema.org のメタ情報の整合性が崩れると、検索エンジンや AI の信頼度が下がる可能性があるため、正確な値を入れることを推奨します。

Q. 上書き値に x-custom-tag のような Private Use タグを入れられる?

A. BCP 47 では x- プレフィックスで Private Use タグが許可されています。サニタイズは入りますが拒否はされません。ただし AI/検索エンジンは無視する可能性があるため、標準的な値を推奨。

関連