@intlayer/docs 8.5.2 → 8.6.1
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/docs/ar/intlayer_with_react_router_v7.md +2 -4
- package/docs/ar/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/ar/intlayer_with_svelte_kit.md +2 -2
- package/docs/ar/intlayer_with_tanstack+solid.md +0 -17
- package/docs/ar/intlayer_with_tanstack.md +0 -8
- package/docs/ar/intlayer_with_vite+lit.md +0 -1
- package/docs/ar/intlayer_with_vite+solid.md +0 -1
- package/docs/ar/intlayer_with_vite+svelte.md +1 -1
- package/docs/bn/intlayer_with_vite+lit.md +0 -1
- package/docs/cs/intlayer_with_vite+lit.md +0 -1
- package/docs/de/intlayer_with_react_router_v7.md +2 -4
- package/docs/de/intlayer_with_react_router_v7_fs_routes.md +1 -2
- package/docs/de/intlayer_with_svelte_kit.md +2 -2
- package/docs/de/intlayer_with_tanstack+solid.md +78 -21
- package/docs/de/intlayer_with_tanstack.md +96 -28
- package/docs/de/intlayer_with_vite+lit.md +0 -1
- package/docs/de/intlayer_with_vite+solid.md +0 -1
- package/docs/de/intlayer_with_vite+svelte.md +1 -1
- package/docs/en/intlayer_with_react_router_v7.md +1 -2
- package/docs/en/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/en/intlayer_with_svelte_kit.md +2 -2
- package/docs/en/intlayer_with_tanstack+solid.md +79 -19
- package/docs/en/intlayer_with_tanstack.md +81 -10
- package/docs/en/intlayer_with_vite+lit.md +0 -1
- package/docs/en/intlayer_with_vite+solid.md +0 -1
- package/docs/en/intlayer_with_vite+svelte.md +5 -6
- package/docs/en-GB/intlayer_with_react_router_v7.md +2 -4
- package/docs/en-GB/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/en-GB/intlayer_with_svelte_kit.md +2 -2
- package/docs/en-GB/intlayer_with_tanstack+solid.md +71 -17
- package/docs/en-GB/intlayer_with_tanstack.md +74 -9
- package/docs/en-GB/intlayer_with_vite+lit.md +0 -1
- package/docs/en-GB/intlayer_with_vite+solid.md +0 -1
- package/docs/en-GB/intlayer_with_vite+svelte.md +1 -1
- package/docs/es/intlayer_with_react_router_v7.md +2 -4
- package/docs/es/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/es/intlayer_with_svelte_kit.md +2 -2
- package/docs/es/intlayer_with_tanstack+solid.md +78 -21
- package/docs/es/intlayer_with_tanstack.md +96 -28
- package/docs/es/intlayer_with_vite+lit.md +0 -1
- package/docs/es/intlayer_with_vite+solid.md +0 -1
- package/docs/es/intlayer_with_vite+svelte.md +1 -1
- package/docs/fr/intlayer_with_react_router_v7.md +2 -4
- package/docs/fr/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/fr/intlayer_with_svelte_kit.md +3 -3
- package/docs/fr/intlayer_with_tanstack+solid.md +78 -21
- package/docs/fr/intlayer_with_tanstack.md +96 -28
- package/docs/fr/intlayer_with_vite+lit.md +0 -1
- package/docs/fr/intlayer_with_vite+solid.md +0 -1
- package/docs/fr/intlayer_with_vite+svelte.md +1 -1
- package/docs/hi/intlayer_with_react_router_v7.md +2 -4
- package/docs/hi/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/hi/intlayer_with_svelte_kit.md +2 -2
- package/docs/hi/intlayer_with_tanstack+solid.md +0 -17
- package/docs/hi/intlayer_with_tanstack.md +0 -8
- package/docs/hi/intlayer_with_vite+lit.md +0 -1
- package/docs/hi/intlayer_with_vite+solid.md +0 -1
- package/docs/hi/intlayer_with_vite+svelte.md +1 -1
- package/docs/id/intlayer_with_react_router_v7.md +2 -4
- package/docs/id/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/id/intlayer_with_svelte_kit.md +2 -2
- package/docs/id/intlayer_with_tanstack+solid.md +0 -17
- package/docs/id/intlayer_with_tanstack.md +0 -8
- package/docs/id/intlayer_with_vite+lit.md +0 -1
- package/docs/id/intlayer_with_vite+solid.md +0 -1
- package/docs/id/intlayer_with_vite+svelte.md +1 -1
- package/docs/it/intlayer_with_react_router_v7.md +2 -4
- package/docs/it/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/it/intlayer_with_svelte_kit.md +3 -3
- package/docs/it/intlayer_with_tanstack+solid.md +76 -19
- package/docs/it/intlayer_with_tanstack.md +96 -30
- package/docs/it/intlayer_with_vite+lit.md +0 -1
- package/docs/it/intlayer_with_vite+solid.md +0 -1
- package/docs/it/intlayer_with_vite+svelte.md +1 -1
- package/docs/ja/intlayer_with_react_router_v7.md +1 -2
- package/docs/ja/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/ja/intlayer_with_tanstack+solid.md +81 -22
- package/docs/ja/intlayer_with_tanstack.md +779 -93
- package/docs/ja/intlayer_with_vite+lit.md +0 -1
- package/docs/ja/intlayer_with_vite+solid.md +0 -1
- package/docs/ja/intlayer_with_vite+svelte.md +1 -1
- package/docs/ko/intlayer_with_react_router_v7.md +2 -4
- package/docs/ko/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/ko/intlayer_with_svelte_kit.md +2 -2
- package/docs/ko/intlayer_with_tanstack+solid.md +79 -22
- package/docs/ko/intlayer_with_tanstack.md +95 -101
- package/docs/ko/intlayer_with_vite+lit.md +0 -1
- package/docs/ko/intlayer_with_vite+solid.md +0 -1
- package/docs/ko/intlayer_with_vite+svelte.md +1 -1
- package/docs/nl/intlayer_with_vite+lit.md +0 -1
- package/docs/pl/intlayer_with_react_router_v7.md +2 -4
- package/docs/pl/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/pl/intlayer_with_svelte_kit.md +2 -2
- package/docs/pl/intlayer_with_tanstack+solid.md +0 -17
- package/docs/pl/intlayer_with_tanstack.md +0 -8
- package/docs/pl/intlayer_with_vite+lit.md +0 -1
- package/docs/pl/intlayer_with_vite+solid.md +0 -1
- package/docs/pl/intlayer_with_vite+svelte.md +1 -1
- package/docs/pt/intlayer_with_react_router_v7.md +1 -2
- package/docs/pt/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/pt/intlayer_with_svelte_kit.md +3 -3
- package/docs/pt/intlayer_with_tanstack+solid.md +76 -19
- package/docs/pt/intlayer_with_tanstack.md +96 -30
- package/docs/pt/intlayer_with_vite+lit.md +0 -1
- package/docs/pt/intlayer_with_vite+solid.md +0 -1
- package/docs/pt/intlayer_with_vite+svelte.md +1 -1
- package/docs/ru/intlayer_with_react_router_v7.md +2 -4
- package/docs/ru/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/ru/intlayer_with_svelte_kit.md +2 -2
- package/docs/ru/intlayer_with_tanstack+solid.md +78 -21
- package/docs/ru/intlayer_with_tanstack.md +99 -33
- package/docs/ru/intlayer_with_vite+lit.md +0 -1
- package/docs/ru/intlayer_with_vite+solid.md +0 -1
- package/docs/ru/intlayer_with_vite+svelte.md +1 -1
- package/docs/tr/intlayer_with_react_router_v7.md +2 -4
- package/docs/tr/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/tr/intlayer_with_svelte_kit.md +2 -2
- package/docs/tr/intlayer_with_tanstack+solid.md +0 -17
- package/docs/tr/intlayer_with_tanstack.md +0 -8
- package/docs/tr/intlayer_with_vite+lit.md +0 -1
- package/docs/tr/intlayer_with_vite+solid.md +0 -1
- package/docs/tr/intlayer_with_vite+svelte.md +1 -1
- package/docs/uk/intlayer_with_react_router_v7.md +1 -2
- package/docs/uk/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/uk/intlayer_with_svelte_kit.md +2 -2
- package/docs/uk/intlayer_with_tanstack+solid.md +0 -17
- package/docs/uk/intlayer_with_tanstack.md +0 -8
- package/docs/uk/intlayer_with_vite+lit.md +0 -1
- package/docs/uk/intlayer_with_vite+solid.md +0 -1
- package/docs/uk/intlayer_with_vite+svelte.md +1 -1
- package/docs/ur/intlayer_with_vite+lit.md +0 -1
- package/docs/vi/intlayer_with_react_router_v7.md +2 -4
- package/docs/vi/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/vi/intlayer_with_svelte_kit.md +2 -2
- package/docs/vi/intlayer_with_tanstack+solid.md +0 -17
- package/docs/vi/intlayer_with_tanstack.md +0 -8
- package/docs/vi/intlayer_with_vite+lit.md +0 -1
- package/docs/vi/intlayer_with_vite+solid.md +0 -1
- package/docs/vi/intlayer_with_vite+svelte.md +1 -1
- package/docs/zh/intlayer_with_react_router_v7.md +2 -4
- package/docs/zh/intlayer_with_react_router_v7_fs_routes.md +2 -4
- package/docs/zh/intlayer_with_svelte_kit.md +2 -2
- package/docs/zh/intlayer_with_tanstack+solid.md +80 -23
- package/docs/zh/intlayer_with_tanstack.md +96 -104
- package/docs/zh/intlayer_with_vite+lit.md +0 -1
- package/docs/zh/intlayer_with_vite+solid.md +0 -1
- package/docs/zh/intlayer_with_vite+svelte.md +1 -1
- package/package.json +8 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2025-09-09
|
|
3
|
-
updatedAt: 2026-03-
|
|
3
|
+
updatedAt: 2026-03-29
|
|
4
4
|
title: Tanstack Start i18n - Tanstack Startアプリの翻訳方法 2026
|
|
5
5
|
description: Intlayerを使用してTanStack Startアプリケーションに国際化(i18n)を追加する方法を学びます。ロケール対応のルーティングでアプリを多言語化するための包括的なガイドに従ってください。
|
|
6
6
|
keywords:
|
|
@@ -12,6 +12,7 @@ keywords:
|
|
|
12
12
|
- i18n
|
|
13
13
|
- TypeScript
|
|
14
14
|
- ロケールルーティング
|
|
15
|
+
- Sitemap
|
|
15
16
|
slugs:
|
|
16
17
|
- doc
|
|
17
18
|
- environment
|
|
@@ -19,6 +20,9 @@ slugs:
|
|
|
19
20
|
applicationTemplate: https://github.com/aymericzip/intlayer-tanstack-start-template
|
|
20
21
|
youtubeVideo: https://www.youtube.com/watch?v=_XTdKVWaeqg
|
|
21
22
|
history:
|
|
23
|
+
- version: 8.6.0
|
|
24
|
+
date: 2026-03-29
|
|
25
|
+
changes: "プリレンダリングとサイトマップの追加"
|
|
22
26
|
- version: 7.5.9
|
|
23
27
|
date: 2025-12-30
|
|
24
28
|
changes: "initコマンドを追加"
|
|
@@ -87,91 +91,687 @@ Intlayerを使用すると、以下のことが可能になります:
|
|
|
87
91
|
|
|
88
92
|
GitHubの[アプリケーションテンプレート](https://github.com/aymericzip/intlayer-tanstack-start-template)を参照してください。
|
|
89
93
|
|
|
90
|
-
|
|
94
|
+
### ステップ 1: プロジェクトの作成
|
|
91
95
|
|
|
92
|
-
|
|
96
|
+
まず、TanStack Startウェブサイトの[プロジェクトの開始](https://tanstack.com/start/latest/docs/framework/react/quick-start)ガイドに従って、新しいTanStack Startプロジェクトを作成します。
|
|
93
97
|
|
|
94
|
-
Intlayer
|
|
98
|
+
### ステップ 2: Intlayerパッケージのインストール
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
好みのパッケージマネージャーを使用して、必要なパッケージをインストールします:
|
|
97
101
|
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
```bash packageManager="npm"
|
|
103
|
+
npm install intlayer react-intlayer
|
|
104
|
+
npm install vite-intlayer --save-dev
|
|
105
|
+
npx intlayer init
|
|
101
106
|
```
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
```bash packageManager="pnpm"
|
|
109
|
+
pnpm add intlayer react-intlayer
|
|
110
|
+
pnpm add vite-intlayer --save-dev
|
|
111
|
+
pnpm intlayer init
|
|
112
|
+
```
|
|
104
113
|
|
|
105
|
-
|
|
114
|
+
```bash packageManager="yarn"
|
|
115
|
+
yarn add intlayer react-intlayer
|
|
116
|
+
yarn add vite-intlayer --save-dev
|
|
117
|
+
yarn intlayer init
|
|
118
|
+
```
|
|
106
119
|
|
|
107
|
-
|
|
120
|
+
```bash packageManager="bun"
|
|
121
|
+
bun add intlayer react-intlayer
|
|
122
|
+
bun add vite-intlayer --dev
|
|
123
|
+
bun x intlayer init
|
|
124
|
+
```
|
|
108
125
|
|
|
109
|
-
|
|
126
|
+
- **intlayer**
|
|
110
127
|
|
|
111
|
-
|
|
112
|
-
import { type IntlayerConfig } from "intlayer";
|
|
128
|
+
構成管理、翻訳、[コンテンツ宣言](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/dictionary/content_file.md)、トランスパイル、および[CLIコマンド](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/cli/index.md)のための国際化ツールを提供するコアパッケージです。
|
|
113
129
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
compiler: {
|
|
117
|
-
/**
|
|
118
|
-
* コンパイラを有効にするかどうかを指定します。
|
|
119
|
-
*/
|
|
120
|
-
enabled: true,
|
|
130
|
+
- **react-intlayer**
|
|
131
|
+
IntlayerをReactアプリケーションと統合するパッケージです。Reactの国際化のためのコンテキストプロバイダーとフックを提供します。
|
|
121
132
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
*/
|
|
125
|
-
output: ({ fileName, extension }) => `./${fileName}${extension}`,
|
|
133
|
+
- **vite-intlayer**
|
|
134
|
+
Intlayerを[Viteバンミラー](https://vite.dev/guide/why.html#why-bundle-for-production)と統合するためのViteプラグイン、およびユーザーの優先ロケールの検出、クッキーの管理、URLリダイレクトの処理のためのミドルウェアが含まれています。
|
|
126
135
|
|
|
127
|
-
|
|
128
|
-
* 変換後にコンポーネントを保存するかどうかを指定します。これにより、コンパイラを一度だけ実行してアプリを変換し、その後削除することができます。
|
|
129
|
-
*/
|
|
130
|
-
saveComponents: false,
|
|
136
|
+
### ステップ 3: プロジェクトの構成
|
|
131
137
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
アプリケーションの言語を構成するための設定ファイルを作成します:
|
|
139
|
+
|
|
140
|
+
```typescript fileName="intlayer.config.ts"
|
|
141
|
+
import type { IntlayerConfig } from "intlayer";
|
|
142
|
+
|
|
143
|
+
import { Locales } from "intlayer";
|
|
144
|
+
|
|
145
|
+
const config: IntlayerConfig = {
|
|
146
|
+
internationalization: {
|
|
147
|
+
defaultLocale: Locales.ENGLISH,
|
|
148
|
+
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
136
149
|
},
|
|
137
150
|
};
|
|
138
151
|
|
|
139
152
|
export default config;
|
|
140
153
|
```
|
|
141
154
|
|
|
142
|
-
|
|
143
|
-
/** @type {import('intlayer').IntlayerConfig} */
|
|
144
|
-
const config = {
|
|
145
|
-
// ... 他の構成
|
|
146
|
-
compiler: {
|
|
147
|
-
/**
|
|
148
|
-
* コンパイラを有効にするかどうかを指定します。
|
|
149
|
-
*/
|
|
150
|
-
enabled: true,
|
|
155
|
+
> この構成ファイルを通じて、ローカライズされたURL、ミドルウェアのリダイレクト、クッキー名、コンテンツ宣言の場所と拡張子、コンソールでのIntlayerログの無効化などを設定できます。利用可能なパラメータの完全なリストについては、[構成ドキュメント](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/configuration.md)を参照してください。
|
|
151
156
|
|
|
152
|
-
|
|
153
|
-
* 出力ファイルのパスを定義します。
|
|
154
|
-
*/
|
|
155
|
-
output: ({ fileName, extension }) => `./${fileName}${extension}`,
|
|
157
|
+
### ステップ 4: Vite構成へのIntlayerの統合
|
|
156
158
|
|
|
157
|
-
|
|
158
|
-
* 変換後にコンポーネントを保存するかどうかを指定します。これにより、コンパイラを一度だけ実行してアプリを変換し、その後削除することができます。
|
|
159
|
-
*/
|
|
160
|
-
saveComponents: false,
|
|
159
|
+
構成にintlayerプラグインを追加します:
|
|
161
160
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
```typescript fileName="vite.config.ts"
|
|
162
|
+
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
163
|
+
import viteReact from "@vitejs/plugin-react";
|
|
164
|
+
import { nitro } from "nitro/vite";
|
|
165
|
+
import { defineConfig } from "vite";
|
|
166
|
+
import { intlayer } from "vite-intlayer";
|
|
167
|
+
|
|
168
|
+
const config = defineConfig({
|
|
169
|
+
plugins: [
|
|
170
|
+
nitro(),
|
|
171
|
+
intlayer(),
|
|
172
|
+
tanstackStart({
|
|
173
|
+
router: {
|
|
174
|
+
routeFileIgnorePattern:
|
|
175
|
+
".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5)$",
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
viteReact(),
|
|
179
|
+
],
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
export default config;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> `intlayer()` Viteプラグインは、IntlayerをViteと統合するために使用されます。コンテンツ宣言ファイルの構築を確実にし、開発モードでそれらを監視します。Viteアプリケーション内でIntlayer環境変数を定義します。さらに、パフォーマンスを最適化するためのエイリアスを提供します。
|
|
186
|
+
|
|
187
|
+
### ステップ 5: ルートレイアウトの作成
|
|
188
|
+
|
|
189
|
+
`useMatches`を使用して現在のロケールを検出し、`html`タグに`lang`および`dir`属性を設定することで、国際化をサポートするようにルートレイアウトを構成します。
|
|
190
|
+
|
|
191
|
+
```tsx fileName="src/routes/__root.tsx"
|
|
192
|
+
import {
|
|
193
|
+
createRootRouteWithContext,
|
|
194
|
+
HeadContent,
|
|
195
|
+
Outlet,
|
|
196
|
+
Scripts,
|
|
197
|
+
useMatches,
|
|
198
|
+
} from "@tanstack/react-router";
|
|
199
|
+
import { defaultLocale, getHTMLTextDir } from "intlayer";
|
|
200
|
+
import { type ReactNode } from "react";
|
|
201
|
+
import { IntlayerProvider } from "react-intlayer";
|
|
202
|
+
|
|
203
|
+
export const Route = createRootRouteWithContext<{}>()({
|
|
204
|
+
shellComponent: RootDocument,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
function RootDocument({ children }: { children: ReactNode }) {
|
|
208
|
+
const matches = useMatches();
|
|
209
|
+
|
|
210
|
+
// アクティブなマッチのパラメータからロケールを探そうとします
|
|
211
|
+
// これは、ルートツリーで動的セグメント "/{-$locale}" を使用していることを前提としています
|
|
212
|
+
const localeRoute = matches.find((match) => match.routeId === "/{-$locale}");
|
|
213
|
+
const locale = localeRoute?.params?.locale ?? defaultLocale;
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<html dir={getHTMLTextDir(locale)} lang={locale}>
|
|
217
|
+
<head>
|
|
218
|
+
<HeadContent />
|
|
219
|
+
</head>
|
|
220
|
+
<body>
|
|
221
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
222
|
+
<Scripts />
|
|
223
|
+
</body>
|
|
224
|
+
</html>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### ステップ 6: ロケールレイアウトの作成
|
|
230
|
+
|
|
231
|
+
ロケール接頭辞を処理し、検証を実行するレイアウトを作成します。
|
|
232
|
+
|
|
233
|
+
```tsx fileName="src/routes/{-$locale}/route.tsx"
|
|
234
|
+
import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";
|
|
235
|
+
import { validatePrefix } from "intlayer";
|
|
236
|
+
|
|
237
|
+
export const Route = createFileRoute("/{-$locale}")({
|
|
238
|
+
beforeLoad: ({ params }) => {
|
|
239
|
+
const localeParam = params.locale;
|
|
240
|
+
|
|
241
|
+
// ロケール接頭辞を検証する
|
|
242
|
+
const { isValid, localePrefix } = validatePrefix(localeParam);
|
|
243
|
+
|
|
244
|
+
if (!isValid) {
|
|
245
|
+
throw redirect({
|
|
246
|
+
to: "/{-$locale}/404",
|
|
247
|
+
params: { locale: localePrefix },
|
|
248
|
+
});
|
|
249
|
+
}
|
|
166
250
|
},
|
|
251
|
+
component: Outlet,
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
> ここで、`{-$locale}`は現在のロケールに置き換えられる動的なルートパラメータです。この記法によりスロットがオプションになり、`'prefix-no-default'`などのルーティングモードで動作できるようになります。
|
|
256
|
+
|
|
257
|
+
> 同じルートで複数の動的セグメントを使用する場合(例:`/{-$locale}/other-path/$anotherDynamicPath/...`)、このスロットが問題を引き起こす可能性があることに注意してください。
|
|
258
|
+
> `'prefix-all'`モードの場合、スロットを`$locale`に切り替えることをお勧めします。
|
|
259
|
+
> `'no-prefix'`または`'search-params'`モードの場合、スロットを完全に削除できます。
|
|
260
|
+
|
|
261
|
+
### ステップ 7: コンテンツの宣言
|
|
262
|
+
|
|
263
|
+
翻訳を保存するためのコンテンツ宣言を作成および管理します:
|
|
264
|
+
|
|
265
|
+
```tsx fileName="src/contents/page.content.ts"
|
|
266
|
+
import type { Dictionary } from "intlayer";
|
|
267
|
+
|
|
268
|
+
import { t } from "intlayer";
|
|
269
|
+
|
|
270
|
+
const appContent = {
|
|
271
|
+
content: {
|
|
272
|
+
links: {
|
|
273
|
+
about: t({
|
|
274
|
+
en: "About",
|
|
275
|
+
es: "Acerca de",
|
|
276
|
+
fr: "À propos",
|
|
277
|
+
ja: "アバウト",
|
|
278
|
+
}),
|
|
279
|
+
home: t({
|
|
280
|
+
en: "Home",
|
|
281
|
+
es: "Inicio",
|
|
282
|
+
fr: "Accueil",
|
|
283
|
+
ja: "ホーム",
|
|
284
|
+
}),
|
|
285
|
+
},
|
|
286
|
+
meta: {
|
|
287
|
+
title: t({
|
|
288
|
+
en: "Welcome to Intlayer + TanStack Router",
|
|
289
|
+
es: "Bienvenido a Intlayer + TanStack Router",
|
|
290
|
+
fr: "Bienvenue à Intlayer + TanStack Router",
|
|
291
|
+
ja: "Intlayer + TanStack Router へようこそ",
|
|
292
|
+
}),
|
|
293
|
+
description: t({
|
|
294
|
+
en: "This is an example of using Intlayer with TanStack Router",
|
|
295
|
+
es: "Este es un ejemplo de uso de Intlayer con TanStack Router",
|
|
296
|
+
fr: "Ceci est un exemple d'utilisation d'Intlayer avec TanStack Router",
|
|
297
|
+
ja: "これは Intlayer と TanStack Router を使用した例です",
|
|
298
|
+
}),
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
key: "app",
|
|
302
|
+
} satisfies Dictionary;
|
|
303
|
+
|
|
304
|
+
export default appContent;
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
> コンテンツ宣言は、`contentDir`ディレクトリ(デフォルトでは`./app`)に含まれている限り、アプリケーションのどこでも定義できます。また、コンテンツ宣言ファイルの拡張子(デフォルトでは`.content.{json,ts,tsx,js,jsx,mjs,cjs}`)と一致する必要があります。
|
|
308
|
+
|
|
309
|
+
> 詳細については、[コンテンツ宣言ドキュメント](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/dictionary/content_file.md)を参照してください。
|
|
310
|
+
|
|
311
|
+
### ステップ 8: ロケール対応コンポーネントとフックの作成
|
|
312
|
+
|
|
313
|
+
ロケール対応のナビゲーションのための `LocalizedLink` コンポーネントを作成します:
|
|
314
|
+
|
|
315
|
+
```tsx fileName="src/components/localized-link.tsx"
|
|
316
|
+
import type { FC } from "react";
|
|
317
|
+
|
|
318
|
+
import { Link, type LinkComponentProps } from "@tanstack/react-router";
|
|
319
|
+
import { useLocale } from "react-intlayer";
|
|
320
|
+
import { getPrefix } from "intlayer";
|
|
321
|
+
|
|
322
|
+
export const LOCALE_ROUTE = "{-$locale}" as const;
|
|
323
|
+
|
|
324
|
+
// メインユーティリティ
|
|
325
|
+
export type RemoveLocaleParam<T> = T extends string
|
|
326
|
+
? RemoveLocaleFromString<T>
|
|
327
|
+
: T;
|
|
328
|
+
|
|
329
|
+
export type To = RemoveLocaleParam<LinkComponentProps["to"]>;
|
|
330
|
+
|
|
331
|
+
type CollapseDoubleSlashes<S extends string> =
|
|
332
|
+
S extends `${infer H}//${infer T}` ? CollapseDoubleSlashes<`${H}/${T}`> : S;
|
|
333
|
+
|
|
334
|
+
type LocalizedLinkProps = {
|
|
335
|
+
to?: To;
|
|
336
|
+
} & Omit<LinkComponentProps, "to">;
|
|
337
|
+
|
|
338
|
+
// ヘルパー
|
|
339
|
+
type RemoveAll<
|
|
340
|
+
S extends string,
|
|
341
|
+
Sub extends string,
|
|
342
|
+
> = S extends `${infer H}${Sub}${infer T}` ? RemoveAll<`${H}${T}`, Sub> : S;
|
|
343
|
+
|
|
344
|
+
type RemoveLocaleFromString<S extends string> = CollapseDoubleSlashes<
|
|
345
|
+
RemoveAll<S, typeof LOCALE_ROUTE>
|
|
346
|
+
>;
|
|
347
|
+
|
|
348
|
+
export const LocalizedLink: FC<LocalizedLinkProps> = (props) => {
|
|
349
|
+
const { locale } = useLocale();
|
|
350
|
+
const { localePrefix } = getPrefix(locale);
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<Link
|
|
354
|
+
{...props}
|
|
355
|
+
params={{
|
|
356
|
+
locale: localePrefix,
|
|
357
|
+
...(typeof props?.params === "object" ? props?.params : {}),
|
|
358
|
+
}}
|
|
359
|
+
to={`/${LOCALE_ROUTE}${props.to}` as LinkComponentProps["to"]}
|
|
360
|
+
/>
|
|
361
|
+
);
|
|
167
362
|
};
|
|
363
|
+
```
|
|
168
364
|
|
|
169
|
-
|
|
365
|
+
このコンポーネントには2つの目的があります:
|
|
366
|
+
|
|
367
|
+
- URLから不要な`{-$locale}`接頭辞を削除します。
|
|
368
|
+
- ロケールパラメータをURLに挿入して、ユーザーがローカライズされたルートに直接リダイレクトされるようにします。
|
|
369
|
+
|
|
370
|
+
次に、プログラムによるナビゲーションのための `useLocalizedNavigate` フックを作成できます:
|
|
371
|
+
|
|
372
|
+
```tsx fileName="src/hooks/useLocalizedNavigate.tsx"
|
|
373
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
374
|
+
import { getPrefix } from "intlayer";
|
|
375
|
+
import { useLocale } from "react-intlayer";
|
|
376
|
+
import { LOCALE_ROUTE } from "@/components/localized-link";
|
|
377
|
+
import type { FileRouteTypes } from "@/routeTree.gen";
|
|
378
|
+
|
|
379
|
+
type StripLocalePrefix<T extends string> = T extends
|
|
380
|
+
| `/${typeof LOCALE_ROUTE}`
|
|
381
|
+
| `/${typeof LOCALE_ROUTE}/`
|
|
382
|
+
? "/"
|
|
383
|
+
: T extends `/${typeof LOCALE_ROUTE}/${infer Rest}`
|
|
384
|
+
? `/${Rest}`
|
|
385
|
+
: never;
|
|
386
|
+
|
|
387
|
+
type LocalizedTo = StripLocalePrefix<FileRouteTypes["to"]>;
|
|
388
|
+
|
|
389
|
+
type LocalizedNavigate = {
|
|
390
|
+
(to: LocalizedTo): ReturnType<ReturnType<typeof useNavigate>>;
|
|
391
|
+
(
|
|
392
|
+
opts: { to: LocalizedTo } & Record<string, unknown>
|
|
393
|
+
): ReturnType<ReturnType<typeof useNavigate>>;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export const useLocalizedNavigate = () => {
|
|
397
|
+
const navigate = useNavigate();
|
|
398
|
+
|
|
399
|
+
const { locale } = useLocale();
|
|
400
|
+
|
|
401
|
+
const localizedNavigate: LocalizedNavigate = (args: any) => {
|
|
402
|
+
const { localePrefix } = getPrefix(locale);
|
|
403
|
+
|
|
404
|
+
if (typeof args === "string") {
|
|
405
|
+
return navigate({
|
|
406
|
+
to: `/${LOCALE_ROUTE}${args}`,
|
|
407
|
+
params: { locale: localePrefix },
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const { to, ...rest } = args;
|
|
412
|
+
|
|
413
|
+
const localizedTo = `/${LOCALE_ROUTE}${to}` as any;
|
|
414
|
+
|
|
415
|
+
return navigate({
|
|
416
|
+
to: localizedTo,
|
|
417
|
+
params: { locale: localePrefix, ...rest } as any,
|
|
418
|
+
});
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
return localizedNavigate;
|
|
422
|
+
};
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### ステップ 9: ページでのIntlayerの利用
|
|
426
|
+
|
|
427
|
+
アプリケーション全体でコンテンツ辞書にアクセスします:
|
|
428
|
+
|
|
429
|
+
#### ローカライズされたホームページ
|
|
430
|
+
|
|
431
|
+
```tsx fileName="src/routes/{-$locale}/index.tsx"
|
|
432
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
433
|
+
import {
|
|
434
|
+
getIntlayer,
|
|
435
|
+
defaultLocale,
|
|
436
|
+
localeMap,
|
|
437
|
+
getLocalizedUrl,
|
|
438
|
+
} from "intlayer";
|
|
439
|
+
import { useIntlayer } from "react-intlayer";
|
|
440
|
+
|
|
441
|
+
import LocaleSwitcher from "@/components/locale-switcher";
|
|
442
|
+
import { LocalizedLink } from "@/components/localized-link";
|
|
443
|
+
import { useLocalizedNavigate } from "@/hooks/useLocalizedNavigate";
|
|
444
|
+
|
|
445
|
+
export const Route = createFileRoute("/{-$locale}/")({
|
|
446
|
+
component: RouteComponent,
|
|
447
|
+
head: ({ params }) => {
|
|
448
|
+
const { locale } = params;
|
|
449
|
+
const path = "/"; // このルートのパス
|
|
450
|
+
|
|
451
|
+
const metaContent = getIntlayer("app", locale);
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
links: [
|
|
455
|
+
// カノニカルリンク:現在のローカライズされたページを指します
|
|
456
|
+
{ rel: "canonical", href: getLocalizedUrl(path, locale) },
|
|
457
|
+
|
|
458
|
+
// Hreflang:すべてのローカライズされたバージョンについてGoogleに伝えます
|
|
459
|
+
...localeMap(({ locale: mapLocale }) => ({
|
|
460
|
+
rel: "alternate",
|
|
461
|
+
hrefLang: mapLocale,
|
|
462
|
+
href: getLocalizedUrl(path, mapLocale),
|
|
463
|
+
})),
|
|
464
|
+
|
|
465
|
+
// x-default:一致する言語がないユーザー向け
|
|
466
|
+
// デフォルトのフォールバックロケールを定義します(通常は主要言語)
|
|
467
|
+
{
|
|
468
|
+
rel: "alternate",
|
|
469
|
+
hrefLang: "x-default",
|
|
470
|
+
href: getLocalizedUrl(path, defaultLocale),
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
meta: [
|
|
474
|
+
{ title: metaContent.title },
|
|
475
|
+
{ name: "description", content: metaContent.meta.description },
|
|
476
|
+
],
|
|
477
|
+
};
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
function RouteComponent() {
|
|
482
|
+
const content = useIntlayer("app");
|
|
483
|
+
const navigate = useLocalizedNavigate();
|
|
484
|
+
|
|
485
|
+
return (
|
|
486
|
+
<div>
|
|
487
|
+
<div>
|
|
488
|
+
{content.title}
|
|
489
|
+
<LocaleSwitcher />
|
|
490
|
+
<div>
|
|
491
|
+
<LocalizedLink to="/">{content.links.home}</LocalizedLink>
|
|
492
|
+
<LocalizedLink to="/about">{content.links.about}</LocalizedLink>
|
|
493
|
+
</div>
|
|
494
|
+
<div>
|
|
495
|
+
<button onClick={() => navigate({ to: "/" })}>
|
|
496
|
+
{content.links.home}
|
|
497
|
+
</button>
|
|
498
|
+
<button onClick={() => navigate({ to: "/about" })}>
|
|
499
|
+
{content.links.about}
|
|
500
|
+
</button>
|
|
501
|
+
</div>
|
|
502
|
+
</div>
|
|
503
|
+
</div>
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
> `useIntlayer`フックの詳細については、[ドキュメント](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/packages/react-intlayer/useIntlayer.md)を参照してください。
|
|
509
|
+
|
|
510
|
+
### ステップ 10: ロケールスイッチャーコンポーネントの作成
|
|
511
|
+
|
|
512
|
+
ユーザーが言語を変更できるようにするコンポーネントを作成します:
|
|
513
|
+
|
|
514
|
+
```tsx fileName="src/components/locale-switcher.tsx"
|
|
515
|
+
import { useLocation } from "@tanstack/react-router";
|
|
516
|
+
import {
|
|
517
|
+
getHTMLTextDir,
|
|
518
|
+
getLocaleName,
|
|
519
|
+
getPathWithoutLocale,
|
|
520
|
+
getPrefix,
|
|
521
|
+
Locales,
|
|
522
|
+
} from "intlayer";
|
|
523
|
+
import type { FC } from "react";
|
|
524
|
+
import { useLocale } from "react-intlayer";
|
|
525
|
+
|
|
526
|
+
import { LocalizedLink, type To } from "./localized-link";
|
|
527
|
+
|
|
528
|
+
export const LocaleSwitcher: FC = () => {
|
|
529
|
+
const { pathname } = useLocation();
|
|
530
|
+
|
|
531
|
+
const { availableLocales, locale, setLocale } = useLocale();
|
|
532
|
+
|
|
533
|
+
const pathWithoutLocale = getPathWithoutLocale(pathname);
|
|
534
|
+
|
|
535
|
+
return (
|
|
536
|
+
<ol>
|
|
537
|
+
{availableLocales.map((localeEl) => (
|
|
538
|
+
<li key={localeEl}>
|
|
539
|
+
<LocalizedLink
|
|
540
|
+
aria-current={localeEl === locale ? "page" : undefined}
|
|
541
|
+
onClick={() => setLocale(localeEl)}
|
|
542
|
+
params={{ locale: getPrefix(localeEl).localePrefix }}
|
|
543
|
+
to={pathWithoutLocale as To}
|
|
544
|
+
>
|
|
545
|
+
<span>
|
|
546
|
+
{/* ロケール - 例: FR */}
|
|
547
|
+
{localeEl}
|
|
548
|
+
</span>
|
|
549
|
+
<span>
|
|
550
|
+
{/* そのロケール自体での言語名 - 例: Français */}
|
|
551
|
+
{getLocaleName(localeEl, locale)}
|
|
552
|
+
</span>
|
|
553
|
+
<span dir={getHTMLTextDir(localeEl)} lang={localeEl}>
|
|
554
|
+
{/* 現在のロケールでの言語名 - 例: Locales.SPANISH の場合 Francés */}
|
|
555
|
+
{getLocaleName(localeEl)}
|
|
556
|
+
</span>
|
|
557
|
+
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
558
|
+
{/* 英語での言語名 - 例: French */}
|
|
559
|
+
{getLocaleName(localeEl, Locales.ENGLISH)}
|
|
560
|
+
</span>
|
|
561
|
+
</LocalizedLink>
|
|
562
|
+
</li>
|
|
563
|
+
))}
|
|
564
|
+
</ol>
|
|
565
|
+
);
|
|
566
|
+
};
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
> `useLocale`フックの詳細については、[ドキュメント](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/packages/react-intlayer/useLocale.md)を参照してください。
|
|
570
|
+
|
|
571
|
+
### ステップ 11: HTML属性の管理
|
|
572
|
+
|
|
573
|
+
ステップ 5 で見たように、ルートコンポーネントで `useMatches` を使用して `html` タグの `lang` および `dir` 属性を管理できます。これにより、サーバーとクライアントで正しい属性が設定されるようになります。
|
|
574
|
+
|
|
575
|
+
```tsx fileName="src/routes/__root.tsx"
|
|
576
|
+
function RootDocument({ children }: { children: ReactNode }) {
|
|
577
|
+
const matches = useMatches();
|
|
578
|
+
|
|
579
|
+
// アクティブなマッチのパラメータからロケールを探そうとします
|
|
580
|
+
const localeRoute = matches.find((match) => match.routeId === "/{-$locale}");
|
|
581
|
+
const locale = localeRoute?.params?.locale ?? defaultLocale;
|
|
582
|
+
|
|
583
|
+
return (
|
|
584
|
+
<html dir={getHTMLTextDir(locale)} lang={locale}>
|
|
585
|
+
{/* ... */}
|
|
586
|
+
</html>
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
### ステップ 12: ミドルウェアの追加 (オプション)
|
|
594
|
+
|
|
595
|
+
`intlayerProxy`を使用して、アプリケーションにサーバーサイドのルーティングを追加することもできます。このプラグインは、URLに基づいて現在のロケールを自動的に検出し、適切なロケールクッキーを設定します。ロケールが指定されていない場合、ユーザーのブラウザの言語設定に基づいて最も適切なロケールを決定します。ロケールが検出されない場合は、デフォルトのロケールにリダイレクトされます。
|
|
596
|
+
|
|
597
|
+
> `intlayerProxy`を本番環境で使用するには、`vite-intlayer`パッケージを`devDependencies`から`dependencies`に切り替える必要があることに注意してください。
|
|
598
|
+
|
|
599
|
+
```typescript {7,14-17} fileName="vite.config.ts"
|
|
600
|
+
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
601
|
+
import viteReact from "@vitejs/plugin-react";
|
|
602
|
+
import { nitro } from "nitro/vite";
|
|
603
|
+
import { defineConfig } from "vite";
|
|
604
|
+
import { intlayer, intlayerProxy } from "vite-intlayer";
|
|
605
|
+
|
|
606
|
+
export default defineConfig({
|
|
607
|
+
plugins: [
|
|
608
|
+
intlayerProxy(), // Nitroを使用する場合、プロキシをサーバーの前に配置する必要があります
|
|
609
|
+
nitro(),
|
|
610
|
+
intlayer(),
|
|
611
|
+
tanstackStart({
|
|
612
|
+
router: {
|
|
613
|
+
routeFileIgnorePattern:
|
|
614
|
+
".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5)$",
|
|
615
|
+
},
|
|
616
|
+
}),
|
|
617
|
+
viteReact(),
|
|
618
|
+
],
|
|
619
|
+
});
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
### ステップ 12: メタデータの国際化 (オプション)
|
|
625
|
+
|
|
626
|
+
`getIntlayer`フックを使用して、アプリケーション全体でコンテンツ辞書にアクセスすることもできます:
|
|
627
|
+
|
|
628
|
+
```tsx fileName="src/routes/{-$locale}/index.tsx"
|
|
629
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
630
|
+
import { getIntlayer } from "intlayer";
|
|
631
|
+
|
|
632
|
+
export const Route = createFileRoute("/{-$locale}/")({
|
|
633
|
+
component: RouteComponent,
|
|
634
|
+
head: ({ params }) => {
|
|
635
|
+
const { locale } = params;
|
|
636
|
+
const metaContent = getIntlayer("page-metadata", locale);
|
|
637
|
+
|
|
638
|
+
return {
|
|
639
|
+
meta: [
|
|
640
|
+
{ title: metaContent.title },
|
|
641
|
+
{ content: metaContent.description, name: "description" },
|
|
642
|
+
],
|
|
643
|
+
};
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
### ステップ 13: サーバーアクションでのロケールの取得 (オプション)
|
|
651
|
+
|
|
652
|
+
サーバーアクションやAPIエンドポイント内から現在のロケールにアクセスしたい場合があります。
|
|
653
|
+
Intlayerの `getLocale` ヘルパーを使用してこれを行うことができます。
|
|
654
|
+
|
|
655
|
+
以下は、TanStack Startのサーバー関数を使用した例です:
|
|
656
|
+
|
|
657
|
+
```tsx fileName="src/routes/{-$locale}/index.tsx"
|
|
658
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
659
|
+
import {
|
|
660
|
+
getRequestHeader,
|
|
661
|
+
getRequestHeaders,
|
|
662
|
+
} from "@tanstack/react-start/server";
|
|
663
|
+
import { getCookie, getIntlayer, getLocale } from "intlayer";
|
|
664
|
+
|
|
665
|
+
export const getLocaleServer = createServerFn().handler(async () => {
|
|
666
|
+
const locale = await getLocale({
|
|
667
|
+
// リクエストからクッキーを取得する(デフォルト:'INTLAYER_LOCALE')
|
|
668
|
+
getCookie: (name) => {
|
|
669
|
+
const cookieString = getRequestHeader("cookie");
|
|
670
|
+
|
|
671
|
+
return getCookie(name, cookieString);
|
|
672
|
+
},
|
|
673
|
+
// リクエストからヘッダーを取得する(デフォルト:'x-intlayer-locale')
|
|
674
|
+
// Accept-Languageネゴシエーションを使用したフォールバック
|
|
675
|
+
getHeader: (name) => getRequestHeader(name),
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
// getIntlayer()を使用してコンテンツを取得する
|
|
679
|
+
const content = getIntlayer("app", locale);
|
|
680
|
+
|
|
681
|
+
return { locale, content };
|
|
682
|
+
});
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
---
|
|
686
|
+
|
|
687
|
+
### ステップ 14: 「見つかりませんでした」ページの管理 (オプション)
|
|
688
|
+
|
|
689
|
+
ユーザーが存在しないページにアクセスしたときにカスタムの404ページを表示できます。ロケール接頭辞は、404ページがトリガーされる方法に影響を与える可能性があります。
|
|
690
|
+
|
|
691
|
+
#### ロケール接頭辞を使用したTanStack Routerの404処理の理解
|
|
692
|
+
|
|
693
|
+
TanStack Routerでは、ローカライズされたルートでの404ページの処理には多層的なアプローチが必要です:
|
|
694
|
+
|
|
695
|
+
1. **専用の404ルート**: 404 UIを表示するための特定のルート
|
|
696
|
+
2. **ルートレベルの検証**: ロケール接頭辞を検証し、無効なものを404にリダイレクトします
|
|
697
|
+
3. **キャッチオールルート**: ロケールセグメント内の一致しないパスをすべてキャプチャします
|
|
698
|
+
|
|
699
|
+
```tsx fileName="src/routes/{-$locale}/404.tsx"
|
|
700
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
701
|
+
|
|
702
|
+
// これにより、専用の /[locale]/404 ルートが作成されます
|
|
703
|
+
// これは直接のルートとしても使用され、他のファイルでコンポーネントとしてインポートもされます
|
|
704
|
+
export const Route = createFileRoute("/{-$locale}/404")({
|
|
705
|
+
component: NotFoundComponent,
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
// コンポーネントおよびキャッチオールルートで再利用できるように個別にエクスポートされます
|
|
709
|
+
export function NotFoundComponent() {
|
|
710
|
+
return (
|
|
711
|
+
<div>
|
|
712
|
+
<h1>404</h1>
|
|
713
|
+
</div>
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
```tsx fileName="src/routes/{-$locale}/route.tsx"
|
|
719
|
+
import { createFileRoute, Outlet, redirect } from "@tanstack/react-router";
|
|
720
|
+
import { validatePrefix } from "intlayer";
|
|
721
|
+
import { NotFoundComponent } from "./404";
|
|
722
|
+
|
|
723
|
+
export const Route = createFileRoute("/{-$locale}")({
|
|
724
|
+
// beforeLoadは、ルートがレンダリングされる前(サーバーとクライアントの両方)に実行されます
|
|
725
|
+
// ロケール接頭辞を検証するのに最適な場所です
|
|
726
|
+
beforeLoad: ({ params }) => {
|
|
727
|
+
const localeParam = params.locale;
|
|
728
|
+
|
|
729
|
+
// validatePrefixは、intlayer構成に従ってロケールが有効かどうかを確認します
|
|
730
|
+
const { isValid, localePrefix } = validatePrefix(localeParam);
|
|
731
|
+
|
|
732
|
+
if (!isValid) {
|
|
733
|
+
// 無効なロケール接頭辞 - 有効なロケール接頭辞を持つ404ページにリダイレクトします
|
|
734
|
+
throw redirect({
|
|
735
|
+
to: "/{-$locale}/404",
|
|
736
|
+
params: { locale: localePrefix },
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
component: Outlet,
|
|
741
|
+
// notFoundComponentは、子ルートが存在しないときに呼び出されます
|
|
742
|
+
// 例:/en/non-existent-page は /en レイアウト内でこれをトリガーします
|
|
743
|
+
notFoundComponent: NotFoundComponent,
|
|
744
|
+
});
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
```tsx fileName="src/routes/{-$locale}/$.tsx"
|
|
748
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
749
|
+
|
|
750
|
+
import { NotFoundComponent } from "./404";
|
|
751
|
+
|
|
752
|
+
// $(スプラット/キャッチオール)ルートは、他のルートと一致しないパスに一致します
|
|
753
|
+
// 例:/en/some/deeply/nested/invalid/path
|
|
754
|
+
// これにより、ロケール内の一致しないパスがすべて404ページを表示するようになります
|
|
755
|
+
// これがないと、一致しない深いパスが空白ページまたはエラーを表示する可能性があります
|
|
756
|
+
export const Route = createFileRoute("/{-$locale}/$")({
|
|
757
|
+
component: NotFoundComponent,
|
|
758
|
+
});
|
|
170
759
|
```
|
|
171
760
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
761
|
+
---
|
|
762
|
+
|
|
763
|
+
### (オプション) ステップ 15: コンポーネントのコンテンツを抽出する
|
|
764
|
+
|
|
765
|
+
既存のコードベースがある場合、数千のファイルを変換するのは時間がかかることがあります。
|
|
766
|
+
|
|
767
|
+
このプロセスを容易にするために、Intlayerは、コンポーネントを変換しコンテンツを抽出するための [コンパイラ](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/compiler.md) / [エクストラクタ](https://github.com/aymericzip/intlayer/blob/main/docs/docs/ja/cli/extract.md) を提案しています。
|
|
768
|
+
|
|
769
|
+
セットアップするには、`intlayer.config.ts` ファイルに `compiler` セクションを追加します。
|
|
770
|
+
|
|
771
|
+
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
772
|
+
import { type IntlayerConfig } from "intlayer";
|
|
773
|
+
|
|
774
|
+
const config: IntlayerConfig = {
|
|
175
775
|
// ... 他の構成
|
|
176
776
|
compiler: {
|
|
177
777
|
/**
|
|
@@ -185,7 +785,11 @@ const config = {
|
|
|
185
785
|
output: ({ fileName, extension }) => `./${fileName}${extension}`,
|
|
186
786
|
|
|
187
787
|
/**
|
|
188
|
-
*
|
|
788
|
+
* 変換後にコンポーネントを保存するかどうかを指定します。
|
|
789
|
+
*
|
|
790
|
+
* - `true` の場合、コンパイラはディスク上のコンポーネントファイルを書き換えます。変換は永続的になり、次回のプロセスではスキップされます。これにより、アプリの変換後にコンパイラを削除できます。
|
|
791
|
+
*
|
|
792
|
+
* - `false` の場合、コンパイラはビルド出力のコードにのみ `useIntlayer()` 関数呼び出しを注入し、元のコードベースはそのまま維持します。変換はメモリ内でのみ行われます。
|
|
189
793
|
*/
|
|
190
794
|
saveComponents: false,
|
|
191
795
|
|
|
@@ -196,7 +800,7 @@ const config = {
|
|
|
196
800
|
},
|
|
197
801
|
};
|
|
198
802
|
|
|
199
|
-
|
|
803
|
+
export default config;
|
|
200
804
|
```
|
|
201
805
|
|
|
202
806
|
<Tabs>
|
|
@@ -223,55 +827,137 @@ bun x intlayer extract
|
|
|
223
827
|
</Tab>
|
|
224
828
|
<Tab value='Babelコンパイラ'>
|
|
225
829
|
|
|
226
|
-
|
|
227
|
-
npm install @intlayer/babel --save-dev
|
|
228
|
-
```
|
|
830
|
+
`vite.config.ts` を更新して `intlayerCompiler` プラグインを含めます:
|
|
229
831
|
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
|
|
832
|
+
```ts fileName="vite.config.ts"
|
|
833
|
+
import { intlayer, intlayerCompiler } from "vite-intlayer";
|
|
834
|
+
import { defineConfig } from "vite";
|
|
835
|
+
import { devtools } from "@tanstack/devtools-vite";
|
|
836
|
+
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
837
|
+
import viteReact from "@vitejs/plugin-react";
|
|
233
838
|
|
|
234
|
-
|
|
235
|
-
|
|
839
|
+
export default defineConfig({
|
|
840
|
+
plugins: [
|
|
841
|
+
devtools(),
|
|
842
|
+
intlayerCompiler(),
|
|
843
|
+
tanstackStart({
|
|
844
|
+
router: {
|
|
845
|
+
routeFileIgnorePattern:
|
|
846
|
+
".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5)$",
|
|
847
|
+
},
|
|
848
|
+
}),
|
|
849
|
+
viteReact(),
|
|
850
|
+
intlayer(),
|
|
851
|
+
],
|
|
852
|
+
});
|
|
236
853
|
```
|
|
237
854
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
855
|
+
</Tab>
|
|
856
|
+
</Tabs>
|
|
857
|
+
|
|
858
|
+
### ステップ 16: サイトマップの生成 (オプション)
|
|
859
|
+
|
|
860
|
+
Intlayer には、アプリケーションのサイトマップを簡単に作成できるサイトマップ ジェネレーターが組み込まれています。ローカライズされたルートを処理し、検索エンジンに必要なメタデータを追加します。
|
|
861
|
+
|
|
862
|
+
> Intlayer によって生成されたサイトマップは、`xhtml:link` 名前空間 (Hreflang XML Extensions) をサポートしています。生の URL のみを表示するデフォルトのサイトマップ ジェネレーターとは異なり、Intlayer はページのすべての言語バージョン (例: `/about`、`/about?lang=fr`、`/about?lang=es`) 間に必要な双方向リンクを自動的に作成します。これにより、検索エンジンが正しい言語バージョンを正しい対象者に正しくインデックス付けして提供できるようになります。
|
|
863
|
+
|
|
864
|
+
これを使用するには、まず `vite.config.ts` を構成して、ローカライズされたルートのプリレンダリングを有効にし、デフォルトの TanStack Start サイトマップ生成を無効にする必要があります。
|
|
241
865
|
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
getExtractPluginOptions,
|
|
246
|
-
} = require("@intlayer/babel");
|
|
866
|
+
```typescript fileName="vite.config.ts"
|
|
867
|
+
import { localeFlatMap } from "intlayer";
|
|
868
|
+
// ... その他のインポート
|
|
247
869
|
|
|
248
|
-
|
|
249
|
-
|
|
870
|
+
export const pathList = ["", "/about", "/404"];
|
|
871
|
+
|
|
872
|
+
const localizedPages = localeFlatMap(({ urlPrefix }) =>
|
|
873
|
+
pathList.map((path) => ({
|
|
874
|
+
path: `${urlPrefix}${path}`,
|
|
875
|
+
prerender: {
|
|
876
|
+
enabled: true,
|
|
877
|
+
},
|
|
878
|
+
}))
|
|
879
|
+
);
|
|
880
|
+
|
|
881
|
+
export default defineConfig({
|
|
250
882
|
plugins: [
|
|
251
|
-
//
|
|
252
|
-
|
|
883
|
+
// ... その他のプラグイン
|
|
884
|
+
tanstackStart({
|
|
885
|
+
// ... その他の設定
|
|
886
|
+
sitemap: {
|
|
887
|
+
enabled: false,
|
|
888
|
+
},
|
|
889
|
+
prerender: {
|
|
890
|
+
enabled: true,
|
|
891
|
+
crawlLinks: false,
|
|
892
|
+
concurrency: 10,
|
|
893
|
+
},
|
|
894
|
+
pages: localizedPages,
|
|
895
|
+
}),
|
|
253
896
|
],
|
|
254
|
-
};
|
|
897
|
+
});
|
|
255
898
|
```
|
|
256
899
|
|
|
257
|
-
|
|
258
|
-
|
|
900
|
+
次に、`generateSitemap` 関数を使用する `src/routes/sitemap[.]xml.ts` ルートを作成します。
|
|
901
|
+
|
|
902
|
+
```typescript fileName="src/routes/sitemap[.]xml.ts"
|
|
903
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
904
|
+
import { generateSitemap } from "intlayer";
|
|
905
|
+
|
|
906
|
+
const SITE_URL = (
|
|
907
|
+
import.meta.env.VITE_SITE_URL ?? "http://localhost:3000"
|
|
908
|
+
).replace(/\/$/, "");
|
|
909
|
+
|
|
910
|
+
export const Route = createFileRoute("/sitemap.xml")({
|
|
911
|
+
server: {
|
|
912
|
+
handlers: {
|
|
913
|
+
GET: async () => {
|
|
914
|
+
const sitemap = generateSitemap(
|
|
915
|
+
[
|
|
916
|
+
{ path: "/", changefreq: "daily", priority: 1.0 },
|
|
917
|
+
{ path: "/about", changefreq: "monthly", priority: 0.8 },
|
|
918
|
+
],
|
|
919
|
+
{ siteUrl: SITE_URL }
|
|
920
|
+
);
|
|
921
|
+
|
|
922
|
+
return new Response(sitemap, {
|
|
923
|
+
headers: { "Content-Type": "application/xml" },
|
|
924
|
+
});
|
|
925
|
+
},
|
|
926
|
+
},
|
|
927
|
+
},
|
|
928
|
+
});
|
|
259
929
|
```
|
|
260
930
|
|
|
261
|
-
|
|
262
|
-
pnpm run build # または pnpm run dev
|
|
263
|
-
```
|
|
931
|
+
---
|
|
264
932
|
|
|
265
|
-
|
|
266
|
-
yarn build # または yarn dev
|
|
267
|
-
```
|
|
933
|
+
### ステップ 17: TypeScriptの構成 (オプション)
|
|
268
934
|
|
|
269
|
-
|
|
270
|
-
|
|
935
|
+
Intlayerはモジュール拡張機能を利用して、TypeScriptの利点を活用し、コードベースを強化します。
|
|
936
|
+
|
|
937
|
+
TypeScriptの構成に自動生成された型が含まれていることを確認してください:
|
|
938
|
+
|
|
939
|
+
```json5 fileName="tsconfig.json"
|
|
940
|
+
{
|
|
941
|
+
// ... 既存の構成
|
|
942
|
+
include: [
|
|
943
|
+
// ... 既存の包含
|
|
944
|
+
".intlayer/**/*.ts", // 自動生成された型を含める
|
|
945
|
+
],
|
|
946
|
+
}
|
|
271
947
|
```
|
|
272
948
|
|
|
273
|
-
|
|
274
|
-
|
|
949
|
+
---
|
|
950
|
+
|
|
951
|
+
### Git構成
|
|
952
|
+
|
|
953
|
+
Intlayerによって生成されたファイルは無視することをお勧めします。これにより、Gitリポジトリにそれらをコミットすることを避けることができます。
|
|
954
|
+
|
|
955
|
+
これを行うには、`.gitignore`ファイルに以下の指示を追加できます:
|
|
956
|
+
|
|
957
|
+
```plaintext fileName=".gitignore"
|
|
958
|
+
# Intlayerによって生成されたファイルを無視する
|
|
959
|
+
.intlayer
|
|
960
|
+
```
|
|
275
961
|
|
|
276
962
|
---
|
|
277
963
|
|