多言語サイトの設計 ― テキストを 3 種類に分けて持つ

シリーズ・第 3 回 — 「個人で出版ツールを作って配るまで」(全 5 回予定)。前回は出力を契約する話でした。一覧は シリーズの記事一覧 から。今回のテーマは「多言語対応をどう設計するか」です。

「サイトを多言語にする」と聞くと、まず本文の翻訳を思い浮かべます。でも実際にやってみると、翻訳が要るテキストは本文だけではありませんでした。ナビのラベル、プロフィール、設定値。それぞれ性質が違い、同じ入れ物に押し込むと無理が出ます。

うまくいったのは、テキストを種類ごとに分けて、それぞれに合った持ち方をする設計でした。今回はその話です。例として Hugo(crofty が内部で使うもの)を挙げます。

テキストは 3 種類に分かれる

多言語サイトのテキストを整理すると、だいたい次の 3 種類になります。住む場所も、翻訳の仕組みも別です。

種類 持ち方
本文 記事 言語ごとのファイル
固定文言 ナビ、ボタン、「Support」 言語 → 文字列の翻訳テーブル
設定・データ サイト設定、プロフィール フィールド単位で「共通」か「言語ごと」

ひとつずつ見ます。

本文 ― 言語ごとのファイル

記事は、ひとつのフォルダ(ページバンドル)に言語別のファイルを並べます。

content/posts/my-article/
  index.md       … 日本語(既定言語)
  index.en.md    … 英語
  photo.avif     … 同梱する画像

ビルドすると /posts/my-article//en/posts/my-article/ の両方ができ、言語の切り替えはページ同士のリンクで済みます。JavaScript は要りません。

ひとつだけ小さな罠があります。画像(ページではないファイル)は既定言語のパスにしか出力されないので、英語版から相対参照すると 404 になります。英語版だけ、絶対パスで既定言語側を指せば直ります。

![](photo.avif)                     <!-- 日本語:相対でよい -->
![](/posts/my-article/photo.avif)   <!-- 英語:絶対パスで指す -->

固定文言 ― 翻訳テーブル

ナビゲーションやボタンのラベルのような、記事に依らない固定の文言は、本文に混ぜません。言語から文字列を引く「翻訳テーブル」でまとめて持ちます。

crofty では、こうした固定文言を言語ごとに用意してあり、言語名(「日本語」「English」)や支援リンクの「サポートする」「Support」を出し分けます。記事を 1 本も書いていなくても、サイトの枠は両言語で揃う、ということです。

設定・データ ― フィールド単位で選ぶ

設定ファイルや、プロフィールのような構造化データも、多言語の対象です。ただし値によって、全言語で同じものと、言語ごとに変えたいものが混在します。そこで、フィールド単位で「ただの文字列」か「言語ごとの値」かを選べるようにします。

設定ファイル(hugo.yaml)なら、言語ごとの設定ブロックに分けて持ちます。サイトのタイトルのように、言語で変わる値はここに置きます。

# hugo.yaml — 設定も言語ごとに値を持てる
languages:
  ja:
    title: "わたしのブログ"
  en:
    title: "My Blog"

データファイル(data/profile.yml)なら、フィールドごとに文字列かマップかを使い分けます。

support:
  # 文言は言語ごと → マップで持つ
  message:
    ja: "記事が役に立ったら、活動を応援していただけると励みになります。"
    en: "If these posts helped, a little support keeps the work going."
  # URL は全言語で同じ → ただの文字列
  stripe: "https://donate.stripe.com/..."

message は言語ごとなのでマップ、stripe は共通なので文字列。どちらの場合も「言語で変わる値だけ分ける」という同じ発想です。

配信は、全部ビルドして静的のまま

この 3 種類を分けておくと、配信はシンプルです。ビルド時に全言語のページを生成し、あとは静的ファイルを配るだけ。アクセスごとの判定も、クライアントの JavaScript も要りません。

唯一の別問題は「ブラウザの言語で自動的に振り分けたい」とき。これはビルド時には決められないので、やるならエッジ(配信時)かクライアント(JavaScript)に一手間を足すことになります。JavaScript を増やしたくないなら、自動振り分けは持たず、言語スイッチャで読者に選んでもらうのが素直です。crofty も当面はこの方針です。

まとめ

多言語化は、本文を翻訳することだけではありませんでした。テキストを 本文・固定文言・設定/データ の 3 種類に分け、それぞれに合う入れ物を選ぶ。そこを最初に設計しておけば、あとはビルドが全言語を出してくれます。

そして、どの層も静的なまま完結するので、表示の速さと no-JS はそのまま保てます。


← 前の記事:出力を契約する | 次の記事:自作 CLI を brew と scoop で配る