コンテンツにスキップ

国際化(i18n)

Starlightは、ルーティング、フォールバックコンテンツ、右横書き(right-to-left、RTL)言語へのフルサポートなど、複数言語サイトを構築するための機能を組み込みで提供します。

  1. localesdefaultLocaleをStarlightインテグレーションに渡すことで、サポートする言語についてStarlightに伝えます。

    astro.config.mjs
    import { defineConfig } from 'astro/config';
    import starlight from '@astrojs/starlight';
    export default defineConfig({
    integrations: [
    starlight({
    title: 'My Docs',
    // このサイトのデフォルト言語として英語を設定します。
    defaultLocale: 'en',
    locales: {
    // 英語のドキュメントは`src/content/docs/en/`に置きます。
    en: {
    label: 'English',
    },
    // 簡体字中国語のドキュメントは`src/content/docs/zh-cn/`に置きます。
    'zh-cn': {
    label: '简体中文',
    lang: 'zh-CN',
    },
    // アラビア語のドキュメントは`src/content/docs/ar/`に置きます。
    ar: {
    label: 'العربية',
    dir: 'rtl',
    },
    },
    }),
    ],
    });

    defaultLocaleはフォールバックコンテンツとUIラベルに使用されるため、コンテンツを最初に書く可能性が最も高い言語、またはすでにコンテンツがある言語を選択してください。

  2. src/content/docs/に各言語のディレクトリを作成します。たとえば上で示した構成に対しては、以下のフォルダを作成します。

    • ディレクトリsrc/
      • ディレクトリcontent/
        • ディレクトリdocs/
          • ディレクトリar/
          • ディレクトリen/
          • ディレクトリzh-cn/
  3. これで各言語のディレクトリにコンテンツ用のファイルを追加できるようになりました。言語間でページを関連付けるために、同じページには同じファイル名を使用してください。フォールバックコンテンツや翻訳に関する通知など、Starlightのi18n機能をフル活用しましょう。

    たとえば、ar/index.mden/index.mdは、アラビア語版と英語版のホームページをそれぞれ表わします。

より高度なi18nシナリオの場合、Astroのi18n設定オプションを使用した国際化の設定も可能です。

「ルート」ロケールを使用すると、パスにi18nプレフィックスを付けずにある言語を提供できます。たとえば英語をルートロケールとすると、英語のページパスは/en/aboutではなく/aboutのようになります。

ルートロケールを設定するには、localesrootキーを使用します。ルートロケールがコンテンツのデフォルトロケールでもある場合は、defaultLocaleを削除するか、'root'に設定します。

astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
integrations: [
starlight({
title: 'My Docs',
defaultLocale: 'root', // 任意
locales: {
root: {
label: 'English',
lang: 'en', // langはルートロケールに必要です
},
'zh-cn': {
label: '简体中文',
lang: 'zh-CN',
},
},
}),
],
});

rootロケールを使用する場合は、言語専用のフォルダではなく、src/content/docs/にその言語のページを直接保存します。たとえば上記の設定を使用した場合、英語と中国語のホームページ用ファイルは次のようになります。

  • ディレクトリsrc/
    • ディレクトリcontent/
      • ディレクトリdocs/
        • index.md
        • ディレクトリzh-cn/
          • index.md

デフォルトでは、Starlightは単一言語(英語)のサイトです。他の言語で単一言語のサイトを作成するには、その言語をlocalesにおいてrootに設定します。

astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
integrations: [
starlight({
title: '私のドキュメント',
locales: {
root: {
label: '简体中文',
lang: 'zh-CN',
},
},
}),
],
});

これにより、言語選択機能などの他の国際化機能は有効化せずに、Starlightのデフォルト言語を上書きできます。

Starlightは、すべての言語で同等のページが作成されることを想定しています。たとえばen/about.mdファイルがある場合、サポートする他の言語でもそれぞれabout.mdを作成してください。これにより、まだ翻訳されていないページに対して、Starlightが自動的にフォールバックコンテンツを提供できるようになります。

ある言語の翻訳がまだである場合、Starlightはそのページのコンテンツを(defaultLocaleで設定する)デフォルト言語で表示します。たとえば、概要(about)ページのフランス語版をまだ作成していない場合、デフォルト言語が英語であれば、/fr/aboutを訪れた人には未翻訳であるという通知とともに英語のコンテンツが表示されます。これにより、まずデフォルト言語にコンテンツを追加し、そして翻訳者が時間を掛けて徐々に翻訳していくことが可能となります。

デフォルトでは、Starlightはすべての言語で同じサイトタイトルを使用します。各ロケールのタイトルをカスタマイズする必要がある場合は、Starlightのオプションでtitleにオブジェクトを渡すことができます。

astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
integrations: [
starlight({
title: 'My Docs',
title: {
en: 'My Docs',
'zh-CN': '我的文档',
},
defaultLocale: 'en',
locales: {
en: { label: 'English' },
'zh-cn': { label: '简体中文', lang: 'zh-CN' },
},
}),
],
});

Starlightでは、読者が選択した言語でサイト全体を体験できるように、翻訳されたコンテンツファイルをホストするだけでなく、デフォルトUIの文字列(たとえば目次に表示されている「目次」という見出し)も翻訳できるようになっています。

アラビア語、イタリア語、インドネシア語、ウクライナ語、オランダ語、カタロニア語、ガリシア語、ギリシャ語、スウェーデン語、スペイン語、スロバキア語、タイ語、チェコ語、デンマーク語、ドイツ語、トルコ語、ノルウェー語(ブークモール)、ハンガリー語、ヒンディー語、フィンランド語、フランス語、ベトナム語、ヘブライ語、ペルシア語、ポーランド語、ポルトガル語、ラトビア語、ルーマニア語、ロシア語、英語、韓国語、中国語、中国語 (台湾)、日本語の翻訳済みUI文字列がすでに用意されていますが、デフォルト言語をさらに追加するための貢献も歓迎します。

18nデータコレクションを使用すると、サポートしたい言語の翻訳を追加したり、デフォルトのラベルを上書きしたりできます。

  1. 現状が未設定の場合は、src/content.config.tsi18nデータコレクションを設定します。

    src/content.config.ts
    import { defineCollection } from 'astro:content';
    import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders';
    import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
    export const collections = {
    docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
    i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() }),
    };
  2. UI文字列を追加で翻訳したいロケールごとに、src/content/i18n/にJSONファイルを作成します。たとえば、以下ではアラビア語と簡体字中国語の翻訳ファイルを追加しています。

    • ディレクトリsrc/
      • ディレクトリcontent/
        • ディレクトリi18n/
          • ar.json
          • zh-CN.json
  3. 翻訳したいキーに対応する翻訳を、JSONファイルに追加します。キーは英語のままとし、値のみを翻訳します(例: "search.label": "Buscar")。

    以下は、Starlightが英語版のデフォルトとして使用している文字列です。

    {
    "skipLink.label": "Skip to content",
    "search.label": "Search",
    "search.ctrlKey": "Ctrl",
    "search.cancelLabel": "Cancel",
    "search.devWarning": "Search is only available in production builds. \nTry building and previewing the site to test it out locally.",
    "themeSelect.accessibleLabel": "Select theme",
    "themeSelect.dark": "Dark",
    "themeSelect.light": "Light",
    "themeSelect.auto": "Auto",
    "languageSelect.accessibleLabel": "Select language",
    "menuButton.accessibleLabel": "Menu",
    "sidebarNav.accessibleLabel": "Main",
    "tableOfContents.onThisPage": "On this page",
    "tableOfContents.overview": "Overview",
    "i18n.untranslatedContent": "This content is not available in your language yet.",
    "page.editLink": "Edit page",
    "page.lastUpdated": "Last updated:",
    "page.previousLink": "Previous",
    "page.nextLink": "Next",
    "page.draft": "This content is a draft and will not be included in production builds.",
    "404.text": "Page not found. Check the URL or try using the search bar.",
    "aside.note": "Note",
    "aside.tip": "Tip",
    "aside.caution": "Caution",
    "aside.danger": "Danger",
    "fileTree.directory": "Directory",
    "builtWithStarlight.label": "Built with Starlight",
    "heading.anchorLabel": "Section titled “{{title}}”"
    }

    Starlightのコードブロックは、Expressive Codeライブラリによって動作しています。expressiveCodeキーを使用して、同じJSONファイルでUI文字列の翻訳について設定できます。

    {
    "expressiveCode.copyButtonCopied": "Copied!",
    "expressiveCode.copyButtonTooltip": "Copy to clipboard",
    "expressiveCode.terminalWindowFallbackTitle": "Terminal window"
    }

    Starlightの検索モーダルは、Pagefindライブラリによって動作しています。PagefindのUIの翻訳は、pagefindキーを使用して同じJSONファイルに設定できます。

    {
    "pagefind.clear_search": "Clear",
    "pagefind.load_more": "Load more results",
    "pagefind.search_label": "Search this site",
    "pagefind.filters_label": "Filters",
    "pagefind.zero_results": "No results for [SEARCH_TERM]",
    "pagefind.many_results": "[COUNT] results for [SEARCH_TERM]",
    "pagefind.one_result": "[COUNT] result for [SEARCH_TERM]",
    "pagefind.alt_search": "No results for [SEARCH_TERM]. Showing results for [DIFFERENT_TERM] instead",
    "pagefind.search_suggestion": "No results for [SEARCH_TERM]. Try one of the following searches:",
    "pagefind.searching": "Searching for [SEARCH_TERM]..."
    }

