多言語対応(i18n)#
Ovellum は同じサイトを複数の言語で公開できます。これはオプトインです。単一言語のサイトはロケールフォルダを必要とせず、これまでとまったく同じように動作します。言語を宣言してオンにすると、コンテンツは言語ごとに 1 つのサブツリーへ移動します。
有効にする#
config に site.locales(および必要に応じて site.defaultLocale)を追加します。
export default {
site: {
defaultLocale: 'en-US',
locales: [
{ code: 'en-US', label: 'English' },
{ code: 'ja', label: '日本語' },
{ code: 'zh-Hans', label: '简体中文' },
],
},
} satisfies OvellumUserConfig;
codeは BCP 47 の言語タグです —en-US、ja、zh-Hans/zh-Hant、de、pt-BR。これはコンテンツフォルダ名であり、<html lang>でもあります。labelはピッカーが表示するものです — その言語の自称(その言語自身の名前:日本語、简体中文、English)を使ってください。読者は自分の言語をその固有の文字体系で探すからです。defaultLocaleはサイトのルートで配信されます。デフォルトはlocalesの最初のエントリです。
正しいコードを使ってください。イギリス英語は
en-GB(en-ukではない)、日本語はja(jpではない)、中国語は国コードではなくスクリプトサブタグ —zh-Hans(簡体字)またはzh-Hant(繁体字)— を使います。
ロケールごとにコンテンツを整理する#
ロケールをオンにすると、コンテンツは言語ごとに 1 つのサブツリーへ、そのコードを名前として配置されます。
content/
public/ ← shared across all locales (copied to the root)
en-US/ ← the default locale
_landing.md
docs/
getting-started.md
guides/install.md
ja/ ← Japanese
docs/
getting-started.md
ページは同一の相対パスによって言語間で対応します。en-US/docs/guides/install.md は ja/docs/guides/install.md の翻訳です。このマッピングを言語ピッカーが追います。各ロケールは独自のサイドバー、_meta.json の順序、フロントマターを持ちます。
予約された publicDir(public/)は共有のままです — これはロケールではなく、出力ルートに一度だけコピーされます。
既存サイトの移行#
単一言語のサイトに i18n を追加するのは、一度きりの移動です。既存のコンテンツを content/<defaultLocale>/ に入れ、locales の config を追加します。コンテンツのルートにあったファイル(たとえば CNAME)は public/ に移し、引き続き出力ルートに届くようにします。ほかには何も変わりません。
URL#
デフォルトロケールはルートで配信され、それ以外のロケールはすべてそのコードの下で配信されます。
| ページ | en-US(デフォルト) | ja |
|---|---|---|
docs/guides/install.md | /docs/guides/install/ | /ja/docs/guides/install/ |
| ホーム / ランディング | / | /ja/ |
なので、デフォルトロケールがすでに使っていた言語であれば、既存の URL は変わりません。
言語ピッカー#
トップバー(ナビリンクの後、アイコンクラスターの前)にグローブのドロップダウンが現れ、すべてのロケールをそのラベルで一覧表示します。切り替えると、読者はその言語の同じページに移動します。ページがまだ翻訳されていない場合、ピッカーはそのロケールのホームにフォールバックします — だから、わずかなページだけを翻訳した状態で言語を出荷し、時間をかけて育てていくことができます。
各ページには <html lang> も付き、site.baseUrl が設定されているときは hreflang の alternate リンク(およびデフォルトロケール用の x-default)も付くので、検索エンジンが正しい言語を配信します。
何がローカライズされ、何がまだされていないか#
ローカライズされる: すべてのページコンテンツとフロントマター、サイドバー/ナビ、ページ URL、<html lang>、hreflang、サイトマップ、そしてテンプレート自身の UI クロム — 「このページの内容」、「最終更新」とその日付、「分で読めます」、外観パネルのラベル、前後リンク、404 ページなど。クロムは組み込みの言語セット(現在は英語と日本語)向けに翻訳済みで出荷されます。それ以外のロケールは文字列ごとに英語へフォールバックし、不足分は自分で埋められます(下記参照)。右から左に書く言語には <html dir="rtl"> も自動的に付きます。
config 由来のテキスト — ovellum.config.* に書くランディングのヒーロー/CTA/機能のコピーや、topbarNav / footerNav のリンクラベル — もローカライズできます。これらのフィールドは、プレーンな文字列か、ロケールごとのマップ(下記)のどちらかを受け取ります。
まだされていない: ロケールごとの RSS フィード。
config テキストをローカライズする#
config がユーザー向けのラベルやコピー文字列を受け取る箇所では、プレーンな文字列の代わりにロケールごとのマップを渡せます — ロケール code をキーにし、デフォルトロケールにフォールバックします:
topbarNav: [{ label: { 'en-US': 'Docs', ja: 'ドキュメント' }, href: '/docs/' }],
landing: {
hero: {
title: { 'en-US': "Docs that don't drift.", ja: 'ドリフトしないドキュメント。' },
ctas: [{ label: { 'en-US': 'Get started', ja: 'はじめる' }, href: '/docs/' }],
},
},
プレーンな文字列も引き続き使え、すべてのロケールで表示されます — なので、実際に翻訳する文字列だけをマップ化すればよいのです。対象は topbarNav/footerNav のラベルと、ランディングのヒーロータイトル/サブタイトル、CTA ラベル、機能のタイトル/説明、install のタイトル、トラストストリップのテキストです。
クロム文字列を上書き・追加する#
組み込みにないロケールや、別の言い回しにしたい場合は、ロケールに strings を設定します — 組み込みテーブルの上にマージされます(省略した分は英語が埋めます):
site: {
locales: [
{ code: 'en-US', label: 'English' },
{
code: 'fr',
label: 'Français',
strings: { tocTitle: 'Sur cette page', editedLabel: 'Modifié', backToTop: 'Haut de page' },
},
],
}
キーは UI 文字列の名前(tocTitle、editedLabel、minRead、previous、next、backToTop、外観パネルのラベルなど)です。
翻訳はあなたが書くか生成するもの#
Ovellum は各ロケールフォルダにある Markdown をそのままレンダリングします。手作業で翻訳を書いても、好きな方法で事前翻訳してファイルを置いても構いません — ツールはあなたの代わりに翻訳することはなく、邪魔もしません。
翻訳を同期させ続ける#
手作業で維持する翻訳の難しさは、書くことではありません — **元(ソース)**のページが変わったのに翻訳が気づかぬうちに遅れていく、それに気づくことです。ovellum check はそのドリフトを見張ります。
各翻訳ページは、フロントマターに sourceHash を持てます — それがミラーするデフォルトロケールのページの指紋です(ロケールフォルダをまたいで同一のパスで対応づけられます。例: ja/docs/install.md ↔ en-US/docs/install.md)。check はソースの指紋を再計算して比較します:
- ソースが変わっていない → 何も報告しません。
- スタンプ後にソースが変わった →
[i18n]として stale(古い)と報告します(終了コード1。CI が検知できます)。 - まだ
sourceHashがない → 報告します。新しい翻訳がスタンプされるようにするためです。 - 対応するソースページがない → orphan(みなしご)翻訳として報告します。
ハッシュを手で書く必要はありません。翻訳をソースに合わせ直したら、スタンプします:
ovellum check --update-translations
これは各翻訳ページに現在の sourceHash を書き込み(変更するのはそのフロントマター 1 行だけ)、終了します。指紋はフロントマターではなくページの本文を対象にし、改行コードを正規化します — そのため整形やフロントマターの微修正で誤って「stale」になることはありません。典型的な流れ: 英語ページを編集 → ovellum check が日本語のミラーを指摘 → 変更を翻訳 → ovellum check --update-translations で再スタンプ。