RRelayer
ホーム/機能

多言語化 (i18n)#

依存ゼロのトランスレータ、ファイルベースのカタログ、自動ロケール解決、そしてフレームワーク標準メッセージのローカライズを提供します。設定でオプトインで、i18n 関連の環境変数を一切設定しなければ単一ロケール(英語)のままコスト無しで動き、フレームワークの全文字列は i18n 未使用時とバイト単位で同一です。

設定#

環境変数で制御します(すべて任意):

APP_LOCALE=en            # デフォルト / アクティブロケール(既定: en)
APP_LOCALES=en,ja        # サポートロケール(既定: APP_LOCALE のみ)
LOCALE_COOKIE=locale     # Cookie ソースの Cookie 名(既定: locale)
LOCALE_PATH_PREFIX=true  # /{locale}/... ルーティングの opt-out(既定: true)

TranslatorLocaleResolver は常にコンテナへ登録されます(autowire・ public、サービスと DI)。ただしロケールの*切り替え* (Cookie / Accept-Language / パスプレフィックス解決)が実際に作用するのは APP_LOCALES に 2 つ以上ロケールを並べたときだけです。未設定(または単一ロケール)の場合は全リクエストが APP_LOCALE に解決され、パスの書き換えは一切起きません/en/* のルートも i18n 未使用時とまったく同じに動きます。LOCALE_PATH_PREFIX=false は複数ロケールアプリで /{locale}/... ルーティングを*無効化*するだけ(Cookie / Accept-Language は有効のまま)で、単一ロケールアプリでプレフィックスルーティングを有効化することはできません(区別すべきものが無いため)。

APP_LOCALE はデフォルトの*アクティブ*ロケールであり、フレームワークのフォールバックではありません。組み込みの relayer.* メッセージは常に 英語(同梱の完全なカタログ)にフォールバックするため、フレームワーク文字列で未翻訳キーがそのまま露出することはありません。

ロケール解決の優先順位#

リクエストごとに LocaleResolver が以下の優先順(上が最優先)でロケールを決定します:

  1. URL パスプレフィックス — 先頭セグメントがサポートロケールのとき

/{locale}/...。ルーターがマッチするパスを書き換える唯一のソースで、 /ja/about/about は同じ src/Pages/about/page.psx に到達します(ルーティングとページ)。

  1. セッション既にセッションが開始済みのときだけ参照します。

ロケール判定のためだけにセッションを開始するとリクエスト毎に Set-Cookie が出て匿名ページの CDN キャッシュを壊すため、リゾルバはそれをしません。既にセッションを持つログイン済みフローでは保存済みの _locale が尊重されます。

  1. CookieLOCALE_COOKIE(CDN セーフ、セッション不要)。
  2. Accept-Language — サポートリストに対し q 値でネゴシエート。
  3. デフォルトAPP_LOCALE

照合はプライマリサブタグ単位(ja-JP はサポートされた ja に一致)で、解決値は APP_LOCALES の正規表記です。決定したロケールは <html lang="…"> にも反映され、$request->locale() で取得できます。

defer フラグメントとパスプレフィックスルーティング。 <X defer /> のサブリクエストは /{locale} を含まないルート絶対の /_defer/{name} URL で取得されます(usePHP がルートに固定するため)。よって親ページのパスプレフィックスは伝わらず、フラグメントのロケールは URL パスではなく Cookie / Accept-Language / デフォルトから解決されます。/{locale}/… プレフィックスだけでロケールを切り替えていて defer フラグメントも同じ言語にしたい場合は、 LOCALE_COOKIE も設定してください(または Accept-Language に委ねる)。 Cookie は CDN セーフで、このケースの想定キャリアです。

自分のコンテンツを翻訳する#

<projectRoot>/translations/{locale}.php に PHP カタログを置きます。フラットでもネストでも可で、フレームワークのカタログにマージ(上書き)されます:

// translations/ja.php
return [
    'home.title'   => 'ようこそ',
    'cart.items'   => '{count}点|{count}点', // パイプ = 複数形
    'user'         => ['greeting' => 'こんにちは、{name}さん'],
];

Translator は任意のページ・レイアウト・サービスに注入できます:

use Polidog\Relayer\I18n\Translator;

return static fn (Translator $t) => h('h1', [], $t->trans('home.title'));
// プレースホルダ:  $t->trans('user.greeting', ['name' => $name])
// 複数形:          $t->transChoice('cart.items', $count)
  • trans(string $key, array $params = [], ?string $locale = null)

{name} プレースホルダを $params で置換。

  • transChoice(string $key, int $count, array $params = [], ?string $locale = null)

one|other のパイプ区切りメッセージから簡易 CLDR ルール(英語型の one/other、日本語・中国語・韓国語などは単一形)で形を選択。

  • 未知のキーはキー自身(プレースホルダ置換後)にフォールバック —

見えるが致命的にはなりません。

フレームワーク標準メッセージのローカライズ#

バリデーションメッセージと HTTP エラーの理由句(HTML エラーページ、および API ルートの JSON {"error": …} ボディ)は、同じカタログの relayer.* 名前空間で解決され、 enja を同梱しています。バリデーションスキーマはコンテナ外で構築されるため、AppRouter がリクエスト毎に設定するプロセス全体のアンビエント保持器(Polidog\Relayer\I18n\Translators)経由で現在のトランスレータに到達します。refine() / required('…') に渡した独自メッセージは常にそのまま透過されます。CLI 出力(relayer …CLI コマンド)は英語のみです。

最終更新: 2026-05-19
変更履歴 (1)
  • v0.17.0 の多言語化(i18n)を新規ページとして追加