@intlayer/docs 7.5.11 → 7.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/ar/intlayer_with_i18next.md +0 -2
- package/blog/ar/intlayer_with_next-i18next.md +0 -2
- package/blog/ar/intlayer_with_react-i18next.md +0 -2
- package/blog/de/intlayer_with_i18next.md +0 -45
- package/blog/de/intlayer_with_next-i18next.md +0 -46
- package/blog/de/intlayer_with_react-i18next.md +0 -2
- package/blog/en/intlayer_with_i18next.md +0 -46
- package/blog/en/intlayer_with_next-i18next.md +0 -48
- package/blog/en/intlayer_with_next-intl.md +0 -44
- package/blog/en/intlayer_with_react-i18next.md +0 -44
- package/blog/en/intlayer_with_react-intl.md +0 -42
- package/blog/en/intlayer_with_vue-i18n.md +0 -44
- package/blog/en-GB/intlayer_with_i18next.md +0 -45
- package/blog/en-GB/intlayer_with_next-i18next.md +0 -47
- package/blog/en-GB/intlayer_with_next-intl.md +0 -42
- package/blog/en-GB/intlayer_with_react-i18next.md +0 -43
- package/blog/en-GB/intlayer_with_react-intl.md +0 -42
- package/blog/en-GB/intlayer_with_vue-i18n.md +0 -46
- package/blog/es/intlayer_with_i18next.md +0 -45
- package/blog/es/intlayer_with_next-i18next.md +0 -47
- package/blog/es/intlayer_with_next-intl.md +0 -42
- package/blog/es/intlayer_with_react-i18next.md +0 -43
- package/blog/es/intlayer_with_react-intl.md +0 -42
- package/blog/es/intlayer_with_vue-i18n.md +0 -46
- package/blog/fr/intlayer_with_i18next.md +0 -45
- package/blog/fr/intlayer_with_next-i18next.md +0 -47
- package/blog/fr/intlayer_with_next-intl.md +0 -42
- package/blog/fr/intlayer_with_react-i18next.md +0 -43
- package/blog/fr/intlayer_with_react-intl.md +0 -42
- package/blog/fr/intlayer_with_vue-i18n.md +0 -46
- package/blog/hi/intlayer_with_i18next.md +0 -2
- package/blog/hi/intlayer_with_next-i18next.md +0 -2
- package/blog/hi/intlayer_with_react-i18next.md +0 -2
- package/blog/id/intlayer_with_i18next.md +0 -2
- package/blog/id/intlayer_with_next-i18next.md +0 -2
- package/blog/id/intlayer_with_react-i18next.md +0 -2
- package/blog/it/intlayer_with_i18next.md +0 -2
- package/blog/it/intlayer_with_next-i18next.md +0 -2
- package/blog/it/intlayer_with_react-i18next.md +0 -2
- package/blog/ja/intlayer_with_i18next.md +0 -45
- package/blog/ja/intlayer_with_next-i18next.md +0 -46
- package/blog/ja/intlayer_with_next-intl.md +0 -42
- package/blog/ja/intlayer_with_react-i18next.md +0 -42
- package/blog/ja/intlayer_with_react-intl.md +0 -42
- package/blog/ja/intlayer_with_vue-i18n.md +0 -46
- package/blog/ko/intlayer_with_i18next.md +0 -2
- package/blog/ko/intlayer_with_next-i18next.md +0 -2
- package/blog/ko/intlayer_with_react-i18next.md +0 -1
- package/blog/pl/intlayer_with_i18next.md +0 -45
- package/blog/pl/intlayer_with_next-i18next.md +0 -46
- package/blog/pl/intlayer_with_next-intl.md +0 -42
- package/blog/pl/intlayer_with_react-i18next.md +0 -43
- package/blog/pl/intlayer_with_react-intl.md +0 -42
- package/blog/pl/intlayer_with_vue-i18n.md +0 -46
- package/blog/pt/intlayer_with_i18next.md +0 -2
- package/blog/pt/intlayer_with_next-i18next.md +0 -2
- package/blog/pt/intlayer_with_react-i18next.md +0 -2
- package/blog/ru/intlayer_with_i18next.md +0 -45
- package/blog/ru/intlayer_with_next-i18next.md +0 -47
- package/blog/ru/intlayer_with_next-intl.md +0 -42
- package/blog/ru/intlayer_with_react-i18next.md +0 -43
- package/blog/ru/intlayer_with_react-intl.md +0 -42
- package/blog/ru/intlayer_with_vue-i18n.md +0 -46
- package/blog/tr/intlayer_with_i18next.md +0 -2
- package/blog/tr/intlayer_with_next-i18next.md +0 -1
- package/blog/tr/intlayer_with_react-i18next.md +0 -2
- package/blog/vi/intlayer_with_i18next.md +0 -2
- package/blog/vi/intlayer_with_next-i18next.md +0 -2
- package/blog/vi/intlayer_with_react-i18next.md +0 -2
- package/blog/zh/intlayer_with_i18next.md +0 -2
- package/blog/zh/intlayer_with_next-i18next.md +0 -2
- package/blog/zh/intlayer_with_react-i18next.md +0 -2
- package/blog/zh/intlayer_with_vue-i18n.md +0 -46
- package/dist/cjs/generated/blog.entry.cjs +58 -29
- package/dist/cjs/generated/blog.entry.cjs.map +1 -1
- package/dist/cjs/generated/docs.entry.cjs +218 -99
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/cjs/generated/frequentQuestions.entry.cjs +30 -15
- package/dist/cjs/generated/frequentQuestions.entry.cjs.map +1 -1
- package/dist/cjs/generated/legal.entry.cjs +4 -2
- package/dist/cjs/generated/legal.entry.cjs.map +1 -1
- package/dist/esm/generated/blog.entry.mjs +58 -29
- package/dist/esm/generated/blog.entry.mjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +218 -99
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/esm/generated/frequentQuestions.entry.mjs +30 -15
- package/dist/esm/generated/frequentQuestions.entry.mjs.map +1 -1
- package/dist/esm/generated/legal.entry.mjs +4 -2
- package/dist/esm/generated/legal.entry.mjs.map +1 -1
- package/dist/types/generated/blog.entry.d.ts.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +1 -0
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/dist/types/generated/frequentQuestions.entry.d.ts.map +1 -1
- package/dist/types/generated/legal.entry.d.ts.map +1 -1
- package/docs/ar/intlayer_with_next-i18next.md +0 -1
- package/docs/ar/intlayer_with_nextjs_14.md +28 -0
- package/docs/ar/intlayer_with_nextjs_15.md +28 -0
- package/docs/ar/intlayer_with_nextjs_16.md +28 -0
- package/docs/ar/intlayer_with_nextjs_no_locale_path.md +1159 -0
- package/docs/ar/plugins/sync-json.md +6 -2
- package/docs/de/intlayer_with_next-i18next.md +0 -1
- package/docs/de/intlayer_with_nextjs_14.md +28 -0
- package/docs/de/intlayer_with_nextjs_15.md +28 -0
- package/docs/de/intlayer_with_nextjs_16.md +28 -0
- package/docs/de/intlayer_with_nextjs_no_locale_path.md +1152 -0
- package/docs/de/plugins/sync-json.md +6 -2
- package/docs/en/intlayer_with_next-i18next.md +0 -1
- package/docs/en/intlayer_with_nextjs_14.md +28 -0
- package/docs/en/intlayer_with_nextjs_15.md +28 -0
- package/docs/en/intlayer_with_nextjs_16.md +31 -1
- package/docs/en/intlayer_with_nextjs_no_locale_path.md +1132 -0
- package/docs/en/plugins/sync-json.md +6 -2
- package/docs/en-GB/intlayer_with_next-i18next.md +0 -1
- package/docs/en-GB/intlayer_with_nextjs_14.md +28 -0
- package/docs/en-GB/intlayer_with_nextjs_15.md +28 -0
- package/docs/en-GB/intlayer_with_nextjs_16.md +28 -0
- package/docs/en-GB/intlayer_with_nextjs_no_locale_path.md +1154 -0
- package/docs/en-GB/plugins/sync-json.md +6 -2
- package/docs/es/intlayer_with_next-i18next.md +0 -1
- package/docs/es/intlayer_with_nextjs_14.md +28 -0
- package/docs/es/intlayer_with_nextjs_15.md +28 -0
- package/docs/es/intlayer_with_nextjs_16.md +28 -0
- package/docs/es/intlayer_with_nextjs_no_locale_path.md +1143 -0
- package/docs/es/plugins/sync-json.md +6 -2
- package/docs/fr/intlayer_with_next-i18next.md +0 -1
- package/docs/fr/intlayer_with_nextjs_14.md +28 -0
- package/docs/fr/intlayer_with_nextjs_15.md +28 -0
- package/docs/fr/intlayer_with_nextjs_16.md +28 -0
- package/docs/fr/intlayer_with_nextjs_no_locale_path.md +1174 -0
- package/docs/fr/plugins/sync-json.md +9 -5
- package/docs/hi/intlayer_with_next-i18next.md +0 -1
- package/docs/hi/intlayer_with_nextjs_14.md +28 -0
- package/docs/hi/intlayer_with_nextjs_15.md +28 -0
- package/docs/hi/intlayer_with_nextjs_16.md +28 -0
- package/docs/hi/intlayer_with_nextjs_no_locale_path.md +1151 -0
- package/docs/hi/plugins/sync-json.md +6 -2
- package/docs/id/intlayer_with_next-i18next.md +0 -1
- package/docs/id/intlayer_with_nextjs_14.md +28 -0
- package/docs/id/intlayer_with_nextjs_15.md +28 -0
- package/docs/id/intlayer_with_nextjs_16.md +28 -0
- package/docs/id/intlayer_with_nextjs_no_locale_path.md +1154 -0
- package/docs/id/plugins/sync-json.md +6 -2
- package/docs/it/intlayer_with_next-i18next.md +0 -1
- package/docs/it/intlayer_with_nextjs_14.md +28 -0
- package/docs/it/intlayer_with_nextjs_15.md +28 -0
- package/docs/it/intlayer_with_nextjs_16.md +28 -0
- package/docs/it/intlayer_with_nextjs_no_locale_path.md +1148 -0
- package/docs/it/plugins/sync-json.md +6 -2
- package/docs/ja/intlayer_with_next-i18next.md +0 -1
- package/docs/ja/intlayer_with_nextjs_14.md +28 -0
- package/docs/ja/intlayer_with_nextjs_15.md +28 -0
- package/docs/ja/intlayer_with_nextjs_16.md +28 -0
- package/docs/ja/intlayer_with_nextjs_no_locale_path.md +1222 -0
- package/docs/ja/plugins/sync-json.md +6 -2
- package/docs/ko/intlayer_with_next-i18next.md +0 -1
- package/docs/ko/intlayer_with_nextjs_14.md +28 -0
- package/docs/ko/intlayer_with_nextjs_15.md +28 -0
- package/docs/ko/intlayer_with_nextjs_16.md +28 -0
- package/docs/ko/intlayer_with_nextjs_no_locale_path.md +1205 -0
- package/docs/ko/plugins/sync-json.md +6 -2
- package/docs/pl/intlayer_with_next-i18next.md +0 -1
- package/docs/pl/intlayer_with_nextjs_14.md +28 -0
- package/docs/pl/intlayer_with_nextjs_15.md +28 -0
- package/docs/pl/intlayer_with_nextjs_16.md +28 -0
- package/docs/pl/intlayer_with_nextjs_no_locale_path.md +1149 -0
- package/docs/pl/plugins/sync-json.md +6 -2
- package/docs/pt/intlayer_with_next-i18next.md +0 -1
- package/docs/pt/intlayer_with_nextjs_14.md +28 -0
- package/docs/pt/intlayer_with_nextjs_15.md +28 -0
- package/docs/pt/intlayer_with_nextjs_16.md +28 -0
- package/docs/pt/intlayer_with_nextjs_no_locale_path.md +1152 -0
- package/docs/pt/plugins/sync-json.md +6 -2
- package/docs/ru/intlayer_with_next-i18next.md +0 -1
- package/docs/ru/intlayer_with_nextjs_14.md +28 -0
- package/docs/ru/intlayer_with_nextjs_15.md +28 -0
- package/docs/ru/intlayer_with_nextjs_16.md +28 -0
- package/docs/ru/intlayer_with_nextjs_no_locale_path.md +1204 -0
- package/docs/ru/plugins/sync-json.md +6 -2
- package/docs/tr/intlayer_with_next-i18next.md +0 -1
- package/docs/tr/intlayer_with_nextjs_14.md +28 -0
- package/docs/tr/intlayer_with_nextjs_15.md +28 -0
- package/docs/tr/intlayer_with_nextjs_16.md +28 -0
- package/docs/tr/intlayer_with_nextjs_no_locale_path.md +1159 -0
- package/docs/tr/plugins/sync-json.md +6 -2
- package/docs/uk/compiler.md +133 -0
- package/docs/uk/component_i18n.md +194 -0
- package/docs/uk/intlayer_with_nextjs_14.md +1646 -0
- package/docs/uk/intlayer_with_nextjs_15.md +1910 -0
- package/docs/uk/intlayer_with_nextjs_16.md +1763 -0
- package/docs/uk/intlayer_with_nextjs_no_locale_path.md +1159 -0
- package/docs/uk/intlayer_with_react_native+expo.md +715 -0
- package/docs/uk/packages/intlayer/getConfiguration.md +145 -0
- package/docs/uk/vs_code_extension.md +133 -0
- package/docs/vi/intlayer_with_next-i18next.md +0 -1
- package/docs/vi/intlayer_with_nextjs_14.md +28 -0
- package/docs/vi/intlayer_with_nextjs_15.md +28 -0
- package/docs/vi/intlayer_with_nextjs_16.md +28 -0
- package/docs/vi/intlayer_with_nextjs_no_locale_path.md +1151 -0
- package/docs/vi/plugins/sync-json.md +6 -2
- package/docs/zh/intlayer_with_next-i18next.md +0 -1
- package/docs/zh/intlayer_with_nextjs_14.md +28 -0
- package/docs/zh/intlayer_with_nextjs_15.md +28 -0
- package/docs/zh/intlayer_with_nextjs_16.md +28 -0
- package/docs/zh/intlayer_with_nextjs_no_locale_path.md +1206 -0
- package/docs/zh/plugins/sync-json.md +9 -5
- package/frequent_questions/ar/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/de/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/en/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/en-GB/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/es/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/fr/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/hi/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/id/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/it/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ja/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ko/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/pl/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/pt/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/ru/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/tr/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/vi/SSR_Next_no_[locale].md +1 -1
- package/frequent_questions/zh/SSR_Next_no_[locale].md +1 -1
- package/package.json +6 -6
- package/src/generated/blog.entry.ts +29 -0
- package/src/generated/docs.entry.ts +119 -0
- package/src/generated/frequentQuestions.entry.ts +15 -0
- package/src/generated/legal.entry.ts +2 -0
|
@@ -0,0 +1,1222 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-01-10
|
|
3
|
+
updatedAt: 2026-01-10
|
|
4
|
+
title: Next.js 16 アプリを翻訳する方法(ページパスに [locale] を含めない) – i18n ガイド 2026
|
|
5
|
+
description: ページパスに [locale] を含めずに Next.js 16 ウェブサイトを多言語対応にする方法を紹介します。ドキュメントに従って国際化(i18n)と翻訳を行ってください。
|
|
6
|
+
keywords:
|
|
7
|
+
- 国際化
|
|
8
|
+
- ドキュメント
|
|
9
|
+
- Intlayer
|
|
10
|
+
- Next.js 16
|
|
11
|
+
- JavaScript
|
|
12
|
+
- React
|
|
13
|
+
slugs:
|
|
14
|
+
- doc
|
|
15
|
+
- environment
|
|
16
|
+
- nextjs
|
|
17
|
+
- no-locale-path
|
|
18
|
+
applicationTemplate: https://github.com/aymericzip/intlayer-next-no-lolale-path-template
|
|
19
|
+
youtubeVideo: https://www.youtube.com/watch?v=e_PPG7PTqGU
|
|
20
|
+
history:
|
|
21
|
+
- version: 1.0.0
|
|
22
|
+
date: 2026-01-10
|
|
23
|
+
changes: 初回リリース
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Next.js 16 ウェブサイトを翻訳する(ページパスに [locale] を含めない) — Intlayer を使った国際化 (i18n)
|
|
27
|
+
|
|
28
|
+
<Tab defaultTab="video">
|
|
29
|
+
<TabItem label="動画" value="video">
|
|
30
|
+
|
|
31
|
+
<iframe title="Next.js に最適な i18n ソリューション?Intlayer をご紹介" class="m-auto aspect-16/9 w-full overflow-hidden rounded-lg border-0" allow="autoplay; gyroscope;" loading="lazy" width="1080" height="auto" src="https://www.youtube.com/embed/e_PPG7PTqGU?autoplay=0&origin=http://intlayer.org&controls=0&rel=1"/>
|
|
32
|
+
|
|
33
|
+
</TabItem>
|
|
34
|
+
<TabItem label="コード" value="code">
|
|
35
|
+
|
|
36
|
+
<iframe
|
|
37
|
+
src="https://stackblitz.com/github/aymericzip/intlayer-next-16-no-locale-path-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
38
|
+
className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
|
|
39
|
+
title="デモ CodeSandbox - Intlayer を使ってアプリケーションを国際化する方法"
|
|
40
|
+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
|
41
|
+
loading="lazy"
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
</TabItem>
|
|
45
|
+
</Tab>
|
|
46
|
+
|
|
47
|
+
GitHub の [アプリケーションテンプレート](https://github.com/aymericzip/intlayer-next-no-lolale-path-template) を参照してください。
|
|
48
|
+
|
|
49
|
+
## 目次
|
|
50
|
+
|
|
51
|
+
<TOC/>
|
|
52
|
+
|
|
53
|
+
## Intlayer とは?
|
|
54
|
+
|
|
55
|
+
**Intlayer** は、現代のウェブアプリケーションにおける多言語対応を簡素化するために設計された、革新的なオープンソースの国際化(i18n)ライブラリです。Intlayer は、強力な **App Router** を含む最新の **Next.js 16** フレームワークとシームレスに統合されます。効率的なレンダリングのために **Server Components** を活用するよう最適化されており、[**Turbopack**](https://nextjs.org/docs/architecture/turbopack) と完全に互換性があります。
|
|
56
|
+
|
|
57
|
+
Intlayer を使うと、以下が可能になります:
|
|
58
|
+
|
|
59
|
+
- **コンポーネントレベルで宣言的な辞書を使い、翻訳を簡単に管理**できます。
|
|
60
|
+
- **メタデータ、ルート、コンテンツを動的にローカライズ**できます。
|
|
61
|
+
- **クライアント側およびサーバー側のコンポーネントの両方で翻訳にアクセス**できます。
|
|
62
|
+
- **自動生成された型による TypeScript サポートを確保し、オートコンプリートとエラー検出を向上**させます。
|
|
63
|
+
- **高度な機能を活用できます**(動的なロケール検出や切り替えなど)。
|
|
64
|
+
|
|
65
|
+
> Intlayer は Next.js 12、13、14、16 と互換性があります。Next.js の Page Router を使用している場合はこの [ガイド](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/intlayer_with_nextjs_page_router.md) を参照してください。App Router を使用している Next.js 12、13、14 に関しては、この [ガイド](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/intlayer_with_nextjs_14.md) を参照してください。
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Next.js アプリケーションで Intlayer をセットアップする手順
|
|
70
|
+
|
|
71
|
+
### ステップ 1: 依存関係をインストール
|
|
72
|
+
|
|
73
|
+
npm を使用して必要なパッケージをインストールします:
|
|
74
|
+
|
|
75
|
+
```bash packageManager="npm"
|
|
76
|
+
npm install intlayer next-intlayer
|
|
77
|
+
npx intlayer init
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```bash packageManager="pnpm"
|
|
81
|
+
pnpm add intlayer next-intlayer
|
|
82
|
+
pnpm intlayer init
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```bash packageManager="yarn"
|
|
86
|
+
yarn add intlayer next-intlayer
|
|
87
|
+
yarn intlayer init
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```bash packageManager="bun"
|
|
91
|
+
bun add intlayer next-intlayer
|
|
92
|
+
bunx intlayer init
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **intlayer**
|
|
96
|
+
|
|
97
|
+
国際化(internationalization)向けのコアパッケージで、設定管理、翻訳、[コンテンツ宣言](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/dictionary/content_file.md)、transpilation、そして[CLIコマンド](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/cli/index.md)用のツールを提供します。
|
|
98
|
+
|
|
99
|
+
- **next-intlayer**
|
|
100
|
+
|
|
101
|
+
IntlayerをNext.jsと統合するパッケージです。Next.js向けの国際化のためのコンテキストプロバイダーとフックを提供します。さらに、[Webpack](https://webpack.js.org/)や[Turbopack](https://nextjs.org/docs/app/api-reference/turbopack)とIntlayerを統合するためのNext.jsプラグイン、およびユーザーの優先ロケールの検出、クッキー管理、URLリダイレクト処理のためのプロキシも含まれます。
|
|
102
|
+
|
|
103
|
+
### ステップ 2: プロジェクトを設定する
|
|
104
|
+
|
|
105
|
+
以下が作成する最終的な構成です:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
.
|
|
109
|
+
├── src
|
|
110
|
+
│ ├── app
|
|
111
|
+
│ │ ├── layout.tsx
|
|
112
|
+
│ │ ├── page.content.ts
|
|
113
|
+
│ │ └── page.tsx
|
|
114
|
+
│ ├── components
|
|
115
|
+
│ │ ├── clientComponentExample
|
|
116
|
+
│ │ │ ├── client-component-example.content.ts
|
|
117
|
+
│ │ │ └── ClientComponentExample.tsx
|
|
118
|
+
│ │ ├── localeSwitcher
|
|
119
|
+
│ │ │ ├── localeSwitcher.content.ts
|
|
120
|
+
│ │ │ └── LocaleSwitcher.tsx
|
|
121
|
+
│ │ └── serverComponentExample
|
|
122
|
+
│ │ ├── server-component-example.content.ts
|
|
123
|
+
│ │ └── ServerComponentExample.tsx
|
|
124
|
+
│ └── proxy.ts
|
|
125
|
+
├── intlayer.config.ts
|
|
126
|
+
├── next.config.ts
|
|
127
|
+
├── package.json
|
|
128
|
+
└── tsconfig.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
> ロケールルーティングを使用したくない場合、intlayerは単純なプロバイダー/フックとして使用できます。詳細は[このガイド](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/intlayer_with_nextjs_no_locale_path.md)を参照してください。
|
|
132
|
+
|
|
133
|
+
アプリケーションの言語を設定するための設定ファイルを作成します:
|
|
134
|
+
|
|
135
|
+
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
136
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
137
|
+
|
|
138
|
+
const config: IntlayerConfig = {
|
|
139
|
+
internationalization: {
|
|
140
|
+
locales: [
|
|
141
|
+
Locales.ENGLISH,
|
|
142
|
+
Locales.FRENCH,
|
|
143
|
+
Locales.SPANISH,
|
|
144
|
+
// 他のロケール
|
|
145
|
+
],
|
|
146
|
+
defaultLocale: Locales.ENGLISH,
|
|
147
|
+
},
|
|
148
|
+
routing: {
|
|
149
|
+
mode: "search-params", // または `no-prefix` - ミドルウェア検出に便利
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export default config;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```javascript fileName="intlayer.config.mjs" codeFormat="esm"
|
|
157
|
+
import { Locales } from "intlayer";
|
|
158
|
+
|
|
159
|
+
/** @type {import('intlayer').IntlayerConfig} */
|
|
160
|
+
const config = {
|
|
161
|
+
internationalization: {
|
|
162
|
+
locales: [
|
|
163
|
+
Locales.ENGLISH,
|
|
164
|
+
Locales.FRENCH,
|
|
165
|
+
Locales.SPANISH,
|
|
166
|
+
// 他のロケール
|
|
167
|
+
],
|
|
168
|
+
defaultLocale: Locales.ENGLISH,
|
|
169
|
+
},
|
|
170
|
+
routing: {
|
|
171
|
+
mode: "search-params", // または `no-prefix` - ミドルウェア検出に便利
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export default config;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```javascript fileName="intlayer.config.cjs" codeFormat="commonjs"
|
|
179
|
+
const { Locales } = require("intlayer");
|
|
180
|
+
|
|
181
|
+
/** @type {import('intlayer').IntlayerConfig} */
|
|
182
|
+
const config = {
|
|
183
|
+
internationalization: {
|
|
184
|
+
locales: [
|
|
185
|
+
Locales.ENGLISH,
|
|
186
|
+
Locales.FRENCH,
|
|
187
|
+
Locales.SPANISH,
|
|
188
|
+
// 他のロケール
|
|
189
|
+
],
|
|
190
|
+
defaultLocale: Locales.ENGLISH,
|
|
191
|
+
},
|
|
192
|
+
routing: {
|
|
193
|
+
mode: "search-params", // または `no-prefix` - ミドルウェア検出に便利
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
module.exports = config;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
> この設定ファイルを使って、ローカライズされた URL、プロキシリダイレクト、クッキー名、コンテンツ宣言の場所と拡張子、コンソール上の Intlayer ログの無効化などを設定できます。利用可能なパラメータの完全な一覧は、[設定ドキュメント](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/configuration.md) を参照してください。
|
|
201
|
+
|
|
202
|
+
### ステップ 3: Next.js の設定に Intlayer を統合する
|
|
203
|
+
|
|
204
|
+
Intlayer を使用するよう Next.js の設定を構成します:
|
|
205
|
+
|
|
206
|
+
```typescript fileName="next.config.ts" codeFormat="typescript"
|
|
207
|
+
import type { NextConfig } from "next";
|
|
208
|
+
import { withIntlayer } from "next-intlayer/server";
|
|
209
|
+
|
|
210
|
+
const nextConfig: NextConfig = {
|
|
211
|
+
/* ここに設定オプションを記述 */
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export default withIntlayer(nextConfig);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```typescript fileName="next.config.mjs" codeFormat="esm"
|
|
218
|
+
import { withIntlayer } from "next-intlayer/server";
|
|
219
|
+
|
|
220
|
+
/** @type {import('next').NextConfig} */
|
|
221
|
+
const nextConfig = {
|
|
222
|
+
/* 設定オプションをここに */
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export default withIntlayer(nextConfig);
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
```typescript fileName="next.config.cjs" codeFormat="commonjs"
|
|
229
|
+
const { withIntlayer } = require("next-intlayer/server");
|
|
230
|
+
|
|
231
|
+
/** @type {import('next').NextConfig} */
|
|
232
|
+
const nextConfig = {
|
|
233
|
+
/* 設定オプションをここに */
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
module.exports = withIntlayer(nextConfig);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
> `withIntlayer()` の Next.js プラグインは Intlayer を Next.js に統合するために使用されます。コンテンツ宣言ファイルの生成を行い、開発モードでそれらを監視します。[Webpack](https://webpack.js.org/) または [Turbopack](https://nextjs.org/docs/app/api-reference/turbopack) 環境内で Intlayer の環境変数を定義します。さらに、パフォーマンス最適化のためのエイリアスを提供し、サーバーコンポーネントとの互換性を確保します。
|
|
240
|
+
|
|
241
|
+
> `withIntlayer()` 関数は Promise 関数です。ビルド開始前に Intlayer の辞書を準備することを可能にします。他のプラグインと一緒に使用する場合は、await できます。例:
|
|
242
|
+
>
|
|
243
|
+
> ```ts
|
|
244
|
+
> const nextConfig = await withIntlayer(nextConfig);
|
|
245
|
+
> const nextConfigWithOtherPlugins = withOtherPlugins(nextConfig);
|
|
246
|
+
>
|
|
247
|
+
> export default nextConfigWithOtherPlugins;
|
|
248
|
+
> ```
|
|
249
|
+
>
|
|
250
|
+
> 同期的に使用したい場合は、`withIntlayerSync()` 関数を使用できます。例:
|
|
251
|
+
>
|
|
252
|
+
> ```ts
|
|
253
|
+
> const nextConfig = withIntlayerSync(nextConfig);
|
|
254
|
+
> const nextConfigWithOtherPlugins = withOtherPlugins(nextConfig);
|
|
255
|
+
>
|
|
256
|
+
> export default nextConfigWithOtherPlugins;
|
|
257
|
+
> ```
|
|
258
|
+
>
|
|
259
|
+
> Intlayer は、コマンドラインフラグ(`--webpack`、`--turbo`、または `--turbopack`)や現在の **Next.js のバージョン**に基づいて、プロジェクトが **webpack** または **Turbopack** のどちらを使用しているかを自動的に検出します。
|
|
260
|
+
>
|
|
261
|
+
> `next>=16` 以降、**Rspack** を使用している場合は、Turbopack を無効化して Intlayer に webpack 設定を明示的に使用させる必要があります:
|
|
262
|
+
>
|
|
263
|
+
> ```ts
|
|
264
|
+
> withRspack(withIntlayer(nextConfig, { enableTurbopack: false }));
|
|
265
|
+
> ```
|
|
266
|
+
|
|
267
|
+
### ステップ 4: 動的ロケールルートを定義する
|
|
268
|
+
|
|
269
|
+
`RootLayout` の中身をすべて削除し、次のコードに置き換えてください:
|
|
270
|
+
|
|
271
|
+
```tsx {3} fileName="src/app/layout.tsx" codeFormat="typescript"
|
|
272
|
+
import type { Metadata } from "next";
|
|
273
|
+
import type { ReactNode } from "react";
|
|
274
|
+
import "./globals.css";
|
|
275
|
+
import { IntlayerClientProvider, LocalPromiseParams } from "next-intlayer";
|
|
276
|
+
import { getHTMLTextDir, getIntlayer } from "intlayer";
|
|
277
|
+
import { getLocale } from "next-intlayer/server";
|
|
278
|
+
export { generateStaticParams } from "next-intlayer";
|
|
279
|
+
|
|
280
|
+
export const generateMetadata = async ({
|
|
281
|
+
params,
|
|
282
|
+
}: LocalPromiseParams): Promise<Metadata> => {
|
|
283
|
+
const { locale } = await params;
|
|
284
|
+
const { title, description, keywords } = getIntlayer("metadata", locale);
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
title,
|
|
288
|
+
description,
|
|
289
|
+
keywords,
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const RootLayout = async ({
|
|
294
|
+
children,
|
|
295
|
+
}: Readonly<{
|
|
296
|
+
children: ReactNode;
|
|
297
|
+
}>) => {
|
|
298
|
+
const locale = await getLocale();
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
302
|
+
<IntlayerClientProvider defaultLocale={locale}>
|
|
303
|
+
<body>{children}</body>
|
|
304
|
+
</IntlayerClientProvider>
|
|
305
|
+
</html>
|
|
306
|
+
);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
export default RootLayout;
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
```jsx {3} fileName="src/app/layout.mjx" codeFormat="esm"
|
|
313
|
+
import "./globals.css";
|
|
314
|
+
import { IntlayerClientProvider } from "next-intlayer";
|
|
315
|
+
import { getHTMLTextDir, getIntlayer } from "intlayer";
|
|
316
|
+
import { getLocale } from "next-intlayer/server";
|
|
317
|
+
import { headers, cookies } from "next/headers";
|
|
318
|
+
export { generateStaticParams } from "next/intlayer";
|
|
319
|
+
|
|
320
|
+
export const generateMetadata = async ({ params }) => {
|
|
321
|
+
const { locale } = await params;
|
|
322
|
+
const { title, description, keywords } = getIntlayer("metadata", locale);
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
title,
|
|
326
|
+
description,
|
|
327
|
+
keywords,
|
|
328
|
+
};
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const RootLayout = async ({ children }) => {
|
|
332
|
+
// Next.js 15+ では headers と cookies を await する
|
|
333
|
+
const headerList = await headers();
|
|
334
|
+
const cookieList = await cookies();
|
|
335
|
+
|
|
336
|
+
const locale = await getLocale({
|
|
337
|
+
// まず intlayer のクッキーを確認(デフォルト: 'INTLAYER_LOCALE')
|
|
338
|
+
getCookie: (name) => cookieList.get(name)?.value,
|
|
339
|
+
|
|
340
|
+
// 次に intlayer のヘッダーを確認(デフォルト: 'x-intlayer-locale')
|
|
341
|
+
// 最後に accept-language ヘッダー ('accept-language') を確認します
|
|
342
|
+
getHeader: (name) => headerList.get(name),
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
347
|
+
<IntlayerClientProvider defaultLocale={locale}>
|
|
348
|
+
<body>{children}</body>
|
|
349
|
+
</IntlayerClientProvider>
|
|
350
|
+
</html>
|
|
351
|
+
);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
export default RootLayout;
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
````jsx {1,8} fileName="src/app/layout.csx" codeFormat="commonjs"
|
|
358
|
+
require("./globals.css");
|
|
359
|
+
const { IntlayerClientProvider } = require("next-intlayer");
|
|
360
|
+
const { getHTMLTextDir, getIntlayer } = require("intlayer");
|
|
361
|
+
const { getLocale } = require("next-intlayer/server");
|
|
362
|
+
const { headers, cookies } = require("next/headers");
|
|
363
|
+
const { generateStaticParams } = require("next-intlayer");
|
|
364
|
+
|
|
365
|
+
const generateMetadata = async ({ params }) => {
|
|
366
|
+
const { locale } = await params;
|
|
367
|
+
const { title, description, keywords } = getIntlayer("metadata", locale);
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
title,
|
|
371
|
+
description,
|
|
372
|
+
keywords,
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const RootLayout = async ({ children }) => {
|
|
377
|
+
// Next.js 15+ではheadersとcookiesをawaitします
|
|
378
|
+
const headerList = await headers();
|
|
379
|
+
const cookieList = await cookies();
|
|
380
|
+
|
|
381
|
+
const locale = await getLocale({
|
|
382
|
+
// まずintlayerのクッキー(デフォルト: 'INTLAYER_LOCALE')を確認します
|
|
383
|
+
getCookie: (name) => cookieList.get(name)?.value,
|
|
384
|
+
|
|
385
|
+
// 次にintlayerヘッダー(デフォルト: 'x-intlayer-locale')を確認します
|
|
386
|
+
// 最後にaccept-languageヘッダー('accept-language')を確認します
|
|
387
|
+
getHeader: (name) => headerList.get(name),
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
392
|
+
<IntlayerClientProvider defaultLocale={locale}>
|
|
393
|
+
<body>{children}</body>
|
|
394
|
+
</IntlayerClientProvider>
|
|
395
|
+
</html>
|
|
396
|
+
);
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
module.exports = {
|
|
400
|
+
default: RootLayout,
|
|
401
|
+
generateStaticParams,
|
|
402
|
+
generateMetadata,
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
### ステップ 5: コンテンツを宣言する
|
|
406
|
+
|
|
407
|
+
翻訳を保存するためのコンテンツ宣言を作成・管理します:
|
|
408
|
+
|
|
409
|
+
```tsx fileName="src/app/metadata.content.ts" contentDeclarationFormat="typescript"
|
|
410
|
+
import { t, type Dictionary } from "intlayer";
|
|
411
|
+
import { Metadata } from "next";
|
|
412
|
+
|
|
413
|
+
const metadataContent = {
|
|
414
|
+
key: "metadata",
|
|
415
|
+
content: {
|
|
416
|
+
title: t({
|
|
417
|
+
ja: "私のプロジェクトのタイトル",
|
|
418
|
+
en: "My Project Title",
|
|
419
|
+
fr: "Le Titre de mon Projet",
|
|
420
|
+
es: "El Título de mi Proyecto",
|
|
421
|
+
}),
|
|
422
|
+
|
|
423
|
+
description: t({
|
|
424
|
+
ja: "ワークフローを簡素化し生産性を高めるよう設計された革新的なプラットフォームをご覧ください。",
|
|
425
|
+
en: "Discover our innovative platform designed to streamline your workflow and boost productivity.",
|
|
426
|
+
ja: "ワークフローを効率化し、生産性を向上させるよう設計された革新的なプラットフォームをご紹介します。",
|
|
427
|
+
fr: "Découvrez notre plateforme innovante conçue pour simplifier votre flux de travail et booster votre productivité.",
|
|
428
|
+
es: "Descubra nuestra plataforma innovadora diseñada para simplificar su flujo de trabajo y aumentar su productividad。",
|
|
429
|
+
}),
|
|
430
|
+
|
|
431
|
+
keywords: t({
|
|
432
|
+
ja: ["イノベーション", "生産性", "ワークフロー", "SaaS"],
|
|
433
|
+
en: ["innovation", "productivity", "workflow", "SaaS"],
|
|
434
|
+
fr: ["innovation", "productivité", "flux de travail", "SaaS"],
|
|
435
|
+
es: ["innovación", "productividad", "flujo de trabajo", "SaaS"],
|
|
436
|
+
}),
|
|
437
|
+
},
|
|
438
|
+
} as Dictionary<Metadata>;
|
|
439
|
+
|
|
440
|
+
export default metadataContent;
|
|
441
|
+
````
|
|
442
|
+
|
|
443
|
+
```tsx fileName="src/app/metadata.content.mjs" contentDeclarationFormat="typescript"
|
|
444
|
+
import { t, type Dictionary } from "intlayer";
|
|
445
|
+
|
|
446
|
+
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
447
|
+
const metadataContent = {
|
|
448
|
+
key: "metadata",
|
|
449
|
+
content: {
|
|
450
|
+
title: t({
|
|
451
|
+
ja: "ワークフローを合理化する私のプロジェクトのタイトル",
|
|
452
|
+
en: "My Project Title",
|
|
453
|
+
fr: "Le Titre de mon Projet",
|
|
454
|
+
es: "El Título de mi Proyecto",
|
|
455
|
+
}),
|
|
456
|
+
|
|
457
|
+
description: t({
|
|
458
|
+
ja: "ワークフローを効率化し、生産性を向上させるために設計された革新的なプラットフォームをご紹介します。",
|
|
459
|
+
en: "Discover our innovative platform designed to streamline your workflow and boost productivity.",
|
|
460
|
+
fr: "Découvrez notre plateforme innovante conçue pour simplifier votre flux de travail et booster votre productivité.",
|
|
461
|
+
es: "Descubra nuestra plataforma innovadora diseñada para simplificar su flujo de trabajo y aumentar su productividad.",
|
|
462
|
+
}),
|
|
463
|
+
|
|
464
|
+
keywords: t({
|
|
465
|
+
ja: ["イノベーション", "生産性", "ワークフロー", "SaaS"],
|
|
466
|
+
en: ["innovation", "productivity", "workflow", "SaaS"],
|
|
467
|
+
fr: ["innovation", "productivité", "flux de travail", "SaaS"],
|
|
468
|
+
es: ["innovación", "productividad", "flujo de trabajo", "SaaS"],
|
|
469
|
+
}),
|
|
470
|
+
},
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
export default metadataContent;
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
```javascript fileName="src/app/metadata.content.cjs" contentDeclarationFormat="commonjs"
|
|
477
|
+
const { t } = require("intlayer");
|
|
478
|
+
|
|
479
|
+
/** @type {import('intlayer').Dictionary<import('next').Metadata>} */
|
|
480
|
+
const metadataContent = {
|
|
481
|
+
key: "metadata",
|
|
482
|
+
content: {
|
|
483
|
+
title: t({
|
|
484
|
+
ja: "私のプロジェクトのタイトル",
|
|
485
|
+
en: "My Project Title",
|
|
486
|
+
fr: "Le Titre de mon Projet",
|
|
487
|
+
es: "El Título de mi Proyecto",
|
|
488
|
+
}),
|
|
489
|
+
|
|
490
|
+
description: t({
|
|
491
|
+
ja: "ワークフローを合理化し、生産性を向上させるために設計された革新的なプラットフォームをぜひご確認ください。",
|
|
492
|
+
en: "Discover our innovative platform designed to streamline your workflow and boost productivity.",
|
|
493
|
+
fr: "Découvrez notre plateforme innovante conçue pour simplifier votre flux de travail et booster votre productivité.",
|
|
494
|
+
es: "Descubra nuestra plataforma innovadora diseñada para simplificar su flujo de trabajo y aumentar su productividad.",
|
|
495
|
+
}),
|
|
496
|
+
|
|
497
|
+
keywords: t({
|
|
498
|
+
ja: ["イノベーション", "生産性", "ワークフロー", "SaaS"],
|
|
499
|
+
en: ["innovation", "productivity", "workflow", "SaaS"],
|
|
500
|
+
fr: ["innovation", "productivité", "flux de travail", "SaaS"],
|
|
501
|
+
es: ["innovación", "productividad", "flujo de trabajo", "SaaS"],
|
|
502
|
+
}),
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
module.exports = metadataContent;
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
```json fileName="src/app/metadata.content.json" contentDeclarationFormat="json"
|
|
510
|
+
{
|
|
511
|
+
"key": "metadata",
|
|
512
|
+
"content": {
|
|
513
|
+
"title": {
|
|
514
|
+
"nodeType": "translation",
|
|
515
|
+
"translation": {
|
|
516
|
+
"ja": "私のプロジェクトのタイトル",
|
|
517
|
+
"en": "My Project Title",
|
|
518
|
+
"fr": "Le Titre de mon Projet",
|
|
519
|
+
"es": "El Título de mi Proyecto"
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
"description": {
|
|
523
|
+
"nodeType": "translation",
|
|
524
|
+
"translation": {
|
|
525
|
+
"ja": "ワークフローを合理化し、生産性を向上させるよう設計された革新的なプラットフォームです。",
|
|
526
|
+
"en": "Discover our innovative platform designed to streamline your workflow and boost productivity.",
|
|
527
|
+
"ja": "ワークフローを簡素化し、生産性を向上させるために設計された革新的なプラットフォームをご覧ください。",
|
|
528
|
+
"fr": "Découvrez notre plateforme innovante conçue pour simplifier votre flux de travail et booster votre productivité.",
|
|
529
|
+
"es": "Descubra nuestra plataforma innovadora diseñada para simplificar su flujo de trabajo y aumentar su productividad."
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
"keywords": {
|
|
533
|
+
"nodeType": "translation",
|
|
534
|
+
"translation": {
|
|
535
|
+
"ja": ["イノベーション", "生産性", "ワークフロー", "SaaS"],
|
|
536
|
+
"en": ["innovation", "productivity", "workflow", "SaaS"],
|
|
537
|
+
"fr": ["innovation", "productivité", "flux de travail", "SaaS"],
|
|
538
|
+
"es": ["innovación", "productividad", "flujo de trabajo", "SaaS"]
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
```tsx fileName="src/app/page.content.ts" contentDeclarationFormat="typescript"
|
|
546
|
+
import { t, type Dictionary } from "intlayer";
|
|
547
|
+
|
|
548
|
+
const pageContent = {
|
|
549
|
+
key: "page",
|
|
550
|
+
content: {
|
|
551
|
+
getStarted: {
|
|
552
|
+
main: t({
|
|
553
|
+
ja: "編集して始めましょう",
|
|
554
|
+
en: "Get started by editing",
|
|
555
|
+
fr: "Commencez par éditer",
|
|
556
|
+
es: "Comience por editar",
|
|
557
|
+
}),
|
|
558
|
+
pageLink: "src/app/page.tsx",
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
} satisfies Dictionary;
|
|
562
|
+
|
|
563
|
+
export default pageContent;
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
```javascript fileName="src/app/page.content.mjs" contentDeclarationFormat="esm"
|
|
567
|
+
import { t } from "intlayer";
|
|
568
|
+
|
|
569
|
+
/** @type {import('intlayer').Dictionary} */
|
|
570
|
+
const pageContent = {
|
|
571
|
+
key: "page",
|
|
572
|
+
content: {
|
|
573
|
+
getStarted: {
|
|
574
|
+
main: t({
|
|
575
|
+
ja: "編集して始めましょう",
|
|
576
|
+
en: "Get started by editing",
|
|
577
|
+
fr: "Commencez par éditer",
|
|
578
|
+
es: "Comience por editar",
|
|
579
|
+
}),
|
|
580
|
+
pageLink: "src/app/page.tsx",
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
export default pageContent;
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
```javascript fileName="src/app/page.content.cjs" contentDeclarationFormat="commonjs"
|
|
589
|
+
const { t } = require("intlayer");
|
|
590
|
+
|
|
591
|
+
/** @type {import('intlayer').Dictionary} */
|
|
592
|
+
const pageContent = {
|
|
593
|
+
key: "page",
|
|
594
|
+
content: {
|
|
595
|
+
getStarted: {
|
|
596
|
+
main: t({
|
|
597
|
+
ja: "編集して始めましょう",
|
|
598
|
+
en: "Get started by editing",
|
|
599
|
+
fr: "Commencez par éditer",
|
|
600
|
+
es: "Comience por editar",
|
|
601
|
+
}),
|
|
602
|
+
pageLink: "src/app/page.tsx",
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
module.exports = pageContent;
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
```json fileName="src/app/page.content.json" contentDeclarationFormat="json"
|
|
611
|
+
{
|
|
612
|
+
"$schema": "https://intlayer.org/schema.json",
|
|
613
|
+
"key": "page",
|
|
614
|
+
"content": {
|
|
615
|
+
"getStarted": {
|
|
616
|
+
"nodeType": "translation",
|
|
617
|
+
"translation": {
|
|
618
|
+
"ja": "編集して始めましょう",
|
|
619
|
+
"en": "Get started by editing",
|
|
620
|
+
"fr": "Commencez par éditer",
|
|
621
|
+
"es": "Comience por editar"
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
"pageLink": "src/app/page.tsx"
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
> コンテンツの宣言は、アプリケーション内の任意の場所に定義できます。ただし、それらが `contentDir` ディレクトリ(デフォルトは `./src`)に含まれており、コンテンツ宣言ファイルの拡張子が一致している必要があります(デフォルト: `.content.{json,ts,tsx,js,jsx,mjs,mjx,cjs,cjx}`)。
|
|
630
|
+
|
|
631
|
+
> 詳細は、[コンテンツ宣言のドキュメント](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/dictionary/content_file.md) を参照してください。
|
|
632
|
+
|
|
633
|
+
### ステップ6: コード内でコンテンツを利用する
|
|
634
|
+
|
|
635
|
+
アプリケーション全体からコンテンツ辞書にアクセスします:
|
|
636
|
+
|
|
637
|
+
```tsx fileName="src/app/page.tsx" codeFormat="typescript"
|
|
638
|
+
import type { FC } from "react";
|
|
639
|
+
import { ClientComponentExample } from "@components/clientComponentExample/ClientComponentExample";
|
|
640
|
+
import { ServerComponentExample } from "@components/serverComponentExample/ServerComponentExample";
|
|
641
|
+
import {
|
|
642
|
+
IntlayerServerProvider,
|
|
643
|
+
useIntlayer,
|
|
644
|
+
getLocale,
|
|
645
|
+
} from "next-intlayer/server";
|
|
646
|
+
import { NextPage } from "next";
|
|
647
|
+
import { headers, cookies } from "next/headers";
|
|
648
|
+
|
|
649
|
+
const PageContent: FC = () => {
|
|
650
|
+
const content = useIntlayer("page");
|
|
651
|
+
|
|
652
|
+
return (
|
|
653
|
+
<>
|
|
654
|
+
<p>{content.getStarted.main}</p>
|
|
655
|
+
<code>{content.getStarted.pageLink}</code>
|
|
656
|
+
</>
|
|
657
|
+
);
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
const Page: NextPage = async () => {
|
|
661
|
+
// Next.js 15+ では headers と cookies を await してください
|
|
662
|
+
const headerList = await headers();
|
|
663
|
+
const cookieList = await cookies();
|
|
664
|
+
|
|
665
|
+
const locale = await getLocale({
|
|
666
|
+
// まず intlayer のクッキーを確認します(デフォルト: 'INTLAYER_LOCALE')
|
|
667
|
+
getCookie: (name) => cookieList.get(name)?.value,
|
|
668
|
+
|
|
669
|
+
// 次に intlayer ヘッダーを確認します(デフォルト: 'x-intlayer-locale')
|
|
670
|
+
// 最後に accept-language ヘッダーを確認します('accept-language')
|
|
671
|
+
getHeader: (name) => headerList.get(name),
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
return (
|
|
675
|
+
<IntlayerServerProvider locale={locale}>
|
|
676
|
+
<PageContent />
|
|
677
|
+
<ServerComponentExample />
|
|
678
|
+
<ClientComponentExample />
|
|
679
|
+
</IntlayerServerProvider>
|
|
680
|
+
);
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
export default Page;
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
```jsx fileName="src/app/page.mjx" codeFormat="esm"
|
|
687
|
+
import { ClientComponentExample } from "@components/clientComponentExample/ClientComponentExample";
|
|
688
|
+
import { ServerComponentExample } from "@components/serverComponentExample/ServerComponentExample";
|
|
689
|
+
import { IntlayerServerProvider, useIntlayer, getLocale } from "next-intlayer/server";
|
|
690
|
+
import { headers, cookies } from "next/headers";
|
|
691
|
+
import { NextPage } from "next";
|
|
692
|
+
|
|
693
|
+
const Page: NextPage = async () => {
|
|
694
|
+
const content = useIntlayer("page");
|
|
695
|
+
|
|
696
|
+
return (
|
|
697
|
+
<>
|
|
698
|
+
<p>{content.getStarted.main}</p>
|
|
699
|
+
<code>{content.getStarted.pageLink}</code>
|
|
700
|
+
</>
|
|
701
|
+
);
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
const Page = async () => {
|
|
705
|
+
|
|
706
|
+
// Next.js 15+ では headers と cookies を await します
|
|
707
|
+
const headerList = await headers();
|
|
708
|
+
const cookieList = await cookies();
|
|
709
|
+
|
|
710
|
+
const locale = await getLocale({
|
|
711
|
+
// まず intlayer のクッキーを確認します(デフォルト: 'INTLAYER_LOCALE')
|
|
712
|
+
getCookie: (name) => cookieList.get(name)?.value,
|
|
713
|
+
|
|
714
|
+
// 次に intlayer のヘッダーを確認します(デフォルト: 'x-intlayer-locale')
|
|
715
|
+
// 最後に accept-language ヘッダーを確認します('accept-language')
|
|
716
|
+
getHeader: (name) => headerList.get(name),
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
return (
|
|
720
|
+
<IntlayerServerProvider locale={locale}>
|
|
721
|
+
<PageContent />
|
|
722
|
+
<ServerComponentExample />
|
|
723
|
+
<ClientComponentExample />
|
|
724
|
+
</IntlayerServerProvider>
|
|
725
|
+
);
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
export default Page;
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
````jsx fileName="src/app/page.csx" codeFormat="commonjs"
|
|
732
|
+
import { ClientComponentExample } from "@components/clientComponentExample/ClientComponentExample";
|
|
733
|
+
import { ServerComponentExample } from "@components/serverComponentExample/ServerComponentExample";
|
|
734
|
+
import { IntlayerServerProvider, useIntlayer, getLocale } from "next-intlayer/server";
|
|
735
|
+
import { NextPage } from "next";
|
|
736
|
+
import { headers, cookies } from "next/headers";
|
|
737
|
+
|
|
738
|
+
const Page: NextPage = async () => {
|
|
739
|
+
const content = useIntlayer("page");
|
|
740
|
+
|
|
741
|
+
return (
|
|
742
|
+
<>
|
|
743
|
+
<p>{content.getStarted.main}</p>
|
|
744
|
+
<code>{content.getStarted.pageLink}</code>
|
|
745
|
+
</>
|
|
746
|
+
);
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
const Page: NextPage = async () => {
|
|
750
|
+
// Next.js 15+ では headers と cookies を await する必要があります
|
|
751
|
+
const headerList = await headers();
|
|
752
|
+
const cookieList = await cookies();
|
|
753
|
+
|
|
754
|
+
const locale = await getLocale({
|
|
755
|
+
// まず intlayer の cookie をチェックします(デフォルト: 'INTLAYER_LOCALE')
|
|
756
|
+
getCookie: (name) => cookieList.get(name)?.value,
|
|
757
|
+
|
|
758
|
+
// 次に intlayer のヘッダーをチェックします(デフォルト: 'x-intlayer-locale')
|
|
759
|
+
// 最後に accept-language ヘッダー('accept-language')をチェックします
|
|
760
|
+
getHeader: (name) => headerList.get(name),
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
return (
|
|
764
|
+
<IntlayerServerProvider locale={locale}>
|
|
765
|
+
<PageContent />
|
|
766
|
+
<ServerComponentExample />
|
|
767
|
+
<ClientComponentExample />
|
|
768
|
+
</IntlayerServerProvider>
|
|
769
|
+
);
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
- **`IntlayerClientProvider`** はクライアント側コンポーネントにロケールを提供するために使用されます。レイアウトを含む任意の親コンポーネントに配置できます。ただし、Next.js がページ間でレイアウトコードを共有するため、レイアウトに配置することが推奨されます。レイアウトで `IntlayerClientProvider` を使用することで、各ページごとに再初期化されることを避け、パフォーマンスを向上させ、アプリケーション全体で一貫したローカリゼーションコンテキストを維持できます。
|
|
773
|
+
- **`IntlayerServerProvider`** はサーバー側の子にロケールを提供するために使用されます。レイアウトに設定することはできません。
|
|
774
|
+
|
|
775
|
+
> Layout と page は共通の server context を共有できません。server context システムはリクエスト単位のデータストア([React の cache](https://react.dev/reference/react/cache) 機構)に基づいており、アプリケーションの異なるセグメントごとに各 "context" が再作成されます。provider を共有 layout に配置するとこの分離が壊れ、server component に対する server context 値の正しい伝播が妨げられます。
|
|
776
|
+
|
|
777
|
+
```tsx {4,7} fileName="src/components/clientComponentExample/ClientComponentExample.tsx" codeFormat="typescript"
|
|
778
|
+
"use client";
|
|
779
|
+
|
|
780
|
+
import type { FC } from "react";
|
|
781
|
+
import { useIntlayer } from "next-intlayer";
|
|
782
|
+
|
|
783
|
+
export const ClientComponentExample: FC = () => {
|
|
784
|
+
const content = useIntlayer("client-component-example"); // 関連コンテンツの宣言を作成します
|
|
785
|
+
|
|
786
|
+
return (
|
|
787
|
+
<div>
|
|
788
|
+
<h2>{content.title}</h2>
|
|
789
|
+
<p>{content.content}</p>
|
|
790
|
+
</div>
|
|
791
|
+
);
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
```jsx {3,6} fileName="src/components/clientComponentExample/ClientComponentExample.mjx" codeFormat="esm"
|
|
795
|
+
"use client";
|
|
796
|
+
|
|
797
|
+
import { useIntlayer } from "next-intlayer";
|
|
798
|
+
|
|
799
|
+
const ClientComponentExample = () => {
|
|
800
|
+
const content = useIntlayer("client-component-example"); // 関連コンテンツの宣言を作成
|
|
801
|
+
|
|
802
|
+
return (
|
|
803
|
+
<div>
|
|
804
|
+
<h2>{content.title}</h2>
|
|
805
|
+
<p>{content.content}</p>
|
|
806
|
+
</div>
|
|
807
|
+
);
|
|
808
|
+
};
|
|
809
|
+
````
|
|
810
|
+
|
|
811
|
+
```jsx {3,6} fileName="src/components/clientComponentExample/ClientComponentExample.csx" codeFormat="commonjs"
|
|
812
|
+
"use client";
|
|
813
|
+
|
|
814
|
+
const { useIntlayer } = require("next-intlayer");
|
|
815
|
+
|
|
816
|
+
const ClientComponentExample = () => {
|
|
817
|
+
const content = useIntlayer("client-component-example"); // 関連コンテンツの宣言を作成
|
|
818
|
+
|
|
819
|
+
return (
|
|
820
|
+
<div>
|
|
821
|
+
<h2>{content.title}</h2>
|
|
822
|
+
<p>{content.content}</p>
|
|
823
|
+
</div>
|
|
824
|
+
);
|
|
825
|
+
};
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
```tsx {2} fileName="src/components/serverComponentExample/ServerComponentExample.tsx" codeFormat="typescript"
|
|
829
|
+
import type { FC } from "react";
|
|
830
|
+
import { useIntlayer } from "next-intlayer/server";
|
|
831
|
+
|
|
832
|
+
export const ServerComponentExample: FC = () => {
|
|
833
|
+
const content = useIntlayer("server-component-example"); // 関連コンテンツの宣言を作成
|
|
834
|
+
|
|
835
|
+
return (
|
|
836
|
+
<div>
|
|
837
|
+
<h2>{content.title}</h2>
|
|
838
|
+
<p>{content.content}</p>
|
|
839
|
+
</div>
|
|
840
|
+
);
|
|
841
|
+
};
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
```jsx {1} fileName="src/components/serverComponentExample/ServerComponentExample.mjx" codeFormat="esm"
|
|
845
|
+
import { useIntlayer } from "next-intlayer/server";
|
|
846
|
+
|
|
847
|
+
const ServerComponentExample = () => {
|
|
848
|
+
const content = useIntlayer("server-component-example"); // 関連コンテンツの宣言を作成
|
|
849
|
+
|
|
850
|
+
return (
|
|
851
|
+
<div>
|
|
852
|
+
<h2>{content.title}</h2>
|
|
853
|
+
<p>{content.content}</p>
|
|
854
|
+
</div>
|
|
855
|
+
);
|
|
856
|
+
};
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
```jsx {1} fileName="src/components/serverComponentExample/ServerComponentExample.csx" codeFormat="commonjs"
|
|
860
|
+
const { useIntlayer } = require("next-intlayer/server");
|
|
861
|
+
|
|
862
|
+
const ServerComponentExample = () => {
|
|
863
|
+
const content = useIntlayer("server-component-example"); // 関連コンテンツの宣言を作成
|
|
864
|
+
|
|
865
|
+
return (
|
|
866
|
+
<div>
|
|
867
|
+
<h2>{content.title}</h2>
|
|
868
|
+
<p>{content.content}</p>
|
|
869
|
+
</div>
|
|
870
|
+
);
|
|
871
|
+
};
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
> `alt`、`title`、`href`、`aria-label` などの `string` 属性でコンテンツを使用する場合は、関数の値を呼び出す必要があります。例えば:
|
|
875
|
+
|
|
876
|
+
> ```jsx
|
|
877
|
+
> <img src={content.image.src.value} alt={content.image.value} />
|
|
878
|
+
> ```
|
|
879
|
+
|
|
880
|
+
> `useIntlayer` フックの詳細は[ドキュメント](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/packages/next-intlayer/useIntlayer.md)を参照してください。
|
|
881
|
+
|
|
882
|
+
### (オプション)ステップ7: ロケール検出のためにプロキシを設定
|
|
883
|
+
|
|
884
|
+
ユーザーの優先ロケールを検出するようにプロキシを設定します:
|
|
885
|
+
|
|
886
|
+
```typescript fileName="src/proxy.ts" codeFormat="typescript"
|
|
887
|
+
export { intlayerProxy as proxy } from "next-intlayer/proxy";
|
|
888
|
+
|
|
889
|
+
export const config = {
|
|
890
|
+
matcher:
|
|
891
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
892
|
+
};
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
```javascript fileName="src/proxy.mjs" codeFormat="esm"
|
|
896
|
+
export { intlayerProxy as proxy } from "next-intlayer/proxy";
|
|
897
|
+
|
|
898
|
+
export const config = {
|
|
899
|
+
matcher:
|
|
900
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
901
|
+
};
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
```javascript fileName="src/proxy.cjs" codeFormat="commonjs"
|
|
905
|
+
const { intlayerProxy } = require("next-intlayer/proxy");
|
|
906
|
+
|
|
907
|
+
const config = {
|
|
908
|
+
matcher:
|
|
909
|
+
"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
module.exports = { proxy: intlayerProxy, config };
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
> `intlayerProxy` はユーザーの優先ロケールを検出し、[設定](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/configuration.md)に記載された適切な URL へリダイレクトするために使用されます。さらに、ユーザーの優先ロケールをクッキーに保存することも可能にします。
|
|
916
|
+
|
|
917
|
+
> 複数のプロキシを連結する必要がある場合(例えば、認証やカスタムプロキシと `intlayerProxy` を組み合わせる場合)、Intlayer は `multipleProxies` というヘルパーを提供しています。
|
|
918
|
+
|
|
919
|
+
```ts
|
|
920
|
+
import { multipleProxies, intlayerProxy } from "next-intlayer/proxy";
|
|
921
|
+
import { customProxy } from "@utils/customProxy";
|
|
922
|
+
|
|
923
|
+
export const proxy = multipleProxies([intlayerProxy, customProxy]);
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
### (オプション)ステップ 8: コンテンツの言語を変更する
|
|
927
|
+
|
|
928
|
+
Next.jsでコンテンツの言語を切り替えるには、推奨される方法は`Link`コンポーネントを使用してユーザーを適切なローカライズされたページにリダイレクトすることです。`Link`コンポーネントはページのプリフェッチを可能にし、フルページのリロードを回避するのに役立ちます。
|
|
929
|
+
|
|
930
|
+
```tsx fileName="src/components/localeSwitcher/LocaleSwitcher.tsx" codeFormat="typescript"
|
|
931
|
+
"use client";
|
|
932
|
+
|
|
933
|
+
import type { FC } from "react";
|
|
934
|
+
import { Locales, getHTMLTextDir, getLocaleName } from "intlayer";
|
|
935
|
+
import { useLocale } from "next-intlayer";
|
|
936
|
+
|
|
937
|
+
export const LocaleSwitcher: FC = () => {
|
|
938
|
+
const { locale, availableLocales, setLocale } = useLocale({
|
|
939
|
+
onChange: () => window.location.reload(),
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
return (
|
|
943
|
+
<div>
|
|
944
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
945
|
+
<div id="localePopover" popover="auto">
|
|
946
|
+
{availableLocales.map((localeItem) => (
|
|
947
|
+
<button
|
|
948
|
+
key={localeItem}
|
|
949
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
950
|
+
onClick={() => setLocale(localeItem)}
|
|
951
|
+
>
|
|
952
|
+
<span>
|
|
953
|
+
{/* ロケール - 例: FR */}
|
|
954
|
+
{localeItem}
|
|
955
|
+
</span>
|
|
956
|
+
<span>
|
|
957
|
+
{/* 自身のロケールでの言語名 - 例: Français */}
|
|
958
|
+
{getLocaleName(localeItem, locale)}
|
|
959
|
+
</span>
|
|
960
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
961
|
+
{/* 現在のロケールでの言語名 - 例: 現在のロケールが Locales.SPANISH の場合: Francés */}
|
|
962
|
+
{getLocaleName(localeItem)}
|
|
963
|
+
</span>
|
|
964
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
965
|
+
{availableLocales.map((localeItem) => (
|
|
966
|
+
<button
|
|
967
|
+
key={localeItem}
|
|
968
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
969
|
+
onClick={() => setLocale(localeItem)}
|
|
970
|
+
>
|
|
971
|
+
<span>
|
|
972
|
+
{/* ロケール - 例: FR */}
|
|
973
|
+
{localeItem}
|
|
974
|
+
</span>
|
|
975
|
+
<span>
|
|
976
|
+
{/* 自身のロケールでの言語名 - 例: Français */}
|
|
977
|
+
{getLocaleName(localeItem, locale)}
|
|
978
|
+
</span>
|
|
979
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
980
|
+
{/* 現在のロケールでの言語名 - 例: 現在のロケールが Locales.SPANISH の場合: Francés */}
|
|
981
|
+
{getLocaleName(localeItem)}
|
|
982
|
+
</span>
|
|
983
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
984
|
+
{/* 英語での言語名 — 例: French */}
|
|
985
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
986
|
+
</span>
|
|
987
|
+
</button>
|
|
988
|
+
))}
|
|
989
|
+
</div>
|
|
990
|
+
</div>
|
|
991
|
+
);
|
|
992
|
+
};
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
```jsx fileName="src/components/localeSwitcher/LocaleSwitcher.msx" codeFormat="esm"
|
|
996
|
+
"use client";
|
|
997
|
+
|
|
998
|
+
import { Locales, getHTMLTextDir, getLocaleName } from "intlayer";
|
|
999
|
+
import { useLocale } from "next-intlayer";
|
|
1000
|
+
|
|
1001
|
+
export const LocaleSwitcher = () => {
|
|
1002
|
+
const { locale, availableLocales, setLocale } = useLocale({
|
|
1003
|
+
onChange: () => window.location.reload(),
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
return (
|
|
1007
|
+
<div>
|
|
1008
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1009
|
+
<div id="localePopover" popover="auto">
|
|
1010
|
+
{availableLocales.map((localeItem) => (
|
|
1011
|
+
<button
|
|
1012
|
+
key={localeItem}
|
|
1013
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1014
|
+
onClick={() => setLocale(localeItem)}
|
|
1015
|
+
>
|
|
1016
|
+
<span>
|
|
1017
|
+
{/* ロケール - 例: FR */}
|
|
1018
|
+
{localeItem}
|
|
1019
|
+
</span>
|
|
1020
|
+
<span>
|
|
1021
|
+
{/* 該当ロケールでの言語名 - 例: Français */}
|
|
1022
|
+
{getLocaleName(localeItem, locale)}
|
|
1023
|
+
</span>
|
|
1024
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1025
|
+
{/* 現在のロケールで表示した言語名 - 例: ロケールが Locales.SPANISH に設定されている場合: Francés */}
|
|
1026
|
+
{getLocaleName(localeItem)}
|
|
1027
|
+
</span>
|
|
1028
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1029
|
+
{/* 英語での言語名 - 例: French */}
|
|
1030
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1031
|
+
</span>
|
|
1032
|
+
</button>
|
|
1033
|
+
))}
|
|
1034
|
+
</div>
|
|
1035
|
+
</div>
|
|
1036
|
+
);
|
|
1037
|
+
};
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
```jsx fileName="src/components/localeSwitcher/LocaleSwitcher.csx" codeFormat="commonjs"
|
|
1041
|
+
"use client";
|
|
1042
|
+
|
|
1043
|
+
const { Locales, getHTMLTextDir, getLocaleName } = require("intlayer");
|
|
1044
|
+
const { useLocale } = require("next-intlayer");
|
|
1045
|
+
|
|
1046
|
+
export const LocaleSwitcher = () => {
|
|
1047
|
+
const path
|
|
1048
|
+
const { locale availableLocales, setLocale } = useLocale({
|
|
1049
|
+
onChange: ()=> window.location.reload(),
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
return (
|
|
1053
|
+
<div>
|
|
1054
|
+
<button popoverTarget="localePopover">{getLocaleName(locale)}</button>
|
|
1055
|
+
<div id="localePopover" popover="auto">
|
|
1056
|
+
{availableLocales.map((localeItem) => (
|
|
1057
|
+
<button
|
|
1058
|
+
key={localeItem}
|
|
1059
|
+
aria-current={locale === localeItem ? "page" : undefined}
|
|
1060
|
+
onClick={() => setLocale(localeItem)}
|
|
1061
|
+
>
|
|
1062
|
+
<span>
|
|
1063
|
+
{/* ロケール(例: FR) */}
|
|
1064
|
+
{localeItem}
|
|
1065
|
+
</span>
|
|
1066
|
+
<span>
|
|
1067
|
+
{/* そのロケールでの言語名(例: Français) */}
|
|
1068
|
+
{getLocaleName(localeItem, locale)}
|
|
1069
|
+
</span>
|
|
1070
|
+
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1071
|
+
{/* 現在のロケールでの言語名(例: 現在のロケールが Locales.SPANISH の場合: Francés) */}
|
|
1072
|
+
{getLocaleName(localeItem)}
|
|
1073
|
+
</span>
|
|
1074
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1075
|
+
{/* 英語での言語名(例: French) */}
|
|
1076
|
+
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1077
|
+
</span>
|
|
1078
|
+
</button>
|
|
1079
|
+
))}
|
|
1080
|
+
</div>
|
|
1081
|
+
</div>
|
|
1082
|
+
);
|
|
1083
|
+
};
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
> 代替方法として、`useLocale` フックが提供する `setLocale` 関数を使用できます。この関数はページのプリフェッチを許可しません。詳細は [`useLocale` フックのドキュメント](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/packages/next-intlayer/useLocale.md) を参照してください。
|
|
1087
|
+
|
|
1088
|
+
> ドキュメント参照:
|
|
1089
|
+
>
|
|
1090
|
+
> - [`useLocale` フック](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/packages/next-intlayer/useLocale.md)
|
|
1091
|
+
> - [`getLocaleName` フック](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/packages/intlayer/getLocaleName.md)
|
|
1092
|
+
> - [`getLocalizedUrl` フック](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/packages/intlayer/getLocalizedUrl.md)
|
|
1093
|
+
|
|
1094
|
+
- [`getHTMLTextDir` フック](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/packages/intlayer/getHTMLTextDir.md)
|
|
1095
|
+
- [`hrefLang` 属性](https://developers.google.com/search/docs/specialty/international/localized-versions?hl=fr)
|
|
1096
|
+
- [`lang` 属性](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang)
|
|
1097
|
+
- [`dir` 属性](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir)
|
|
1098
|
+
- [`aria-current` 属性](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)
|
|
1099
|
+
|
|
1100
|
+
### (任意)ステップ 9: Server Actions で現在のロケールを取得する
|
|
1101
|
+
|
|
1102
|
+
Server Action の内部でアクティブなロケールが必要な場合(例: メールのローカライズやロケール対応のロジックを実行する場合)、`next-intlayer/server` から `getLocale` を呼び出してください:
|
|
1103
|
+
|
|
1104
|
+
```tsx fileName="src/app/actions/getLocale.ts" codeFormat="typescript"
|
|
1105
|
+
"use server";
|
|
1106
|
+
|
|
1107
|
+
import { getLocale } from "next-intlayer/server";
|
|
1108
|
+
|
|
1109
|
+
export const myServerAction = async () => {
|
|
1110
|
+
const locale = await getLocale();
|
|
1111
|
+
|
|
1112
|
+
// ロケールを使って処理を行います
|
|
1113
|
+
};
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
> `getLocale` 関数はユーザーのロケールを決定するためにカスケード戦略を採用します:
|
|
1117
|
+
>
|
|
1118
|
+
> 1. まず、プロキシによって設定されている可能性のあるロケール値についてリクエストヘッダーを確認します
|
|
1119
|
+
> 2. ヘッダーにロケールが見つからない場合、クッキーに保存されたロケールを探します
|
|
1120
|
+
> 3. クッキーが見つからない場合、ブラウザ設定からユーザーの優先言語を検出しようとします
|
|
1121
|
+
> 4. 最後の手段として、アプリケーションで設定されたデフォルトロケールにフォールバックします
|
|
1122
|
+
>
|
|
1123
|
+
> これにより、利用可能なコンテキストに基づいて最も適切なロケールが選択されます。
|
|
1124
|
+
|
|
1125
|
+
### (任意)ステップ10:バンドルサイズを最適化する
|
|
1126
|
+
|
|
1127
|
+
`next-intlayer` を使用すると、辞書はデフォルトで各ページのバンドルに含まれます。バンドルサイズを最適化するために、Intlayer はマクロを使用して `useIntlayer` の呼び出しをインテリジェントに置き換えるオプションの SWC プラグインを提供しています。これにより、辞書は実際にそれらを使用するページのバンドルにのみ含まれるようになります。
|
|
1128
|
+
|
|
1129
|
+
この最適化を有効にするには、`@intlayer/swc` パッケージをインストールしてください。インストール後、`next-intlayer` は自動的にプラグインを検出して使用します:
|
|
1130
|
+
|
|
1131
|
+
```bash packageManager="npm"
|
|
1132
|
+
npm install @intlayer/swc --save-dev
|
|
1133
|
+
npx intlayer init
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
```bash packageManager="pnpm"
|
|
1137
|
+
pnpm add @intlayer/swc --save-dev
|
|
1138
|
+
pnpm intlayer init
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
```bash packageManager="yarn"
|
|
1142
|
+
yarn add @intlayer/swc --save-dev
|
|
1143
|
+
yarn intlayer init
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
```bash packageManager="bun"
|
|
1147
|
+
bun add @intlayer/swc --dev
|
|
1148
|
+
bunx intlayer init
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
> 注意: この最適化は Next.js 13 以降でのみ利用可能です。
|
|
1152
|
+
|
|
1153
|
+
> 注意: SWCプラグインは Next.js 上でまだ実験的なため、このパッケージはデフォルトでインストールされません。将来的に変更される可能性があります。
|
|
1154
|
+
|
|
1155
|
+
> 注意: オプションを `importMode: 'dynamic'` または `importMode: 'live'` に設定すると、Suspense に依存するため、`useIntlayer` の呼び出しを `Suspense` 境界でラップする必要があります。つまり、Page / Layout コンポーネントのトップレベルで `useIntlayer` を直接使用することはできません。
|
|
1156
|
+
|
|
1157
|
+
### Turbopackで辞書の変更を監視する
|
|
1158
|
+
|
|
1159
|
+
Turbopackを開発サーバーとして`next dev`コマンドで使用している場合、辞書の変更はデフォルトでは自動的に検出されません。
|
|
1160
|
+
|
|
1161
|
+
この制限は、Turbopackがコンテンツファイルの変更を監視するためのwebpackプラグインを並列で実行できないために発生します。回避するには、`intlayer watch`コマンドを使用して、開発サーバーとIntlayerのビルドウォッチャーを同時に実行する必要があります。
|
|
1162
|
+
|
|
1163
|
+
```json5 fileName="package.json"
|
|
1164
|
+
{
|
|
1165
|
+
// ... 既存の package.json 設定
|
|
1166
|
+
"scripts": {
|
|
1167
|
+
// ... 既存の scripts 設定
|
|
1168
|
+
"dev": "intlayer watch --with 'next dev'",
|
|
1169
|
+
},
|
|
1170
|
+
}
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
> next-intlayer@<=6.x.x を使用している場合、Next.js 16 アプリケーションを Turbopack と正しく動作させるために `--turbopack` フラグを維持する必要があります。この制約を回避するには next-intlayer@>=7.x.x の使用を推奨します。
|
|
1174
|
+
|
|
1175
|
+
### TypeScript の設定
|
|
1176
|
+
|
|
1177
|
+
Intlayer は TypeScript の利点を活かすためにモジュール拡張 (module augmentation) を使用し、コードベースをより堅牢にします。
|
|
1178
|
+
|
|
1179
|
+

|
|
1180
|
+
|
|
1181
|
+

|
|
1182
|
+
|
|
1183
|
+
TypeScript の設定に自動生成された型が含まれていることを確認してください。
|
|
1184
|
+
|
|
1185
|
+
````json5 fileName="tsconfig.json"
|
|
1186
|
+
{
|
|
1187
|
+
// ... 既存の TypeScript 設定
|
|
1188
|
+
"include": [
|
|
1189
|
+
// ... 既存の TypeScript 設定
|
|
1190
|
+
".intlayer/**/*.ts", // 自動生成された型を含める
|
|
1191
|
+
],
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
### Git 設定
|
|
1195
|
+
|
|
1196
|
+
Intlayer によって生成されたファイルは無視することを推奨します。これにより、これらのファイルを Git リポジトリにコミットすることを避けられます。
|
|
1197
|
+
|
|
1198
|
+
これを行うには、次の内容を `.gitignore` ファイルに追加してください:
|
|
1199
|
+
|
|
1200
|
+
```plaintext fileName=".gitignore"
|
|
1201
|
+
# Intlayer によって生成されたファイルを無視する
|
|
1202
|
+
.intlayer
|
|
1203
|
+
````
|
|
1204
|
+
|
|
1205
|
+
### VS Code 拡張機能
|
|
1206
|
+
|
|
1207
|
+
Intlayer の開発体験を向上させるために、公式の **Intlayer VS Code Extension** をインストールできます。
|
|
1208
|
+
|
|
1209
|
+
[VS Code Marketplace からインストール](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1210
|
+
|
|
1211
|
+
この拡張機能は次の機能を提供します:
|
|
1212
|
+
|
|
1213
|
+
- **Autocompletion**: 翻訳キーの補完。
|
|
1214
|
+
- **リアルタイムのエラーチェック**: 翻訳が欠落している箇所の検出。
|
|
1215
|
+
- **翻訳されたコンテンツのインラインプレビュー**
|
|
1216
|
+
- **翻訳を簡単に作成・更新するためのクイックアクション**
|
|
1217
|
+
|
|
1218
|
+
拡張機能の使用方法の詳細については、[Intlayer VS Code 拡張機能のドキュメント](https://intlayer.org/doc/vs-code-extension) を参照してください。
|
|
1219
|
+
|
|
1220
|
+
### さらに進む
|
|
1221
|
+
|
|
1222
|
+
さらに進むには、[ビジュアルエディタ](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/intlayer_visual_editor.md) を実装するか、[CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/intlayer_CMS.md) を使用してコンテンツを外部化できます。
|