i18n - チュートリアル
このチュートリアルでは、Docusaurus の i18n システムの基本について説明します。
新しく初期化された英語の Docusaurus ウェブサイトにフランス語の翻訳を追加します。
npx create-docusaurus@latest website classic
を使用して新しいサイトを初期化します(こちらを参照)。
サイトの設定
フランス語の i18n サポートを追加するために、docusaurus.config.js
を変更します。
サイト設定
サイトの i18n 設定を使用して、i18n ローケールを宣言します。
export default {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'fa'],
localeConfigs: {
en: {
htmlLang: 'en-GB',
},
// You can omit a locale (e.g. fr) if you don't need to override the defaults
fa: {
direction: 'rtl',
},
},
},
};
ロケール名は、翻訳ファイルの場所と、翻訳されたロケールのベース URL の両方に使用されます。すべてのロケールをビルドする場合、デフォルトのロケールのみがベース URL で名前が省略されます。
Docusaurus は、ロケール名を使用して適切なデフォルト値(<html lang="...">
属性、ロケールラベル、カレンダー形式など)を提供します。これらのデフォルト値は、localeConfigs
でカスタマイズできます。
テーマ設定
ユーザーが目的のロケールを選択できるように、localeDropdown
タイプのナビゲーションバー項目を追加します。
export default {
themeConfig: {
navbar: {
items: [
{
type: 'localeDropdown',
position: 'left',
},
],
},
},
};
ユーザーがドロップダウンを使用してロケールを変更したときに URL に追加されるクエリパラメーターを渡すことができます(例:queryString: '?persistLocale=true'
)。
これは、サーバーで自動ロケール検出を実装するのに役立ちます。たとえば、このパラメーターを使用して、ユーザーの優先ロケールを Cookie に保存できます。
サイトの起動
選択したロケールを使用して、ローカライズされたサイトを開発モードで起動します。
- npm
- Yarn
- pnpm
npm run start -- --locale fr
yarn run start --locale fr
pnpm run start --locale fr
サイトは http://localhost:3000/fr/
でアクセスできます。
まだ翻訳を提供していないため、サイトはほとんど翻訳されていません。
Docusaurus は、ページャーの「次へ」や「前へ」など、一般的なテーマラベルのデフォルトの翻訳を提供します。
これらのデフォルトの翻訳の完成にご協力ください。
各ロケールは、個別のスタンドアロンのシングルページアプリケーションです。すべてのロケールで Docusaurus サイトを同時に起動することはできません。
サイトの翻訳
フランス語ロケールのすべての翻訳データは、website/i18n/fr
に保存されています。各プラグインは、対応するフォルダーの下で独自の翻訳済みコンテンツをソースとして使用し、code.json
ファイルは React コードで使用されるすべてのテキストラベルを定義します。
ファイルをコピーした後は、npm run start -- --locale fr
でサイトを再起動します。既存のファイルを編集する場合は、ホットリロードがより効果的に機能します。
React コードの翻訳
自分で記述した React コード(React ページ、React コンポーネントなど)については、翻訳 API を使用します。
ユーザーに表示される React コード内のすべてのテキストラベルを見つけ、翻訳 API を使用してマークします。API には 2 つの種類があります。
<Translate>
コンポーネントは、文字列を JSX 要素としてラップします。translate()
コールバックはメッセージを受け取り、文字列を返します。
セマンティックにコンテキストに最適な方を使用します。たとえば、<Translate>
は React の子として使用できますが、文字列を期待するプロパティの場合は、コールバックを使用できます。
- 変更前
- 変更後
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
export default function Home() {
return (
<Layout>
<h1>Welcome to my website</h1>
<main>
You can also visit my
<Link to="https://docusaurus.dokyumento.jp/blog">blog</Link>
<img
src="/img/home.png"
alt="Home icon"
/>
</main>
</Layout>
);
}
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import Translate, {translate} from '@docusaurus/Translate';
export default function Home() {
return (
<Layout>
<h1>
<Translate>Welcome to my website</Translate>
</h1>
<main>
<Translate
id="homepage.visitMyBlog"
description="The homepage message to ask the user to visit my blog"
values={{
blogLink: (
<Link to="https://docusaurus.dokyumento.jp/blog">
<Translate
id="homepage.visitMyBlog.linkLabel"
description="The label for the link to my blog">
blog
</Translate>
</Link>
),
}}>
{'You can also visit my {blogLink}'}
</Translate>
<img
src="/img/home.png"
alt={
translate({
message: 'Home icon',
description: 'The homepage icon alt message',
})
}
/>
</main>
</Layout>
);
}
Docusaurus は、意図的に非常に小さく軽量な翻訳ランタイムを提供しており、プレースホルダー補間の基本的なもののみをサポートしており、ICU メッセージ形式のサブセットを使用しています。
ほとんどのドキュメントウェブサイトは一般的に静的であり、高度な i18n 機能(複数形、性など)は必要ありません。より高度なユースケースには、react-intl などのライブラリを使用してください。
docusaurus write-translations
コマンドは、サイトで使用されているすべての React コードファイルを静的に分析し、これらの API への呼び出しを抽出し、code.json
ファイルに集約します。翻訳ファイルは、ID から翻訳済みメッセージオブジェクト(翻訳済みラベルとラベルの説明を含む)へのマップとして保存されます。翻訳 API(<Translate>
または translate()
)への呼び出しでは、Docusaurus が各翻訳エントリを API 呼び出しに正しく関連付けるために、デフォルトの翻訳されていないメッセージまたは ID を指定する必要があります。
docusaurus write-translations
コマンドは、コードの静的分析のみを実行します。サイトを実際に実行しません。したがって、メッセージが式ではなく文字列であるため、動的なメッセージは抽出できません。
const items = [
{id: 1, title: 'Hello'},
{id: 2, title: 'World'},
];
function ItemsList() {
return (
<ul>
{/* DON'T DO THIS: doesn't work with the write-translations command */}
{items.map((item) => (
<li key={item.id}>
<Translate>{item.title}</Translate>
</li>
))}
<ul>
);
}
これは実行時には正しく動作しますが、将来は、ランタイムで API を呼び出す代わりに、Babel 変換を通じて翻訳を React コードに直接インライン化できる「ランタイムなし」メカニズムを提供する場合があります。したがって、将来に対応するために、常に静的に分析可能なメッセージを優先する必要があります。たとえば、上記のコードを次のようにリファクタリングできます。
const items = [
{id: 1, title: <Translate>Hello</Translate>},
{id: 2, title: <Translate>World</Translate>},
];
function ItemsList() {
return (
<ul>
{/* The titles are now already translated when rendering! */}
{items.map((item) => (
<li key={item.id}>{item.title}</li>
))}
<ul>
);
}
翻訳 API への呼び出しは、単なるマーカーであり、「翻訳済みメッセージで置き換えられるテキストラベルがあります」ということを Docusaurus に伝えるものです。
複数形
write-translations
を実行すると、一部のラベルが複数形になっていることに気付くでしょう。
{
// ...
"theme.blog.post.plurals": "One post|{count} posts"
// ...
}
すべての言語には、可能な複数形カテゴリのリストがあります。Docusaurus は、それらを["zero", "one", "two", "few", "many", "other"]
の順序で配置します。たとえば、英語(en
)には 2 つの複数形(「one」と「other」)があるため、翻訳メッセージにはパイプ(|
)で区切られた 2 つのラベルがあります。3 つの複数形(「one」、「few」、「many」)を持つポーランド語(pl
)の場合、その順序で 3 つのラベルをパイプで区切って提供します。
独自のコードのメッセージも複数形にすることができます。
import {translate} from '@docusaurus/Translate';
import {usePluralForm} from '@docusaurus/theme-common';
function ItemsList({items}) {
// `usePluralForm` will provide the plural selector for the current locale
const {selectMessage} = usePluralForm();
// Select the appropriate pluralized label based on `items.length`
const message = selectMessage(
items.length,
translate(
{message: 'One item|{count} items'},
{count: items.length},
),
);
return (
<>
<h2>{message}</h2>
<ul>{items.map((item) => <li key={item.id}>{item.title}</li>)}<ul>
</>
);
}
Docusaurus は、Intl.PluralRules
を使用して複数形を解決および選択します。selectMessage
が機能するには、正しい順序で適切な数の複数形を提供することが重要です。
プラグインデータの翻訳
JSON 翻訳ファイルは、コード中に散在するすべてのものに使用されます。
- 上記でマークした翻訳済みのラベルを含むReactコード
- テーマ設定ファイル内のナビゲーションバーとフッターのラベル
sidebars.js
ファイル内のドキュメントサイドバーのカテゴリラベル- プラグインオプション内のブログサイドバーのタイトル
- ...
write-translationsコマンドを実行します
- npm
- Yarn
- pnpm
npm run write-translations -- --locale fr
yarn write-translations --locale fr
pnpm run write-translations --locale fr
このコマンドは、翻訳に必要なJSON翻訳ファイルを抽出し、初期化します。ルートディレクトリのcode.json
ファイルには、ソースコードから抽出されたすべての翻訳API呼び出しが含まれています。これは、ユーザーが記述するか、テーマによって提供されるもので、その一部はデフォルトで既に翻訳されている場合があります。
{
// No ID for the <Translate> component: the default message is used as ID
"Welcome to my website": {
"message": "Welcome to my website"
},
"home.visitMyBlog": {
"message": "You can also visit my {blog}",
"description": "The homepage message to ask the user to visit my blog"
},
"homepage.visitMyBlog.linkLabel": {
"message": "Blog",
"description": "The label for the link to my blog"
},
"Home icon": {
"message": "Home icon",
"description": "The homepage icon alt message"
}
}
プラグインとテーマも独自のJSON翻訳ファイルを作成します。例:
{
"title": {
"message": "My Site",
"description": "The title in the navbar"
},
"item.label.Docs": {
"message": "Docs",
"description": "Navbar item with label Docs"
},
"item.label.Blog": {
"message": "Blog",
"description": "Navbar item with label Blog"
},
"item.label.GitHub": {
"message": "GitHub",
"description": "Navbar item with label GitHub"
}
}
i18n/fr
内のJSONファイルのmessage
属性を翻訳すると、サイトのレイアウトとホームページが翻訳されます。
Markdownファイルの翻訳
公式のDocusaurusコンテンツプラグインは、Markdown/MDXファイルを広く使用しており、それらの翻訳が可能です。
ドキュメントの翻訳
ドキュメントのMarkdownファイルをdocs/
からi18n/fr/docusaurus-plugin-content-docs/current
にコピーして翻訳します
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current
cp -r docs/** i18n/fr/docusaurus-plugin-content-docs/current
docusaurus-plugin-content-docs
プラグインは、常にコンテンツをバージョン別に分割することに注意してください。./docs
フォルダのデータは、current
サブフォルダとcurrent.json
ファイルで翻訳されます。「current」の意味については、ドキュメントのバージョン管理ガイドを参照してください。
ブログの翻訳
ブログのMarkdownファイルをi18n/fr/docusaurus-plugin-content-blog
にコピーして翻訳します
mkdir -p i18n/fr/docusaurus-plugin-content-blog
cp -r blog/** i18n/fr/docusaurus-plugin-content-blog
ページの翻訳
ページのMarkdownファイルをi18n/fr/docusaurus-plugin-content-pages
にコピーして翻訳します
mkdir -p i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.md i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.mdx i18n/fr/docusaurus-plugin-content-pages
Reactページは既にJSON翻訳ファイルを通じて翻訳されているため、.md
と.mdx
ファイルのみをコピーします。
デフォルトでは、Markdownの見出し### Hello World
には、hello-world
というIDが生成されます。他のドキュメントは、[link](#hello-world)
でこれをリンクできます。しかし、翻訳後、見出しは### Bonjour le Monde
になり、IDはbonjour-le-monde
になります。
生成されたIDは、すべてのアンカーリンクをローカライズする必要があるため、ローカライズされたサイトには必ずしも適していません。
- [link](#hello-world).
+ [link](#bonjour-le-monde)
ローカライズされたサイトでは、明示的な見出しIDを使用することをお勧めします。
サイトのデプロイ
サイトを**単一ドメイン**でデプロイするか、**複数の(サブ)ドメイン**を使用するかを選択できます。
単一ドメインでのデプロイ
次のコマンドを実行します
- npm
- Yarn
- pnpm
npm run build
yarn build
pnpm run build
Docusaurusは、ロケールごとに**単一ページアプリケーションを1つビルド**します。
website/build
:デフォルトの英語website/build/fr
:フランス語
これで、お好みの静的ホスティングソリューションにbuild
フォルダをデプロイできます。
Docusaurusウェブサイトはこの戦略を使用しています。
静的ホスティングプロバイダーは、一般的に慣例により/unknown/url
を/404.html
にリダイレクトするため、常に**英語の404ページ**が表示されます。
ホストの設定で/fr/*
を/fr/404.html
にリダイレクトすることで、**404ページをローカライズ**します。
これは常に可能とは限らず、ホストによって異なります。GitHub Pagesではこれはできませんが、Netlifyでは可能です。
複数ドメインでのデプロイ
単一ロケール用のサイトをビルドすることもできます。
- npm
- Yarn
- pnpm
npm run build -- --locale fr
yarn build --locale fr
pnpm run build --locale fr
Docusaurusは/fr/
URLプレフィックスを追加しません。
静的ホスティングプロバイダーで静的ホスティングプロバイダー
- ロケールごとに1つのデプロイを作成します
--locale
オプションを使用して、適切なビルドコマンドを設定します- 各デプロイに、選択した(サブ)ドメインを設定します
この戦略は、**単一のデプロイメントしかできない**ため、GitHub Pagesでは**不可能**です。
ハイブリッド
サブパスを使用するロケールとサブドメインを使用するロケールを混在させることができます。
各ロケールを別々のサブドメインとしてデプロイし、CDNレベルで単一の統合ドメインにサブドメインを集約することも可能です。
- サイトを
fr.docusaurus.io
としてデプロイします。 - CDNを設定して
docusaurus.io/fr
から提供します。
翻訳の管理
Docusaurusは、翻訳の管理方法を気にしません。必要なのは、ビルド時にすべての翻訳ファイル(JSON、Markdown、その他のデータファイル)がファイルシステムで使用できることです。ただし、サイト作成者として、翻訳担当者が適切に協力できるように、翻訳の管理方法を検討する必要があります。
Gitの使用とCrowdinの使用という2つの一般的な翻訳コラボレーション戦略を紹介します。