i18nSchema()オプションのextendを設定することで、サイトの翻訳辞書にカスタムキーを追加できます。以下の例では、オプションのcustom.labelキーが新たにデフォルトのキーに追加されています。

src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders';
import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
export const collections = {
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
i18n: defineCollection({
loader: i18nLoader(),
schema: i18nSchema({
extend: z.object({
'custom.label': z.string().optional(),
}),
}),
}),
};

コンテンツコレクションのスキーマについて詳しくは、Astroドキュメントの「コレクションスキーマの定義」を参照してください。

Starlightの組み込みUI文字列ユーザー定義のUI文字列、およびプラグインが提供するUI文字列には、i18nextを利用した共通のAPIを通じてアクセスできます。この共通のAPIは、文字列補間複数形対応などの機能もサポートしています。

Astroコンポーネントにおいて、この共通のAPIはグローバルAstroオブジェクトの一部としてAstro.locals.tから利用できます。

example.astro
<p dir={Astro.locals.t.dir()}>
{Astro.locals.t('404.text')}
</p>

エンドポイントでもこの共通のAPIを使用できます。localsオブジェクトはエンドポイントコンテキストの一部として利用できます。

src/pages/404.ts
export const GET = (context) => {
return new Response(context.locals.t('404.text'));
};

Starlightプラグインのコンテキストでは、useTranslations()ヘルパーを使用して、特定の言語に対するこの共通のAPIにアクセスできます。詳しくは、プラグインリファレンスを参照してください。

locals.t()関数を使用してUI文字列をレンダリングします。これはi18nextのt()関数のインスタンスで、最初の引数としてUI文字列のキーを受け取り、現在の言語に対応する翻訳を返します。

たとえば、以下の内容のカスタム翻訳ファイルがあるとします。

src/content/i18n/en.json
{
"link.astro": "Astro documentation",
"link.astro.custom": "Astro documentation for {{feature}}"
}

最初のUI文字列は、t()関数に'link.astro'を渡すことでレンダリングできます。

src/components/Example.astro
<a href="https://docs.astro.build/">
{Astro.locals.t('link.astro')}
</a>
<!-- レンダリング結果: <a href="...">Astro documentation</a> -->

2番目のUI文字列は、{{feature}}のプレースホルダーにi18nextの補間構文を使用しています。featureの値は、t()の第2引数として渡すオプションオブジェクトで設定する必要があります。

src/components/Example.astro
<a href="https://docs.astro.build/en/guides/astro-db/">
{Astro.locals.t('link.astro.custom', { feature: 'Astro DB' })}
</a>
<!-- レンダリング結果: <a href="...">Astro documentation for Astro DB</a> -->

補間やフォーマットなどのt()関数の使い方について詳しくは、i18nextのドキュメントを参照してください。

locals.t.all()関数は、現在のロケールで利用可能なすべてのUI文字列を含むオブジェクトを返します。

src/components/Example.astro
---
const allStrings = Astro.locals.t.all();
// ^
// {
// "skipLink.label": "Skip to content",
// "search.label": "Search",
// …
// }
---

翻訳キーが存在するかどうかを確認するには、locals.t.exists()関数の最初の引数に翻訳キーを渡します。特定のロケールに対する翻訳が存在するかどうかを確認する必要がある場合は、オプションの第2引数を渡します。

src/components/Example.astro
---
const keyExists = Astro.locals.t.exists('a.key');
// ^ true
const keyExistsInFrench = Astro.locals.t.exists('other.key', { lngs: ['fr'] });
// ^ false
---

詳しくは、i18nextドキュメントのexists()リファレンスを参照してください。

locals.t.dir()関数は、現在のロケールまたは指定したロケールのテキスト方向を返します。

src/components/Example.astro
---
const currentDirection = Astro.locals.t.dir();
// ^
// 'ltr'
const arabicDirection = Astro.locals.t.dir('ar');
// ^
// 'rtl'
---

詳しくは、i18nextドキュメントのdir()リファレンスを参照してください。

現在のロケールにアクセスする

Section titled “現在のロケールにアクセスする”

.astroコンポーネントで現在のロケールを読み取るためには、Astro.currentLocaleを使用できます。

以下の例では、現在のロケールを読み取り、getRelativeLocaleUrl()ヘルパーと組み合わせて、現在の言語での概要ページへのリンクを生成しています。

src/components/AboutLink.astro
---
import { getRelativeLocaleUrl } from 'astro:i18n';
---
<a href={getRelativeLocaleUrl(Astro.currentLocale ?? 'en', 'about')}>About</a>