@intlayer/docs 8.0.0 → 8.0.1-canary.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/dist/cjs/generated/docs.entry.cjs +160 -0
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +160 -0
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +8 -0
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/docs/ar/intlayer_with_adonisjs.md +394 -0
- package/docs/ar/intlayer_with_hono.md +223 -0
- package/docs/ar/intlayer_with_vite+preact.md +317 -675
- package/docs/ar/packages/adonis-intlayer/exports.md +50 -0
- package/docs/ar/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/ar/packages/adonis-intlayer/t.md +149 -0
- package/docs/ar/packages/hono-intlayer/exports.md +59 -0
- package/docs/ar/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/ar/packages/hono-intlayer/t.md +268 -0
- package/docs/de/intlayer_with_adonisjs.md +392 -0
- package/docs/de/intlayer_with_hono.md +418 -0
- package/docs/de/intlayer_with_vite+preact.md +272 -632
- package/docs/de/packages/adonis-intlayer/exports.md +50 -0
- package/docs/de/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/de/packages/adonis-intlayer/t.md +149 -0
- package/docs/de/packages/hono-intlayer/exports.md +59 -0
- package/docs/de/packages/hono-intlayer/intlayer.md +59 -0
- package/docs/de/packages/hono-intlayer/t.md +316 -0
- package/docs/en/index.md +8 -0
- package/docs/en/intlayer_with_adonisjs.md +388 -0
- package/docs/en/intlayer_with_hono.md +418 -0
- package/docs/en/intlayer_with_vite+preact.md +171 -556
- package/docs/en/introduction.md +1 -0
- package/docs/en/packages/adonis-intlayer/exports.md +50 -0
- package/docs/en/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/en/packages/adonis-intlayer/t.md +149 -0
- package/docs/en/packages/hono-intlayer/exports.md +59 -0
- package/docs/en/packages/hono-intlayer/intlayer.md +59 -0
- package/docs/en/packages/hono-intlayer/t.md +316 -0
- package/docs/en-GB/intlayer_with_adonisjs.md +394 -0
- package/docs/en-GB/intlayer_with_hono.md +418 -0
- package/docs/en-GB/intlayer_with_vite+preact.md +236 -583
- package/docs/en-GB/packages/adonis-intlayer/exports.md +50 -0
- package/docs/en-GB/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/en-GB/packages/adonis-intlayer/t.md +149 -0
- package/docs/en-GB/packages/hono-intlayer/exports.md +59 -0
- package/docs/en-GB/packages/hono-intlayer/intlayer.md +59 -0
- package/docs/en-GB/packages/hono-intlayer/t.md +316 -0
- package/docs/es/intlayer_with_adonisjs.md +388 -0
- package/docs/es/intlayer_with_hono.md +418 -0
- package/docs/es/intlayer_with_vite+preact.md +286 -650
- package/docs/es/packages/adonis-intlayer/exports.md +50 -0
- package/docs/es/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/es/packages/adonis-intlayer/t.md +149 -0
- package/docs/es/packages/hono-intlayer/exports.md +59 -0
- package/docs/es/packages/hono-intlayer/intlayer.md +59 -0
- package/docs/es/packages/hono-intlayer/t.md +316 -0
- package/docs/fr/intlayer_with_adonisjs.md +388 -0
- package/docs/fr/intlayer_with_hono.md +418 -0
- package/docs/fr/intlayer_with_vite+preact.md +274 -614
- package/docs/fr/packages/adonis-intlayer/exports.md +50 -0
- package/docs/fr/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/fr/packages/adonis-intlayer/t.md +149 -0
- package/docs/fr/packages/hono-intlayer/exports.md +59 -0
- package/docs/fr/packages/hono-intlayer/intlayer.md +59 -0
- package/docs/fr/packages/hono-intlayer/t.md +316 -0
- package/docs/hi/intlayer_with_adonisjs.md +394 -0
- package/docs/hi/intlayer_with_hono.md +227 -0
- package/docs/hi/intlayer_with_vite+preact.md +304 -680
- package/docs/hi/packages/adonis-intlayer/exports.md +50 -0
- package/docs/hi/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/hi/packages/adonis-intlayer/t.md +149 -0
- package/docs/hi/packages/hono-intlayer/exports.md +59 -0
- package/docs/hi/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/hi/packages/hono-intlayer/t.md +268 -0
- package/docs/id/intlayer_with_adonisjs.md +394 -0
- package/docs/id/intlayer_with_hono.md +227 -0
- package/docs/id/intlayer_with_vite+preact.md +297 -697
- package/docs/id/packages/adonis-intlayer/exports.md +50 -0
- package/docs/id/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/id/packages/adonis-intlayer/t.md +149 -0
- package/docs/id/packages/hono-intlayer/exports.md +59 -0
- package/docs/id/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/id/packages/hono-intlayer/t.md +268 -0
- package/docs/it/intlayer_with_adonisjs.md +394 -0
- package/docs/it/intlayer_with_hono.md +227 -0
- package/docs/it/intlayer_with_vite+preact.md +290 -659
- package/docs/it/packages/adonis-intlayer/exports.md +50 -0
- package/docs/it/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/it/packages/adonis-intlayer/t.md +149 -0
- package/docs/it/packages/hono-intlayer/exports.md +59 -0
- package/docs/it/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/it/packages/hono-intlayer/t.md +268 -0
- package/docs/ja/intlayer_with_adonisjs.md +394 -0
- package/docs/ja/intlayer_with_hono.md +227 -0
- package/docs/ja/intlayer_with_vite+preact.md +307 -662
- package/docs/ja/packages/adonis-intlayer/exports.md +50 -0
- package/docs/ja/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/ja/packages/adonis-intlayer/t.md +149 -0
- package/docs/ja/packages/hono-intlayer/exports.md +59 -0
- package/docs/ja/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/ja/packages/hono-intlayer/t.md +268 -0
- package/docs/ko/intlayer_with_adonisjs.md +394 -0
- package/docs/ko/intlayer_with_hono.md +227 -0
- package/docs/ko/intlayer_with_vite+preact.md +303 -703
- package/docs/ko/packages/adonis-intlayer/exports.md +50 -0
- package/docs/ko/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/ko/packages/adonis-intlayer/t.md +149 -0
- package/docs/ko/packages/hono-intlayer/exports.md +59 -0
- package/docs/ko/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/ko/packages/hono-intlayer/t.md +268 -0
- package/docs/pl/intlayer_with_adonisjs.md +394 -0
- package/docs/pl/intlayer_with_hono.md +227 -0
- package/docs/pl/intlayer_with_vite+preact.md +289 -690
- package/docs/pl/packages/adonis-intlayer/exports.md +50 -0
- package/docs/pl/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/pl/packages/adonis-intlayer/t.md +149 -0
- package/docs/pl/packages/hono-intlayer/exports.md +59 -0
- package/docs/pl/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/pl/packages/hono-intlayer/t.md +268 -0
- package/docs/pt/intlayer_with_adonisjs.md +394 -0
- package/docs/pt/intlayer_with_hono.md +227 -0
- package/docs/pt/intlayer_with_vite+preact.md +275 -637
- package/docs/pt/packages/adonis-intlayer/exports.md +50 -0
- package/docs/pt/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/pt/packages/adonis-intlayer/t.md +149 -0
- package/docs/pt/packages/hono-intlayer/exports.md +59 -0
- package/docs/pt/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/pt/packages/hono-intlayer/t.md +268 -0
- package/docs/ru/intlayer_with_adonisjs.md +393 -0
- package/docs/ru/intlayer_with_hono.md +223 -0
- package/docs/ru/intlayer_with_vite+preact.md +319 -683
- package/docs/ru/packages/adonis-intlayer/exports.md +50 -0
- package/docs/ru/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/ru/packages/adonis-intlayer/t.md +149 -0
- package/docs/ru/packages/hono-intlayer/exports.md +59 -0
- package/docs/ru/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/ru/packages/hono-intlayer/t.md +268 -0
- package/docs/tr/intlayer_with_adonisjs.md +394 -0
- package/docs/tr/intlayer_with_hono.md +227 -0
- package/docs/tr/intlayer_with_vite+preact.md +332 -665
- package/docs/tr/packages/adonis-intlayer/exports.md +50 -0
- package/docs/tr/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/tr/packages/adonis-intlayer/t.md +149 -0
- package/docs/tr/packages/hono-intlayer/exports.md +59 -0
- package/docs/tr/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/tr/packages/hono-intlayer/t.md +268 -0
- package/docs/uk/intlayer_with_adonisjs.md +394 -0
- package/docs/uk/intlayer_with_hono.md +227 -0
- package/docs/uk/intlayer_with_vite+preact.md +228 -626
- package/docs/uk/packages/adonis-intlayer/exports.md +50 -0
- package/docs/uk/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/uk/packages/adonis-intlayer/t.md +149 -0
- package/docs/uk/packages/hono-intlayer/exports.md +59 -0
- package/docs/uk/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/uk/packages/hono-intlayer/t.md +268 -0
- package/docs/vi/intlayer_with_adonisjs.md +394 -0
- package/docs/vi/intlayer_with_hono.md +227 -0
- package/docs/vi/intlayer_with_vite+preact.md +294 -679
- package/docs/vi/packages/adonis-intlayer/exports.md +50 -0
- package/docs/vi/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/vi/packages/adonis-intlayer/t.md +149 -0
- package/docs/vi/packages/hono-intlayer/exports.md +59 -0
- package/docs/vi/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/vi/packages/hono-intlayer/t.md +268 -0
- package/docs/zh/intlayer_with_adonisjs.md +393 -0
- package/docs/zh/intlayer_with_hono.md +418 -0
- package/docs/zh/intlayer_with_vite+preact.md +338 -743
- package/docs/zh/packages/adonis-intlayer/exports.md +50 -0
- package/docs/zh/packages/adonis-intlayer/intlayer.md +54 -0
- package/docs/zh/packages/adonis-intlayer/t.md +149 -0
- package/docs/zh/packages/hono-intlayer/exports.md +59 -0
- package/docs/zh/packages/hono-intlayer/intlayer.md +60 -0
- package/docs/zh/packages/hono-intlayer/t.md +294 -0
- package/package.json +6 -6
- package/src/generated/docs.entry.ts +160 -0
|
@@ -24,7 +24,7 @@ history:
|
|
|
24
24
|
changes: 初始化历史
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
# 使用Intlayer翻译您的Vite
|
|
27
|
+
# 使用 Intlayer 翻译您的 Vite 和 Preact 网站 | 国际化 (i18n)
|
|
28
28
|
|
|
29
29
|
<Tabs defaultTab="video">
|
|
30
30
|
<Tab label="Video" value="video">
|
|
@@ -45,28 +45,30 @@ history:
|
|
|
45
45
|
</Tab>
|
|
46
46
|
</Tabs>
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
## 目录
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
<TOC/>
|
|
51
51
|
|
|
52
52
|
## 什么是 Intlayer?
|
|
53
53
|
|
|
54
|
-
**Intlayer**
|
|
54
|
+
**Intlayer** 是一个创新的开源国际化 (i18n) 库,旨在简化现代 Web 应用程序的多语言支持。
|
|
55
55
|
|
|
56
56
|
使用 Intlayer,您可以:
|
|
57
57
|
|
|
58
|
-
-
|
|
58
|
+
- **轻松管理翻译**:使用组件级的声明式字典。
|
|
59
59
|
- **动态本地化元数据、路由和内容**。
|
|
60
|
-
- **确保 TypeScript
|
|
61
|
-
-
|
|
60
|
+
- **确保 TypeScript 支持**:通过自动生成的类型,改进自动补全和错误检测。
|
|
61
|
+
- **受益于高级功能**:例如动态语言环境检测和切换。
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
-
## 在 Vite 和 Preact
|
|
65
|
+
## 在 Vite 和 Preact 应用程序中设置 Intlayer 的分步指南
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
请参阅 GitHub 上的 [应用程序模板](https://github.com/aymericzip/intlayer-vite-preact-template)。
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
### 第 1 步:安装依赖项
|
|
70
|
+
|
|
71
|
+
使用 npm 安装必要的包:
|
|
70
72
|
|
|
71
73
|
```bash packageManager="npm"
|
|
72
74
|
npm install intlayer preact-intlayer
|
|
@@ -94,59 +96,19 @@ bunx intlayer init
|
|
|
94
96
|
|
|
95
97
|
- **intlayer**
|
|
96
98
|
|
|
97
|
-
|
|
98
|
-
核心包,提供国际化工具,用于配置管理、翻译、[内容声明](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dictionary/get_started.md)、转译以及[命令行工具](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/intlayer_cli.md)。
|
|
99
|
-
|
|
100
|
-
- **preact-intlayer**
|
|
101
|
-
将 Intlayer 集成到 Preact 应用中的包。它提供了 Preact 国际化的上下文提供者和钩子。
|
|
102
|
-
|
|
103
|
-
- **vite-intlayer**
|
|
104
|
-
包含用于将 Intlayer 集成到[Vite 打包工具](https://vite.dev/guide/why.html#why-bundle-for-production)的 Vite 插件,以及用于检测用户首选语言、管理 Cookie 和处理 URL 重定向的中间件。
|
|
105
|
-
|
|
106
|
-
### 第2步:配置您的项目
|
|
107
|
-
|
|
108
|
-
创建一个配置文件来配置您应用程序的语言:
|
|
109
|
-
|
|
110
|
-
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
111
|
-
import { Locales, type IntlayerConfig } from "intlayer";
|
|
112
|
-
|
|
113
|
-
const config: IntlayerConfig = {
|
|
114
|
-
internationalization: {
|
|
115
|
-
locales: [
|
|
116
|
-
Locales.ENGLISH,
|
|
117
|
-
Locales.FRENCH,
|
|
118
|
-
Locales.SPANISH,
|
|
119
|
-
// 您的其他语言
|
|
120
|
-
],
|
|
121
|
-
defaultLocale: Locales.ENGLISH,
|
|
122
|
-
},
|
|
123
|
-
};
|
|
99
|
+
提供配置管理、翻译、[内容声明](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dictionary/content_file.md)、编译和 [CLI 命令](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/cli/index.md) 的核心包。
|
|
124
100
|
|
|
125
|
-
|
|
126
|
-
```
|
|
101
|
+
- **preact-intlayer**
|
|
127
102
|
|
|
128
|
-
|
|
129
|
-
import { Locales } from "intlayer";
|
|
103
|
+
将 Intlayer 与 Preact 应用程序集成的包。它为 Preact 国际化提供上下文提供者和钩子。
|
|
130
104
|
|
|
131
|
-
|
|
132
|
-
const config = {
|
|
133
|
-
internationalization: {
|
|
134
|
-
locales: [
|
|
135
|
-
Locales.ENGLISH,
|
|
136
|
-
Locales.FRENCH,
|
|
137
|
-
Locales.SPANISH,
|
|
138
|
-
// 您的其他语言
|
|
139
|
-
],
|
|
140
|
-
defaultLocale: Locales.ENGLISH,
|
|
141
|
-
},
|
|
142
|
-
};
|
|
105
|
+
- **vite-intlayer**
|
|
143
106
|
|
|
144
|
-
|
|
145
|
-
```
|
|
107
|
+
包含用于将 Intlayer 与 [Vite 打包器](https://vite.dev/guide/why.html#why-bundle-for-production) 集成的 Vite 插件,以及用于检测用户首选语言环境、管理 cookie 和处理 URL 重定向的中间件。
|
|
146
108
|
|
|
147
|
-
### 第2
|
|
109
|
+
### 第 2 步:配置项目
|
|
148
110
|
|
|
149
|
-
|
|
111
|
+
创建一个配置文件来配置应用程序的语言:
|
|
150
112
|
|
|
151
113
|
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
152
114
|
import { Locales, type IntlayerConfig } from "intlayer";
|
|
@@ -157,10 +119,14 @@ const config: IntlayerConfig = {
|
|
|
157
119
|
Locales.ENGLISH,
|
|
158
120
|
Locales.FRENCH,
|
|
159
121
|
Locales.SPANISH,
|
|
160
|
-
//
|
|
122
|
+
// 您的其他语言环境
|
|
161
123
|
],
|
|
162
124
|
defaultLocale: Locales.ENGLISH,
|
|
163
125
|
},
|
|
126
|
+
routing: {
|
|
127
|
+
mode: "prefix-no-default", // 默认:除默认语言环境外,为所有语言环境添加前缀
|
|
128
|
+
storage: ["cookie", "header"], // 默认:在 cookie 中存储语言环境并从 header 中检测
|
|
129
|
+
},
|
|
164
130
|
};
|
|
165
131
|
|
|
166
132
|
export default config;
|
|
@@ -176,10 +142,14 @@ const config = {
|
|
|
176
142
|
Locales.ENGLISH,
|
|
177
143
|
Locales.FRENCH,
|
|
178
144
|
Locales.SPANISH,
|
|
179
|
-
//
|
|
145
|
+
// 您的其他语言环境
|
|
180
146
|
],
|
|
181
147
|
defaultLocale: Locales.ENGLISH,
|
|
182
148
|
},
|
|
149
|
+
routing: {
|
|
150
|
+
mode: "prefix-no-default", // 默认:除默认语言环境外,为所有语言环境添加前缀
|
|
151
|
+
storage: ["cookie", "header"], // 默认:在 cookie 中存储语言环境并从 header 中检测
|
|
152
|
+
},
|
|
183
153
|
};
|
|
184
154
|
|
|
185
155
|
export default config;
|
|
@@ -195,35 +165,22 @@ const config = {
|
|
|
195
165
|
Locales.ENGLISH,
|
|
196
166
|
Locales.FRENCH,
|
|
197
167
|
Locales.SPANISH,
|
|
198
|
-
//
|
|
168
|
+
// 您的其他语言环境
|
|
199
169
|
],
|
|
200
170
|
defaultLocale: Locales.ENGLISH,
|
|
201
171
|
},
|
|
172
|
+
routing: {
|
|
173
|
+
mode: "prefix-no-default", // 默认:除默认语言环境外,为所有语言环境添加前缀
|
|
174
|
+
storage: ["cookie", "header"], // 默认:在 cookie 中存储语言环境并从 header 中检测
|
|
175
|
+
},
|
|
202
176
|
};
|
|
203
177
|
|
|
204
178
|
module.exports = config;
|
|
205
179
|
```
|
|
206
180
|
|
|
207
|
-
>
|
|
181
|
+
> 通过此配置文件,您可以设置本地化 URL、路由模式、存储选项、cookie 名称、内容声明的位置和扩展名、禁用控制台中的 Intlayer 日志等。有关可用参数的完整列表,请参阅 [配置文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/configuration.md)。
|
|
208
182
|
|
|
209
|
-
### 第3
|
|
210
|
-
|
|
211
|
-
将 intlayer 插件添加到您的配置中。
|
|
212
|
-
|
|
213
|
-
```typescript fileName="vite.config.ts" codeFormat="typescript"
|
|
214
|
-
import { defineConfig } from "vite";
|
|
215
|
-
import preact from "@preact/preset-vite";
|
|
216
|
-
import { intlayer } from "vite-intlayer";
|
|
217
|
-
|
|
218
|
-
// https://vitejs.dev/config/
|
|
219
|
-
export default defineConfig({
|
|
220
|
-
plugins: [preact(), intlayer()],
|
|
221
|
-
});
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
> 通过此配置文件,您可以设置本地化的 URL、中间件重定向、cookie 名称、内容声明的位置和扩展名,禁用控制台中的 Intlayer 日志等。有关可用参数的完整列表,请参阅[配置文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/configuration.md)。
|
|
225
|
-
|
|
226
|
-
### 第3步:在您的 Vite 配置中集成 Intlayer
|
|
183
|
+
### 第 3 步:在 Vite 配置中集成 Intlayer
|
|
227
184
|
|
|
228
185
|
将 intlayer 插件添加到您的配置中。
|
|
229
186
|
|
|
@@ -260,9 +217,9 @@ module.exports = defineConfig({
|
|
|
260
217
|
});
|
|
261
218
|
```
|
|
262
219
|
|
|
263
|
-
> `intlayer()` Vite 插件用于将 Intlayer
|
|
220
|
+
> `intlayer()` Vite 插件用于将 Intlayer 与 Vite 集成。它确保构建内容声明文件并在开发模式下监视它们。它在 Vite 应用程序中定义了 Intlayer 环境变量。此外,它还提供别名以优化性能。
|
|
264
221
|
|
|
265
|
-
### 第4
|
|
222
|
+
### 第 4 步:声明内容
|
|
266
223
|
|
|
267
224
|
创建并管理您的内容声明以存储翻译:
|
|
268
225
|
|
|
@@ -287,18 +244,12 @@ const appContent = {
|
|
|
287
244
|
title: "Vite + Preact",
|
|
288
245
|
|
|
289
246
|
count: t({
|
|
290
|
-
zh: "计数是 ",
|
|
291
247
|
en: "count is ",
|
|
292
248
|
fr: "le compte est ",
|
|
293
249
|
es: "el recuento es ",
|
|
294
250
|
}),
|
|
295
251
|
|
|
296
252
|
edit: t<ComponentChildren>({
|
|
297
|
-
zh: (
|
|
298
|
-
<>
|
|
299
|
-
编辑 <code>src/app.tsx</code> 并保存以测试 HMR
|
|
300
|
-
</>
|
|
301
|
-
),
|
|
302
253
|
en: (
|
|
303
254
|
<>
|
|
304
255
|
Edit <code>src/app.tsx</code> and save to test HMR
|
|
@@ -317,7 +268,6 @@ const appContent = {
|
|
|
317
268
|
}),
|
|
318
269
|
|
|
319
270
|
readTheDocs: t({
|
|
320
|
-
zh: "点击 Vite 和 Preact 标志以了解更多信息",
|
|
321
271
|
en: "Click on the Vite and Preact logos to learn more",
|
|
322
272
|
fr: "Cliquez sur les logos Vite et Preact pour en savoir plus",
|
|
323
273
|
es: "Haga clic en los logotipos de Vite y Preact para obtener más información",
|
|
@@ -330,7 +280,7 @@ export default appContent;
|
|
|
330
280
|
|
|
331
281
|
```javascript fileName="src/app.content.mjs" contentDeclarationFormat="esm"
|
|
332
282
|
import { t } from "intlayer";
|
|
333
|
-
// import { h } from 'preact'; //
|
|
283
|
+
// import { h } from 'preact'; // 如果直接在 .mjs 中使用 JSX,则需要此行
|
|
334
284
|
|
|
335
285
|
/** @type {import('intlayer').Dictionary} */
|
|
336
286
|
const appContent = {
|
|
@@ -362,7 +312,7 @@ const appContent = {
|
|
|
362
312
|
}),
|
|
363
313
|
|
|
364
314
|
readTheDocs: t({
|
|
365
|
-
en: "
|
|
315
|
+
en: "Click on the Vite and Preact logos to learn more",
|
|
366
316
|
fr: "Cliquez sur les logos Vite et Preact pour en savoir plus",
|
|
367
317
|
es: "Haga clic en los logotipos de Vite y Preact para obtener más información",
|
|
368
318
|
}),
|
|
@@ -374,19 +324,19 @@ export default appContent;
|
|
|
374
324
|
|
|
375
325
|
```javascript fileName="src/app.content.cjs" contentDeclarationFormat="commonjs"
|
|
376
326
|
const { t } = require("intlayer");
|
|
377
|
-
// const { h } = require('preact'); //
|
|
327
|
+
// const { h } = require('preact'); // 如果直接在 .cjs 中使用 JSX,则需要此行
|
|
378
328
|
|
|
379
329
|
/** @type {import('intlayer').Dictionary} */
|
|
380
330
|
const appContent = {
|
|
381
331
|
key: "app",
|
|
382
332
|
content: {
|
|
383
333
|
viteLogo: t({
|
|
384
|
-
en: "Vite
|
|
334
|
+
en: "Vite logo",
|
|
385
335
|
fr: "Logo Vite",
|
|
386
336
|
es: "Logo Vite",
|
|
387
337
|
}),
|
|
388
338
|
preactLogo: t({
|
|
389
|
-
en: "Preact
|
|
339
|
+
en: "Preact logo",
|
|
390
340
|
fr: "Logo Preact",
|
|
391
341
|
es: "Logo Preact",
|
|
392
342
|
}),
|
|
@@ -397,21 +347,18 @@ const appContent = {
|
|
|
397
347
|
en: "count is ",
|
|
398
348
|
fr: "le compte est ",
|
|
399
349
|
es: "el recuento es ",
|
|
400
|
-
zh: "计数是 ",
|
|
401
350
|
}),
|
|
402
351
|
|
|
403
352
|
edit: t({
|
|
404
353
|
en: "Edit src/app.tsx and save to test HMR",
|
|
405
354
|
fr: "Éditez src/app.tsx et enregistrez pour tester HMR",
|
|
406
355
|
es: "Edita src/app.tsx y guarda para probar HMR",
|
|
407
|
-
zh: "编辑 src/app.tsx 并保存以测试 HMR",
|
|
408
356
|
}),
|
|
409
357
|
|
|
410
358
|
readTheDocs: t({
|
|
411
359
|
en: "Click on the Vite and Preact logos to learn more",
|
|
412
360
|
fr: "Cliquez sur les logos Vite et Preact pour en savoir plus",
|
|
413
|
-
es: "Haga clic en los logotipos de Vite
|
|
414
|
-
zh: "点击 Vite 和 Preact 标志了解更多信息",
|
|
361
|
+
es: "Haga clic en los logotipos de Vite et Preact pour en savoir plus",
|
|
415
362
|
}),
|
|
416
363
|
},
|
|
417
364
|
};
|
|
@@ -427,7 +374,6 @@ module.exports = appContent;
|
|
|
427
374
|
"viteLogo": {
|
|
428
375
|
"nodeType": "translation",
|
|
429
376
|
"translation": {
|
|
430
|
-
"zh": "Vite 标志",
|
|
431
377
|
"en": "Vite logo",
|
|
432
378
|
"fr": "Logo Vite",
|
|
433
379
|
"es": "Logo Vite"
|
|
@@ -436,7 +382,6 @@ module.exports = appContent;
|
|
|
436
382
|
"preactLogo": {
|
|
437
383
|
"nodeType": "translation",
|
|
438
384
|
"translation": {
|
|
439
|
-
"zh": "Preact 标志",
|
|
440
385
|
"en": "Preact logo",
|
|
441
386
|
"fr": "Logo Preact",
|
|
442
387
|
"es": "Logo Preact"
|
|
@@ -445,7 +390,6 @@ module.exports = appContent;
|
|
|
445
390
|
"title": {
|
|
446
391
|
"nodeType": "translation",
|
|
447
392
|
"translation": {
|
|
448
|
-
"zh": "Vite + Preact",
|
|
449
393
|
"en": "Vite + Preact",
|
|
450
394
|
"fr": "Vite + Preact",
|
|
451
395
|
"es": "Vite + Preact"
|
|
@@ -454,7 +398,6 @@ module.exports = appContent;
|
|
|
454
398
|
"count": {
|
|
455
399
|
"nodeType": "translation",
|
|
456
400
|
"translation": {
|
|
457
|
-
"zh": "计数是 ",
|
|
458
401
|
"en": "count is ",
|
|
459
402
|
"fr": "le compte est ",
|
|
460
403
|
"es": "el recuento es "
|
|
@@ -463,7 +406,7 @@ module.exports = appContent;
|
|
|
463
406
|
"edit": {
|
|
464
407
|
"nodeType": "translation",
|
|
465
408
|
"translation": {
|
|
466
|
-
"en": "
|
|
409
|
+
"en": "Edit src/app.tsx and save to test HMR",
|
|
467
410
|
"fr": "Éditez src/app.tsx et enregistrez pour tester HMR",
|
|
468
411
|
"es": "Edita src/app.tsx y guarda para probar HMR"
|
|
469
412
|
}
|
|
@@ -471,7 +414,7 @@ module.exports = appContent;
|
|
|
471
414
|
"readTheDocs": {
|
|
472
415
|
"nodeType": "translation",
|
|
473
416
|
"translation": {
|
|
474
|
-
"en": "
|
|
417
|
+
"en": "Click on the Vite and Preact logos to learn more",
|
|
475
418
|
"fr": "Cliquez sur les logos Vite et Preact pour en savoir plus",
|
|
476
419
|
"es": "Haga clic en los logotipos de Vite y Preact para obtener más información"
|
|
477
420
|
}
|
|
@@ -480,22 +423,22 @@ module.exports = appContent;
|
|
|
480
423
|
}
|
|
481
424
|
```
|
|
482
425
|
|
|
483
|
-
>
|
|
426
|
+
> 只要您的内容声明包含在 `contentDir` 目录(默认情况下为 `./src`)中,就可以在应用程序的任何位置定义它们。并且需匹配内容声明文件扩展名(默认情况下为 `.content.{json,ts,tsx,js,jsx,mjs,cjs}`)。
|
|
484
427
|
|
|
485
|
-
>
|
|
428
|
+
> 有关更多详细信息,请参考 [内容声明文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dictionary/content_file.md)。
|
|
486
429
|
|
|
487
|
-
> 如果您的内容文件包含 TSX 代码,您可能需要导入 `import { h } from "preact"
|
|
430
|
+
> 如果您的内容文件包含 TSX 代码,您可能需要导入 `import { h } from "preact";` 或确保正确设置 Preact 的 JSX pragma。
|
|
488
431
|
|
|
489
|
-
### 第5步:在代码中使用 Intlayer
|
|
432
|
+
### 第 5 步:在代码中使用 Intlayer
|
|
490
433
|
|
|
491
434
|
在整个应用程序中访问您的内容字典:
|
|
492
435
|
|
|
493
436
|
```tsx {6,10} fileName="src/app.tsx" codeFormat="typescript"
|
|
494
437
|
import { useState } from "preact/hooks";
|
|
495
438
|
import type { FunctionalComponent } from "preact";
|
|
496
|
-
import preactLogo from "./assets/preact.svg"; //
|
|
439
|
+
import preactLogo from "./assets/preact.svg"; // 假设您有 preact.svg
|
|
497
440
|
import viteLogo from "/vite.svg";
|
|
498
|
-
import "./app.css"; //
|
|
441
|
+
import "./app.css"; // 假设您的 CSS 文件名为 app.css
|
|
499
442
|
import { IntlayerProvider, useIntlayer } from "preact-intlayer";
|
|
500
443
|
|
|
501
444
|
const AppContent: FunctionalComponent = () => {
|
|
@@ -524,6 +467,12 @@ const AppContent: FunctionalComponent = () => {
|
|
|
524
467
|
</button>
|
|
525
468
|
<p>{content.edit}</p>
|
|
526
469
|
</div>
|
|
470
|
+
{/* Markdown 内容 */}
|
|
471
|
+
<div>{content.myMarkdownContent}</div>
|
|
472
|
+
|
|
473
|
+
{/* HTML 内容 */}
|
|
474
|
+
<div>{content.myHtmlContent}</div>
|
|
475
|
+
|
|
527
476
|
<p class="read-the-docs">{content.readTheDocs}</p>
|
|
528
477
|
</>
|
|
529
478
|
);
|
|
@@ -632,7 +581,7 @@ const App = () => (
|
|
|
632
581
|
module.exports = App;
|
|
633
582
|
```
|
|
634
583
|
|
|
635
|
-
>
|
|
584
|
+
> 如果您想在字符串属性中使用您的内容,例如 `alt`、`title`、`href`、`aria-label` 等,您必须调用该函数的值,例如:
|
|
636
585
|
|
|
637
586
|
> ```jsx
|
|
638
587
|
> <img src={content.image.src.value} alt={content.image.value} />
|
|
@@ -640,11 +589,11 @@ module.exports = App;
|
|
|
640
589
|
|
|
641
590
|
> 注意:在 Preact 中,`className` 通常写作 `class`。
|
|
642
591
|
|
|
643
|
-
>
|
|
592
|
+
> 要了解有关 `useIntlayer` 钩子的更多信息,请参考 [文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/react-intlayer/useIntlayer.md)(`preact-intlayer` 的 API 类似)。
|
|
644
593
|
|
|
645
|
-
###
|
|
594
|
+
### (可选)第 6 步:更改内容语言
|
|
646
595
|
|
|
647
|
-
|
|
596
|
+
要更改内容的语言,您可以使用 `useLocale` 钩子提供的 `setLocale` 函数。此函数允许您设置应用程序的语言环境并相应地更新内容。
|
|
648
597
|
|
|
649
598
|
```tsx fileName="src/components/LocaleSwitcher.tsx" codeFormat="typescript"
|
|
650
599
|
import type { FunctionalComponent } from "preact";
|
|
@@ -655,7 +604,9 @@ const LocaleSwitcher: FunctionalComponent = () => {
|
|
|
655
604
|
const { setLocale } = useLocale();
|
|
656
605
|
|
|
657
606
|
return (
|
|
658
|
-
<button onClick={() => setLocale(Locales.ENGLISH)}
|
|
607
|
+
<button onClick={() => setLocale(Locales.ENGLISH)}>
|
|
608
|
+
Change Language to English
|
|
609
|
+
</button>
|
|
659
610
|
);
|
|
660
611
|
};
|
|
661
612
|
|
|
@@ -670,7 +621,9 @@ const LocaleSwitcher = () => {
|
|
|
670
621
|
const { setLocale } = useLocale();
|
|
671
622
|
|
|
672
623
|
return (
|
|
673
|
-
<button onClick={() => setLocale(Locales.ENGLISH)}
|
|
624
|
+
<button onClick={() => setLocale(Locales.ENGLISH)}>
|
|
625
|
+
Change Language to English
|
|
626
|
+
</button>
|
|
674
627
|
);
|
|
675
628
|
};
|
|
676
629
|
|
|
@@ -685,18 +638,20 @@ const LocaleSwitcher = () => {
|
|
|
685
638
|
const { setLocale } = useLocale();
|
|
686
639
|
|
|
687
640
|
return (
|
|
688
|
-
<button onClick={() => setLocale(Locales.ENGLISH)}
|
|
641
|
+
<button onClick={() => setLocale(Locales.ENGLISH)}>
|
|
642
|
+
Change Language to English
|
|
643
|
+
</button>
|
|
689
644
|
);
|
|
690
645
|
};
|
|
691
646
|
|
|
692
647
|
module.exports = LocaleSwitcher;
|
|
693
648
|
```
|
|
694
649
|
|
|
695
|
-
>
|
|
650
|
+
> 要了解有关 `useLocale` 钩子的更多信息,请参考 [文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/react-intlayer/useLocale.md)(`preact-intlayer` 的 API 类似)。
|
|
696
651
|
|
|
697
|
-
### (可选)步骤7
|
|
652
|
+
### (可选)步骤 7:为您的应用程序添加本地化路由
|
|
698
653
|
|
|
699
|
-
|
|
654
|
+
此步骤的目的是为每种语言设置唯一的路由。这对于 SEO 和 SEO 友好的 URL 非常有用。
|
|
700
655
|
示例:
|
|
701
656
|
|
|
702
657
|
```plaintext
|
|
@@ -705,410 +660,107 @@ module.exports = LocaleSwitcher;
|
|
|
705
660
|
- https://example.com/fr/about
|
|
706
661
|
```
|
|
707
662
|
|
|
708
|
-
>
|
|
709
|
-
|
|
710
|
-
要为您的应用添加本地化路由,您可以创建一个 `LocaleRouter` 组件来包裹应用的路由,并处理基于语言的路由。以下是使用 [preact-iso](https://github.com/preactjs/preact-iso) 的示例:
|
|
711
|
-
|
|
712
|
-
首先,安装 `preact-iso`:
|
|
663
|
+
> 默认情况下,默认语言环境的路由不带前缀。如果您想为默认语言环境添加前缀,可以在配置中将 `routing.mode` 选项设置为 `"prefix-all"`。有关更多信息,请参阅 [配置文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/configuration.md)。
|
|
713
664
|
|
|
714
|
-
|
|
715
|
-
npm install preact-iso
|
|
716
|
-
npx intlayer init
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
```bash packageManager="pnpm"
|
|
720
|
-
pnpm add preact-iso
|
|
721
|
-
pnpm intlayer init
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
```bash packageManager="yarn"
|
|
725
|
-
yarn add preact-iso
|
|
726
|
-
```
|
|
665
|
+
要为您的应用程序添加本地化路由,您可以创建一个 `LocaleRouter` 组件,该组件包裹您的应用程序路由并处理基于语言环境的路由。以下是使用 [preact-iso](https://github.com/preactjs/preact-iso) 的示例:
|
|
727
666
|
|
|
728
667
|
```tsx fileName="src/components/LocaleRouter.tsx" codeFormat="typescript"
|
|
729
|
-
import {
|
|
730
|
-
import { ComponentChildren, FunctionalComponent } from "preact";
|
|
668
|
+
import { localeMap } from "intlayer";
|
|
731
669
|
import { IntlayerProvider } from "preact-intlayer";
|
|
732
|
-
import { LocationProvider,
|
|
733
|
-
import {
|
|
734
|
-
|
|
735
|
-
const { internationalization, middleware } = configuration;
|
|
736
|
-
const { locales, defaultLocale } = internationalization;
|
|
737
|
-
|
|
738
|
-
const Navigate: FunctionalComponent<{ to: string; replace?: boolean }> = ({
|
|
739
|
-
to,
|
|
740
|
-
replace,
|
|
741
|
-
}) => {
|
|
742
|
-
const { route } = useLocation();
|
|
743
|
-
useEffect(() => {
|
|
744
|
-
route(to, replace);
|
|
745
|
-
}, [to, replace, route]);
|
|
746
|
-
return null;
|
|
747
|
-
};
|
|
748
|
-
|
|
749
|
-
/**
|
|
750
|
-
* 一个处理本地化并用适当的语言环境上下文包裹子组件的组件。
|
|
751
|
-
* 它管理基于 URL 的语言环境检测和验证。
|
|
752
|
-
*/
|
|
753
|
-
/**
|
|
754
|
-
* 一个处理本地化的组件,使用适当的语言环境上下文包裹子组件。
|
|
755
|
-
* 它管理基于 URL 的语言环境检测和验证。
|
|
756
|
-
*/
|
|
757
|
-
const AppLocalized: FunctionalComponent<{
|
|
758
|
-
children: ComponentChildren;
|
|
759
|
-
locale?: Locales;
|
|
760
|
-
}> = ({ children, locale }) => {
|
|
761
|
-
const { path: pathname, url } = useLocation();
|
|
762
|
-
|
|
763
|
-
if (!url) {
|
|
764
|
-
return null;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
const search = url.substring(pathname.length);
|
|
768
|
-
|
|
769
|
-
// 确定当前语言环境,如果未提供则回退到默认语言环境
|
|
770
|
-
const currentLocale = locale ?? defaultLocale;
|
|
771
|
-
|
|
772
|
-
// 从路径中移除语言环境前缀以构造基础路径
|
|
773
|
-
const pathWithoutLocale = getPathWithoutLocale(
|
|
774
|
-
pathname // 当前 URL 路径
|
|
775
|
-
);
|
|
776
|
-
|
|
777
|
-
/**
|
|
778
|
-
* 如果 middleware.prefixDefault 为 true,则默认语言应始终带有前缀。
|
|
779
|
-
*/
|
|
780
|
-
if (middleware.prefixDefault) {
|
|
781
|
-
// 验证语言
|
|
782
|
-
if (!locale || !locales.includes(locale)) {
|
|
783
|
-
// 重定向到带有更新路径的默认语言
|
|
784
|
-
return (
|
|
785
|
-
<Navigate
|
|
786
|
-
to={`/${defaultLocale}/${pathWithoutLocale}${search}`}
|
|
787
|
-
replace // 用新条目替换当前历史记录
|
|
788
|
-
/>
|
|
789
|
-
);
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
// 使用 IntlayerProvider 包裹子组件并设置当前语言
|
|
793
|
-
return (
|
|
794
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
795
|
-
);
|
|
796
|
-
} else {
|
|
797
|
-
/**
|
|
798
|
-
* 当 middleware.prefixDefault 为 false 时,默认语言不带前缀。
|
|
799
|
-
* 确保当前语言环境有效且不是默认语言环境。
|
|
800
|
-
*/
|
|
801
|
-
if (
|
|
802
|
-
currentLocale.toString() !== defaultLocale.toString() &&
|
|
803
|
-
!locales
|
|
804
|
-
.filter(
|
|
805
|
-
(loc) => loc.toString() !== defaultLocale.toString() // 排除默认语言环境
|
|
806
|
-
)
|
|
807
|
-
.includes(currentLocale) // 检查当前语言环境是否在有效语言环境列表中
|
|
808
|
-
) {
|
|
809
|
-
// 重定向到无语言环境前缀的路径
|
|
810
|
-
return <Navigate to={`${pathWithoutLocale}${search}`} replace />;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// 使用 IntlayerProvider 包裹子组件并设置当前语言环境
|
|
814
|
-
return (
|
|
815
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
816
|
-
);
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
|
-
|
|
820
|
-
const RouterContent: FunctionalComponent<{
|
|
821
|
-
children: ComponentChildren;
|
|
822
|
-
}> = ({ children }) => {
|
|
823
|
-
const { path } = useLocation();
|
|
824
|
-
|
|
825
|
-
if (!path) {
|
|
826
|
-
return null;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
const pathLocale = path.split("/")[1] as Locales;
|
|
830
|
-
|
|
831
|
-
const isLocaleRoute = locales
|
|
832
|
-
.filter((locale) => middleware.prefixDefault || locale !== defaultLocale)
|
|
833
|
-
.some((locale) => locale.toString() === pathLocale);
|
|
834
|
-
|
|
835
|
-
if (isLocaleRoute) {
|
|
836
|
-
return <AppLocalized locale={pathLocale}>{children}</AppLocalized>;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
return (
|
|
840
|
-
<AppLocalized
|
|
841
|
-
locale={!middleware.prefixDefault ? defaultLocale : undefined}
|
|
842
|
-
>
|
|
843
|
-
{children}
|
|
844
|
-
</AppLocalized>
|
|
845
|
-
);
|
|
846
|
-
};
|
|
670
|
+
import { LocationProvider, Router, Route } from "preact-iso";
|
|
671
|
+
import type { ComponentChildren, FunctionalComponent } from "preact";
|
|
847
672
|
|
|
848
673
|
/**
|
|
849
|
-
*
|
|
850
|
-
* 它使用 preact-iso
|
|
674
|
+
* 设置特定语言环境路由的路由组件。
|
|
675
|
+
* 它使用 preact-iso 管理导航并渲染本地化组件。
|
|
851
676
|
*/
|
|
852
677
|
export const LocaleRouter: FunctionalComponent<{
|
|
853
678
|
children: ComponentChildren;
|
|
854
679
|
}> = ({ children }) => (
|
|
855
680
|
<LocationProvider>
|
|
856
|
-
<
|
|
681
|
+
<Router>
|
|
682
|
+
{localeMap(({ locale, urlPrefix }) => ({ locale, urlPrefix }))
|
|
683
|
+
.sort((a, b) => b.urlPrefix.length - a.urlPrefix.length)
|
|
684
|
+
.map(({ locale, urlPrefix }) => (
|
|
685
|
+
<Route
|
|
686
|
+
key={locale}
|
|
687
|
+
path={`${urlPrefix}/:rest*`}
|
|
688
|
+
component={() => (
|
|
689
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
690
|
+
)}
|
|
691
|
+
/>
|
|
692
|
+
))}
|
|
693
|
+
</Router>
|
|
857
694
|
</LocationProvider>
|
|
858
695
|
);
|
|
859
696
|
```
|
|
860
697
|
|
|
861
698
|
```jsx fileName="src/components/LocaleRouter.jsx" codeFormat="esm"
|
|
862
|
-
|
|
863
|
-
import { configuration, getPathWithoutLocale } from "intlayer";
|
|
699
|
+
import { localeMap } from "intlayer";
|
|
864
700
|
import { IntlayerProvider } from "preact-intlayer";
|
|
865
|
-
import { LocationProvider,
|
|
866
|
-
import { useEffect } from "preact/hooks";
|
|
867
|
-
import { h } from "preact"; // JSX 所需
|
|
868
|
-
|
|
869
|
-
// 从 Intlayer 中解构配置
|
|
870
|
-
const { internationalization, middleware } = configuration;
|
|
871
|
-
const { locales, defaultLocale } = internationalization;
|
|
872
|
-
|
|
873
|
-
const Navigate = ({ to, replace }) => {
|
|
874
|
-
const { route } = useLocation();
|
|
875
|
-
useEffect(() => {
|
|
876
|
-
route(to, replace);
|
|
877
|
-
}, [to, replace, route]);
|
|
878
|
-
return null;
|
|
879
|
-
};
|
|
701
|
+
import { LocationProvider, Router, Route } from "preact-iso";
|
|
880
702
|
|
|
881
703
|
/**
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*/
|
|
885
|
-
const AppLocalized = ({ children, locale }) => {
|
|
886
|
-
const { path: pathname, url } = useLocation();
|
|
887
|
-
|
|
888
|
-
if (!url) {
|
|
889
|
-
return null;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
const search = url.substring(pathname.length);
|
|
893
|
-
|
|
894
|
-
// 确定当前语言环境,如果未提供则回退到默认语言
|
|
895
|
-
const currentLocale = locale ?? defaultLocale;
|
|
896
|
-
|
|
897
|
-
// 从路径中移除语言前缀以构造基础路径
|
|
898
|
-
const pathWithoutLocale = getPathWithoutLocale(
|
|
899
|
-
pathname // 当前 URL 路径
|
|
900
|
-
);
|
|
901
|
-
|
|
902
|
-
/**
|
|
903
|
-
* 如果 middleware.prefixDefault 为 true,默认语言应始终带有前缀。
|
|
904
|
-
*/
|
|
905
|
-
if (middleware.prefixDefault) {
|
|
906
|
-
// 验证语言环境
|
|
907
|
-
if (!locale || !locales.includes(locale)) {
|
|
908
|
-
// 重定向到带有更新路径的默认语言环境
|
|
909
|
-
return (
|
|
910
|
-
<Navigate
|
|
911
|
-
to={`/${defaultLocale}/${pathWithoutLocale}${search}`}
|
|
912
|
-
replace // 用新的历史记录条目替换当前条目
|
|
913
|
-
/>
|
|
914
|
-
);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
// 使用 IntlayerProvider 包裹子组件并设置当前语言环境
|
|
918
|
-
return (
|
|
919
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
920
|
-
);
|
|
921
|
-
} else {
|
|
922
|
-
/**
|
|
923
|
-
* 当 middleware.prefixDefault 为 false 时,默认语言环境不带前缀。
|
|
924
|
-
* 确保当前语言环境有效且不是默认语言环境。
|
|
925
|
-
*/
|
|
926
|
-
if (
|
|
927
|
-
currentLocale.toString() !== defaultLocale.toString() &&
|
|
928
|
-
!locales
|
|
929
|
-
.filter(
|
|
930
|
-
(loc) => loc.toString() !== defaultLocale.toString() // 排除默认语言环境
|
|
931
|
-
)
|
|
932
|
-
.includes(currentLocale) // 检查当前语言环境是否在有效语言环境列表中
|
|
933
|
-
) {
|
|
934
|
-
// 重定向到不带语言环境前缀的路径
|
|
935
|
-
return <Navigate to={`${pathWithoutLocale}${search}`} replace />;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
// 使用 IntlayerProvider 包裹子组件并设置当前语言环境
|
|
939
|
-
return (
|
|
940
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
941
|
-
);
|
|
942
|
-
}
|
|
943
|
-
};
|
|
944
|
-
|
|
945
|
-
const RouterContent = ({ children }) => {
|
|
946
|
-
const { path } = useLocation();
|
|
947
|
-
|
|
948
|
-
if (!path) {
|
|
949
|
-
return null;
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
const pathLocale = path.split("/")[1];
|
|
953
|
-
|
|
954
|
-
const isLocaleRoute = locales
|
|
955
|
-
.filter((locale) => middleware.prefixDefault || locale !== defaultLocale)
|
|
956
|
-
.some((locale) => locale.toString() === pathLocale);
|
|
957
|
-
|
|
958
|
-
if (isLocaleRoute) {
|
|
959
|
-
return <AppLocalized locale={pathLocale}>{children}</AppLocalized>;
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
return (
|
|
963
|
-
<AppLocalized
|
|
964
|
-
locale={!middleware.prefixDefault ? defaultLocale : undefined}
|
|
965
|
-
>
|
|
966
|
-
{children}
|
|
967
|
-
</AppLocalized>
|
|
968
|
-
);
|
|
969
|
-
};
|
|
970
|
-
|
|
971
|
-
/**
|
|
972
|
-
* 一个设置特定语言环境路由的路由组件。
|
|
973
|
-
* 它使用 preact-iso 来管理导航并渲染本地化组件。
|
|
704
|
+
* 设置特定语言环境路由的路由组件。
|
|
705
|
+
* 它使用 preact-iso 管理导航并渲染本地化组件。
|
|
974
706
|
*/
|
|
975
707
|
export const LocaleRouter = ({ children }) => (
|
|
976
708
|
<LocationProvider>
|
|
977
|
-
<
|
|
709
|
+
<Router>
|
|
710
|
+
{localeMap(({ locale, urlPrefix }) => ({ locale, urlPrefix }))
|
|
711
|
+
.sort((a, b) => b.urlPrefix.length - a.urlPrefix.length)
|
|
712
|
+
.map(({ locale, urlPrefix }) => (
|
|
713
|
+
<Route
|
|
714
|
+
key={locale}
|
|
715
|
+
path={`${urlPrefix}/:rest*`}
|
|
716
|
+
component={() => (
|
|
717
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
718
|
+
)}
|
|
719
|
+
/>
|
|
720
|
+
))}
|
|
721
|
+
</Router>
|
|
978
722
|
</LocationProvider>
|
|
979
723
|
);
|
|
980
724
|
```
|
|
981
725
|
|
|
982
726
|
```jsx fileName="src/components/LocaleRouter.cjsx" codeFormat="commonjs"
|
|
983
|
-
|
|
984
|
-
const { configuration, getPathWithoutLocale } = require("intlayer");
|
|
727
|
+
const { localeMap } = require("intlayer");
|
|
985
728
|
const { IntlayerProvider } = require("preact-intlayer");
|
|
986
|
-
const { LocationProvider,
|
|
987
|
-
const { useEffect } = require("preact/hooks");
|
|
988
|
-
const { h } = require("preact"); // JSX所需
|
|
989
|
-
|
|
990
|
-
// 从Intlayer中解构配置
|
|
991
|
-
const { internationalization, middleware } = configuration;
|
|
992
|
-
const { locales, defaultLocale } = internationalization;
|
|
993
|
-
|
|
994
|
-
const Navigate = ({ to, replace }) => {
|
|
995
|
-
const { route } = useLocation();
|
|
996
|
-
useEffect(() => {
|
|
997
|
-
route(to, replace);
|
|
998
|
-
}, [to, replace, route]);
|
|
999
|
-
return null;
|
|
1000
|
-
};
|
|
729
|
+
const { LocationProvider, Router, Route } = require("preact-iso");
|
|
1001
730
|
|
|
1002
731
|
/**
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
732
|
+
* 设置特定语言环境路由的路由组件。
|
|
733
|
+
* 它使用 preact-iso 管理导航并渲染本地化组件。
|
|
1005
734
|
*/
|
|
1006
|
-
const
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
);
|
|
1022
|
-
|
|
1023
|
-
/**
|
|
1024
|
-
* 如果 middleware.prefixDefault 为 true,则默认语言环境应始终带有前缀。
|
|
1025
|
-
*/
|
|
1026
|
-
if (middleware.prefixDefault) {
|
|
1027
|
-
// 验证语言环境
|
|
1028
|
-
if (!locale || !locales.includes(locale)) {
|
|
1029
|
-
// 重定向到带有更新路径的默认语言环境
|
|
1030
|
-
return (
|
|
1031
|
-
<Navigate
|
|
1032
|
-
to={`/${defaultLocale}/${pathWithoutLocale}${search}`}
|
|
1033
|
-
replace // 替换当前历史记录条目为新的条目
|
|
1034
|
-
/>
|
|
1035
|
-
);
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
// 使用 IntlayerProvider 包裹子组件并设置当前语言环境
|
|
1039
|
-
return (
|
|
1040
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
1041
|
-
);
|
|
1042
|
-
} else {
|
|
1043
|
-
/**
|
|
1044
|
-
* 当 middleware.prefixDefault 为 false 时,默认语言环境不带前缀。
|
|
1045
|
-
* 确保当前语言环境有效且不是默认语言环境。
|
|
1046
|
-
*/
|
|
1047
|
-
if (
|
|
1048
|
-
currentLocale.toString() !== defaultLocale.toString() &&
|
|
1049
|
-
!locales
|
|
1050
|
-
.filter(
|
|
1051
|
-
(loc) => loc.toString() !== defaultLocale.toString() // 排除默认语言环境
|
|
735
|
+
const LocaleRouter = ({ children }) =>
|
|
736
|
+
h(
|
|
737
|
+
LocationProvider,
|
|
738
|
+
{},
|
|
739
|
+
h(
|
|
740
|
+
Router,
|
|
741
|
+
{},
|
|
742
|
+
localeMap(({ locale, urlPrefix }) => ({ locale, urlPrefix }))
|
|
743
|
+
.sort((a, b) => b.urlPrefix.length - a.urlPrefix.length)
|
|
744
|
+
.map(({ locale, urlPrefix }) =>
|
|
745
|
+
h(Route, {
|
|
746
|
+
key: locale,
|
|
747
|
+
path: `${urlPrefix}/:rest*`,
|
|
748
|
+
component: () => h(IntlayerProvider, { locale }, children),
|
|
749
|
+
})
|
|
1052
750
|
)
|
|
1053
|
-
|
|
1054
|
-
) {
|
|
1055
|
-
// 重定向到无语言环境前缀的路径
|
|
1056
|
-
return <Navigate to={`${pathWithoutLocale}${search}`} replace />;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// 使用 IntlayerProvider 包裹子组件并设置当前语言环境
|
|
1060
|
-
return (
|
|
1061
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
1062
|
-
);
|
|
1063
|
-
}
|
|
1064
|
-
};
|
|
1065
|
-
|
|
1066
|
-
const RouterContent = ({ children }) => {
|
|
1067
|
-
const { path } = useLocation();
|
|
1068
|
-
|
|
1069
|
-
if (!path) {
|
|
1070
|
-
return null;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
const pathLocale = path.split("/")[1];
|
|
1074
|
-
|
|
1075
|
-
const isLocaleRoute = locales
|
|
1076
|
-
.filter((locale) => middleware.prefixDefault || locale !== defaultLocale)
|
|
1077
|
-
.some((locale) => locale.toString() === pathLocale);
|
|
1078
|
-
|
|
1079
|
-
if (isLocaleRoute) {
|
|
1080
|
-
return <AppLocalized locale={pathLocale}>{children}</AppLocalized>;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
return (
|
|
1084
|
-
<AppLocalized
|
|
1085
|
-
locale={!middleware.prefixDefault ? defaultLocale : undefined}
|
|
1086
|
-
>
|
|
1087
|
-
{children}
|
|
1088
|
-
</AppLocalized>
|
|
751
|
+
)
|
|
1089
752
|
);
|
|
1090
|
-
};
|
|
1091
|
-
|
|
1092
|
-
/**
|
|
1093
|
-
* 一个设置特定语言环境路由的路由组件。
|
|
1094
|
-
* 它使用 preact-iso 来管理导航并渲染本地化组件。
|
|
1095
|
-
*/
|
|
1096
|
-
const LocaleRouter = ({ children }) => (
|
|
1097
|
-
<LocationProvider>
|
|
1098
|
-
<RouterContent>{children}</RouterContent>
|
|
1099
|
-
</LocationProvider>
|
|
1100
|
-
);
|
|
1101
753
|
|
|
1102
754
|
module.exports = { LocaleRouter };
|
|
1103
755
|
```
|
|
1104
756
|
|
|
1105
|
-
|
|
757
|
+
然后,您可以在应用程序中使用 `LocaleRouter` 组件:
|
|
1106
758
|
|
|
1107
759
|
```tsx fileName="src/app.tsx" codeFormat="typescript"
|
|
1108
760
|
import { LocaleRouter } from "./components/LocaleRouter";
|
|
1109
761
|
import type { FunctionalComponent } from "preact";
|
|
1110
|
-
|
|
1111
|
-
// ...
|
|
762
|
+
|
|
763
|
+
// ... 您的 AppContent 组件
|
|
1112
764
|
|
|
1113
765
|
const App: FunctionalComponent = () => (
|
|
1114
766
|
<LocaleRouter>
|
|
@@ -1121,7 +773,8 @@ export default App;
|
|
|
1121
773
|
|
|
1122
774
|
```jsx fileName="src/app.jsx" codeFormat="esm"
|
|
1123
775
|
import { LocaleRouter } from "./components/LocaleRouter";
|
|
1124
|
-
|
|
776
|
+
|
|
777
|
+
// ... 您的 AppContent 组件
|
|
1125
778
|
|
|
1126
779
|
const App = () => (
|
|
1127
780
|
<LocaleRouter>
|
|
@@ -1134,7 +787,8 @@ export default App;
|
|
|
1134
787
|
|
|
1135
788
|
```jsx fileName="src/app.cjsx" codeFormat="commonjs"
|
|
1136
789
|
const { LocaleRouter } = require("./components/LocaleRouter");
|
|
1137
|
-
|
|
790
|
+
|
|
791
|
+
// ... 您的 AppContent 组件
|
|
1138
792
|
|
|
1139
793
|
const App = () => (
|
|
1140
794
|
<LocaleRouter>
|
|
@@ -1145,47 +799,12 @@ const App = () => (
|
|
|
1145
799
|
module.exports = App;
|
|
1146
800
|
```
|
|
1147
801
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
```typescript {3,7} fileName="vite.config.ts" codeFormat="typescript"
|
|
1151
|
-
import { defineConfig } from "vite";
|
|
1152
|
-
import preact from "@preact/preset-vite";
|
|
1153
|
-
import { intlayer, intlayerProxy } from "vite-intlayer";
|
|
1154
|
-
|
|
1155
|
-
// https://vitejs.dev/config/
|
|
1156
|
-
export default defineConfig({
|
|
1157
|
-
plugins: [preact(), intlayer(), intlayerProxy()],
|
|
1158
|
-
});
|
|
1159
|
-
```
|
|
1160
|
-
|
|
1161
|
-
```javascript {3,7} fileName="vite.config.mjs" codeFormat="esm"
|
|
1162
|
-
import { defineConfig } from "vite";
|
|
1163
|
-
import preact from "@preact/preset-vite";
|
|
1164
|
-
import { intlayer, intlayerProxy } from "vite-intlayer";
|
|
802
|
+
### (可选)步骤 8:在语言环境更改时更改 URL
|
|
1165
803
|
|
|
1166
|
-
|
|
1167
|
-
export default defineConfig({
|
|
1168
|
-
plugins: [preact(), intlayer(), intlayerProxy()],
|
|
1169
|
-
});
|
|
1170
|
-
```
|
|
1171
|
-
|
|
1172
|
-
```javascript {3,7} fileName="vite.config.cjs" codeFormat="commonjs"
|
|
1173
|
-
const { defineConfig } = require("vite");
|
|
1174
|
-
const preact = require("@preact/preset-vite");
|
|
1175
|
-
const { intlayer, intlayerProxy } = require("vite-intlayer");
|
|
1176
|
-
|
|
1177
|
-
// https://vitejs.dev/config/
|
|
1178
|
-
module.exports = defineConfig({
|
|
1179
|
-
plugins: [preact(), intlayer(), intlayerProxy()],
|
|
1180
|
-
});
|
|
1181
|
-
```
|
|
1182
|
-
|
|
1183
|
-
### (可选)步骤 8:当语言环境改变时更改 URL
|
|
1184
|
-
|
|
1185
|
-
要在语言环境更改时更改 URL,您可以使用 `useLocale` 钩子提供的 `onLocaleChange` 属性。同时,您可以使用 `preact-iso` 中的 `useLocation` 和 `route` 来更新 URL 路径。
|
|
804
|
+
要在语言环境更改时更改 URL,可以使用 `useLocale` 钩子提供的 `onLocaleChange` 属性。同时,您可以使用 `preact-iso` 中 `useLocation` 的 `route` 方法来更新 URL 路径。
|
|
1186
805
|
|
|
1187
806
|
```tsx fileName="src/components/LocaleSwitcher.tsx" codeFormat="typescript"
|
|
1188
|
-
import { useLocation
|
|
807
|
+
import { useLocation } from "preact-iso";
|
|
1189
808
|
import {
|
|
1190
809
|
Locales,
|
|
1191
810
|
getHTMLTextDir,
|
|
@@ -1196,16 +815,15 @@ import { useLocale } from "preact-intlayer";
|
|
|
1196
815
|
import type { FunctionalComponent } from "preact";
|
|
1197
816
|
|
|
1198
817
|
const LocaleSwitcher: FunctionalComponent = () => {
|
|
1199
|
-
const
|
|
818
|
+
const { url, route } = useLocation();
|
|
1200
819
|
const { locale, availableLocales, setLocale } = useLocale({
|
|
1201
820
|
onLocaleChange: (newLocale) => {
|
|
1202
|
-
const currentFullPath = location.url; // preact-iso 提供完整的 URL
|
|
1203
821
|
// 使用更新后的语言环境构建 URL
|
|
1204
|
-
//
|
|
1205
|
-
const pathWithLocale = getLocalizedUrl(
|
|
822
|
+
// 示例:/es/about?foo=bar
|
|
823
|
+
const pathWithLocale = getLocalizedUrl(url, newLocale);
|
|
1206
824
|
|
|
1207
825
|
// 更新 URL 路径
|
|
1208
|
-
route(pathWithLocale, true); // true
|
|
826
|
+
route(pathWithLocale, true); // true 表示替换 (replace)
|
|
1209
827
|
},
|
|
1210
828
|
});
|
|
1211
829
|
|
|
@@ -1215,13 +833,13 @@ const LocaleSwitcher: FunctionalComponent = () => {
|
|
|
1215
833
|
<div id="localePopover" popover="auto">
|
|
1216
834
|
{availableLocales.map((localeItem) => (
|
|
1217
835
|
<a
|
|
1218
|
-
href={getLocalizedUrl(
|
|
836
|
+
href={getLocalizedUrl(url, localeItem)}
|
|
1219
837
|
hreflang={localeItem}
|
|
1220
838
|
aria-current={locale === localeItem ? "page" : undefined}
|
|
1221
839
|
onClick={(e) => {
|
|
1222
840
|
e.preventDefault();
|
|
1223
841
|
setLocale(localeItem);
|
|
1224
|
-
//
|
|
842
|
+
// 设置语言环境后的程序化导航将由 onLocaleChange 处理
|
|
1225
843
|
}}
|
|
1226
844
|
key={localeItem}
|
|
1227
845
|
>
|
|
@@ -1230,15 +848,15 @@ const LocaleSwitcher: FunctionalComponent = () => {
|
|
|
1230
848
|
{localeItem}
|
|
1231
849
|
</span>
|
|
1232
850
|
<span>
|
|
1233
|
-
{/*
|
|
851
|
+
{/* 该语言环境自身的语言名称 - 例如 Français */}
|
|
1234
852
|
{getLocaleName(localeItem, localeItem)}
|
|
1235
853
|
</span>
|
|
1236
854
|
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
|
|
1237
|
-
{/*
|
|
855
|
+
{/* 当前语言环境下的语言名称 - 例如当当前语言环境为 Locales.SPANISH 时显示 Francés */}
|
|
1238
856
|
{getLocaleName(localeItem, locale)}
|
|
1239
857
|
</span>
|
|
1240
858
|
<span dir="ltr" lang={Locales.ENGLISH}>
|
|
1241
|
-
{/*
|
|
859
|
+
{/* 英语名称 - 例如 French */}
|
|
1242
860
|
{getLocaleName(localeItem, Locales.ENGLISH)}
|
|
1243
861
|
</span>
|
|
1244
862
|
</a>
|
|
@@ -1252,7 +870,7 @@ export default LocaleSwitcher;
|
|
|
1252
870
|
```
|
|
1253
871
|
|
|
1254
872
|
```jsx fileName="src/components/LocaleSwitcher.jsx" codeFormat="esm"
|
|
1255
|
-
import { useLocation
|
|
873
|
+
import { useLocation } from "preact-iso";
|
|
1256
874
|
import {
|
|
1257
875
|
Locales,
|
|
1258
876
|
getHTMLTextDir,
|
|
@@ -1260,14 +878,12 @@ import {
|
|
|
1260
878
|
getLocalizedUrl,
|
|
1261
879
|
} from "intlayer";
|
|
1262
880
|
import { useLocale } from "preact-intlayer";
|
|
1263
|
-
import { h } from "preact"; // 用于 JSX
|
|
1264
881
|
|
|
1265
882
|
const LocaleSwitcher = () => {
|
|
1266
|
-
const
|
|
883
|
+
const { url, route } = useLocation();
|
|
1267
884
|
const { locale, availableLocales, setLocale } = useLocale({
|
|
1268
885
|
onLocaleChange: (newLocale) => {
|
|
1269
|
-
const
|
|
1270
|
-
const pathWithLocale = getLocalizedUrl(currentFullPath, newLocale);
|
|
886
|
+
const pathWithLocale = getLocalizedUrl(url, newLocale);
|
|
1271
887
|
route(pathWithLocale, true);
|
|
1272
888
|
},
|
|
1273
889
|
});
|
|
@@ -1278,7 +894,7 @@ const LocaleSwitcher = () => {
|
|
|
1278
894
|
<div id="localePopover" popover="auto">
|
|
1279
895
|
{availableLocales.map((localeItem) => (
|
|
1280
896
|
<a
|
|
1281
|
-
href={getLocalizedUrl(
|
|
897
|
+
href={getLocalizedUrl(url, localeItem)}
|
|
1282
898
|
hreflang={localeItem}
|
|
1283
899
|
aria-current={locale === localeItem ? "page" : undefined}
|
|
1284
900
|
onClick={(e) => {
|
|
@@ -1306,7 +922,7 @@ export default LocaleSwitcher;
|
|
|
1306
922
|
```
|
|
1307
923
|
|
|
1308
924
|
```jsx fileName="src/components/LocaleSwitcher.cjsx" codeFormat="commonjs"
|
|
1309
|
-
const { useLocation
|
|
925
|
+
const { useLocation } = require("preact-iso");
|
|
1310
926
|
const {
|
|
1311
927
|
Locales,
|
|
1312
928
|
getHTMLTextDir,
|
|
@@ -1314,45 +930,51 @@ const {
|
|
|
1314
930
|
getLocalizedUrl,
|
|
1315
931
|
} = require("intlayer");
|
|
1316
932
|
const { useLocale } = require("preact-intlayer");
|
|
1317
|
-
const { h } = require("preact"); // 用于 JSX
|
|
1318
933
|
|
|
1319
934
|
const LocaleSwitcher = () => {
|
|
1320
|
-
const
|
|
935
|
+
const { url, route } = useLocation();
|
|
1321
936
|
const { locale, availableLocales, setLocale } = useLocale({
|
|
1322
937
|
onLocaleChange: (newLocale) => {
|
|
1323
|
-
const
|
|
1324
|
-
const pathWithLocale = getLocalizedUrl(currentFullPath, newLocale);
|
|
938
|
+
const pathWithLocale = getLocalizedUrl(url, newLocale);
|
|
1325
939
|
route(pathWithLocale, true);
|
|
1326
940
|
},
|
|
1327
941
|
});
|
|
1328
942
|
|
|
1329
|
-
return (
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
943
|
+
return h(
|
|
944
|
+
"div",
|
|
945
|
+
{},
|
|
946
|
+
h("button", { popovertarget: "localePopover" }, getLocaleName(locale)),
|
|
947
|
+
h(
|
|
948
|
+
"div",
|
|
949
|
+
{ id: "localePopover", popover: "auto" },
|
|
950
|
+
availableLocales.map((localeItem) =>
|
|
951
|
+
h(
|
|
952
|
+
"a",
|
|
953
|
+
{
|
|
954
|
+
href: getLocalizedUrl(url, localeItem),
|
|
955
|
+
hreflang: localeItem,
|
|
956
|
+
"aria-current": locale === localeItem ? "page" : undefined,
|
|
957
|
+
onClick: (e) => {
|
|
1339
958
|
e.preventDefault();
|
|
1340
959
|
setLocale(localeItem);
|
|
1341
|
-
}
|
|
1342
|
-
key
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
960
|
+
},
|
|
961
|
+
key: localeItem,
|
|
962
|
+
},
|
|
963
|
+
h("span", {}, localeItem),
|
|
964
|
+
h("span", {}, getLocaleName(localeItem, localeItem)),
|
|
965
|
+
h(
|
|
966
|
+
"span",
|
|
967
|
+
{ dir: getHTMLTextDir(localeItem), lang: localeItem },
|
|
968
|
+
getLocaleName(localeItem, locale)
|
|
969
|
+
),
|
|
970
|
+
h(
|
|
971
|
+
"span",
|
|
972
|
+
{ dir: "ltr", lang: Locales.ENGLISH },
|
|
973
|
+
getLocaleName(localeItem, Locales.ENGLISH)
|
|
974
|
+
)
|
|
975
|
+
)
|
|
976
|
+
)
|
|
977
|
+
)
|
|
1356
978
|
);
|
|
1357
979
|
};
|
|
1358
980
|
|
|
@@ -1361,25 +983,21 @@ module.exports = LocaleSwitcher;
|
|
|
1361
983
|
|
|
1362
984
|
> 文档参考:
|
|
1363
985
|
>
|
|
1364
|
-
> > - [`useLocale` 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/react-intlayer/useLocale.md)(`preact-intlayer` 的 API 类似)> - [`getLocaleName` 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/intlayer/getLocaleName.md)> - [`getLocalizedUrl` 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/intlayer/getLocalizedUrl.md)> - [`getHTMLTextDir` 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/intlayer/getHTMLTextDir.md)> - [`hreflang` 属性](https://developers.google.com/search/docs/specialty/international/localized-versions?hl=fr)> - [`lang` 属性](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/lang)> - [`dir` 属性](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/dir)> - [`aria-current` 属性](https://developer.mozilla.org/zh-CN/docs/Web/Accessibility/ARIA/Attributes/aria-current)> - [Popover API](https://developer.mozilla.org/zh-CN/docs/Web/API/Popover_API)
|
|
1365
|
-
|
|
1366
|
-
下面是更新后的**第9步**,增加了说明和优化的代码示例:
|
|
986
|
+
> > - [`useLocale` 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/react-intlayer/useLocale.md)(`preact-intlayer` 的 API 类似)> - [`getLocaleName` 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/intlayer/getLocaleName.md)> - [`getLocalizedUrl` 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/intlayer/getLocalizedUrl.md)> - [`getHTMLTextDir` 钩子](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/packages/intlayer/getHTMLTextDir.md)> - [`hreflang` 属性](https://developers.google.com/search/docs/specialty/international/localized-versions?hl=fr)> - [`lang` 属性](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/lang)> - [`dir` 属性](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/dir)> - [`aria-current` 属性](https://developer.mozilla.org/zh-CN/docs/Web/Accessibility/ARIA/Attributes/aria-current)> - [Popover API](https://developer.mozilla.org/zh-CN/docs/Web/API/Popover_API)
|
|
1367
987
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
### (可选)第9步:切换HTML的语言和方向属性
|
|
988
|
+
### (可选)第 9 步:切换 HTML 语言和方向属性
|
|
1371
989
|
|
|
1372
|
-
|
|
990
|
+
当您的应用程序支持多种语言时,更新 `<html>` 标签的 `lang` 和 `dir` 属性以匹配当前语言环境至关重要。这样做可以确保:
|
|
1373
991
|
|
|
1374
|
-
-
|
|
1375
|
-
- **文本渲染**:`dir
|
|
992
|
+
- **无障碍性**:屏幕阅读器和辅助技术依靠正确的 `lang` 属性来准确发音和解释内容。
|
|
993
|
+
- **文本渲染**:`dir`(方向)属性确保文本以正确的顺序呈现(例如,英语从左到右,阿拉伯语或希伯来语从右到左),这对于可读性至关重要。
|
|
1376
994
|
- **SEO**:搜索引擎使用 `lang` 属性来确定页面的语言,有助于在搜索结果中提供正确的本地化内容。
|
|
1377
995
|
|
|
1378
|
-
|
|
996
|
+
通过在语言环境更改时动态更新这些属性,您可以确保所有支持语言的用户都能获得一致且无障碍的体验。
|
|
1379
997
|
|
|
1380
|
-
####
|
|
998
|
+
#### 实现钩子
|
|
1381
999
|
|
|
1382
|
-
|
|
1000
|
+
创建一个自定义钩子来管理 HTML 属性。该钩子监听语言环境更改并相应地更新属性:
|
|
1383
1001
|
|
|
1384
1002
|
```tsx fileName="src/hooks/useI18nHTMLAttributes.tsx" codeFormat="typescript"
|
|
1385
1003
|
import { useEffect } from "preact/hooks";
|
|
@@ -1388,8 +1006,8 @@ import { getHTMLTextDir } from "intlayer";
|
|
|
1388
1006
|
|
|
1389
1007
|
/**
|
|
1390
1008
|
* 根据当前语言环境更新 HTML <html> 元素的 `lang` 和 `dir` 属性。
|
|
1391
|
-
* - `lang
|
|
1392
|
-
* - `dir
|
|
1009
|
+
* - `lang`: 通知浏览器和搜索引擎页面的语言。
|
|
1010
|
+
* - `dir`: 确保正确的阅读顺序(例如,英语为 'ltr',阿拉伯语为 'rtl')。
|
|
1393
1011
|
*
|
|
1394
1012
|
* 这种动态更新对于正确的文本渲染、无障碍访问和 SEO 至关重要。
|
|
1395
1013
|
*/
|
|
@@ -1397,7 +1015,7 @@ export const useI18nHTMLAttributes = () => {
|
|
|
1397
1015
|
const { locale } = useLocale();
|
|
1398
1016
|
|
|
1399
1017
|
useEffect(() => {
|
|
1400
|
-
//
|
|
1018
|
+
// 将语言属性更新为当前语言环境。
|
|
1401
1019
|
document.documentElement.lang = locale;
|
|
1402
1020
|
|
|
1403
1021
|
// 根据当前语言环境设置文本方向。
|
|
@@ -1444,22 +1062,22 @@ const useI18nHTMLAttributes = () => {
|
|
|
1444
1062
|
module.exports = { useI18nHTMLAttributes };
|
|
1445
1063
|
```
|
|
1446
1064
|
|
|
1447
|
-
####
|
|
1065
|
+
#### 在应用程序中使用钩子
|
|
1448
1066
|
|
|
1449
|
-
|
|
1067
|
+
将钩子集成到您的主组件中,以便在语言环境更改时更新 HTML 属性:
|
|
1450
1068
|
|
|
1451
1069
|
```tsx fileName="src/app.tsx" codeFormat="typescript"
|
|
1452
1070
|
import type { FunctionalComponent } from "preact";
|
|
1453
|
-
import { IntlayerProvider } from "preact-intlayer"; // 如果 AppContent 需要,useIntlayer
|
|
1071
|
+
import { IntlayerProvider } from "preact-intlayer"; // 如果 AppContent 需要,useIntlayer 已导入
|
|
1454
1072
|
import { useI18nHTMLAttributes } from "./hooks/useI18nHTMLAttributes";
|
|
1455
1073
|
import "./app.css";
|
|
1456
|
-
// 第5
|
|
1074
|
+
// 第 5 步中的 AppContent 定义
|
|
1457
1075
|
|
|
1458
1076
|
const AppWithHooks: FunctionalComponent = () => {
|
|
1459
|
-
//
|
|
1077
|
+
// 应用钩子以根据语言环境更新 <html> 标签的 lang 和 dir 属性。
|
|
1460
1078
|
useI18nHTMLAttributes();
|
|
1461
1079
|
|
|
1462
|
-
// 假设 AppContent 是您在第 5
|
|
1080
|
+
// 假设 AppContent 是您在第 5 步中的主要内容显示组件
|
|
1463
1081
|
return <AppContent />;
|
|
1464
1082
|
};
|
|
1465
1083
|
|
|
@@ -1476,7 +1094,7 @@ export default App;
|
|
|
1476
1094
|
import { IntlayerProvider } from "preact-intlayer";
|
|
1477
1095
|
import { useI18nHTMLAttributes } from "./hooks/useI18nHTMLAttributes";
|
|
1478
1096
|
import "./app.css";
|
|
1479
|
-
//
|
|
1097
|
+
// 第 5 步中的 AppContent 定义
|
|
1480
1098
|
|
|
1481
1099
|
const AppWithHooks = () => {
|
|
1482
1100
|
useI18nHTMLAttributes();
|
|
@@ -1496,7 +1114,7 @@ export default App;
|
|
|
1496
1114
|
const { IntlayerProvider } = require("preact-intlayer");
|
|
1497
1115
|
const { useI18nHTMLAttributes } = require("./hooks/useI18nHTMLAttributes");
|
|
1498
1116
|
require("./app.css");
|
|
1499
|
-
// 第5
|
|
1117
|
+
// 第 5 步中的 AppContent 定义
|
|
1500
1118
|
|
|
1501
1119
|
const AppWithHooks = () => {
|
|
1502
1120
|
useI18nHTMLAttributes();
|
|
@@ -1512,209 +1130,186 @@ const App = () => (
|
|
|
1512
1130
|
module.exports = App;
|
|
1513
1131
|
```
|
|
1514
1132
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
- 确保 **语言** (`lang`) 属性正确反映当前的语言环境,这对于 SEO 和浏览器行为非常重要。
|
|
1518
|
-
- 根据语言环境调整 **文本方向** (`dir`),提升不同阅读顺序语言的可读性和可用性。
|
|
1519
|
-
- 提供更**无障碍**的体验,因为辅助技术依赖这些属性来实现最佳功能。
|
|
1133
|
+
### (可选)第 10 步:创建本地化链接组件
|
|
1520
1134
|
|
|
1521
|
-
|
|
1135
|
+
为了确保您的应用程序导航尊重当前语言环境,您可以创建一个自定义 `Link` 组件。此组件会自动为内部 URL 添加当前语言前缀。
|
|
1522
1136
|
|
|
1523
|
-
|
|
1137
|
+
这种行为在以下几个方面非常有用:
|
|
1524
1138
|
|
|
1525
|
-
|
|
1139
|
+
- **SEO 和用户体验**:本地化 URL 帮助搜索引擎正确索引特定语言的页面,并为用户提供其首选语言的内容。
|
|
1140
|
+
- **一致性**:通过在整个应用程序中使用本地化链接,您可以确保导航保持在当前语言环境内,防止意外的语言切换。
|
|
1141
|
+
- **可维护性**:将本地化逻辑集中在单个组件中可以简化 URL 的管理。
|
|
1526
1142
|
|
|
1527
|
-
|
|
1528
|
-
- **一致性**:通过在整个应用中使用本地化链接,您可以确保导航保持在当前语言环境内,避免意外的语言切换。
|
|
1529
|
-
- **可维护性**:将本地化逻辑集中在单个组件中简化了 URL 的管理。
|
|
1143
|
+
以下是 Preact 中本地化 `Link` 组件的实现:
|
|
1530
1144
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
```tsx fileName="src/components/LocalizedLink.tsx" codeFormat="typescript"
|
|
1145
|
+
```tsx fileName="src/components/Link.tsx" codeFormat="typescript"
|
|
1534
1146
|
import { getLocalizedUrl } from "intlayer";
|
|
1535
|
-
import { useLocale
|
|
1536
|
-
|
|
1537
|
-
import type { JSX } from "preact";
|
|
1538
|
-
import { forwardRef } from "preact/compat"; // 用于转发 refs
|
|
1147
|
+
import { useLocale } from "preact-intlayer";
|
|
1148
|
+
import { forwardRef } from "preact/compat";
|
|
1149
|
+
import type { JSX } from "preact";
|
|
1539
1150
|
|
|
1540
|
-
export interface
|
|
1151
|
+
export interface LinkProps extends JSX.HTMLAttributes<HTMLAnchorElement> {
|
|
1541
1152
|
href: string;
|
|
1542
|
-
replace?: boolean; // 可选:替换历史记录状态
|
|
1543
1153
|
}
|
|
1544
1154
|
|
|
1545
1155
|
/**
|
|
1546
|
-
*
|
|
1547
|
-
* 如果 URL 以 http:// 或 https://
|
|
1156
|
+
* 检查给定 URL 是否为外部链接的实用函数。
|
|
1157
|
+
* 如果 URL 以 http:// 或 https:// 开头,则被视为外部链接。
|
|
1548
1158
|
*/
|
|
1549
1159
|
export const checkIsExternalLink = (href?: string): boolean =>
|
|
1550
1160
|
/^https?:\/\//.test(href ?? "");
|
|
1551
1161
|
|
|
1552
1162
|
/**
|
|
1553
|
-
*
|
|
1554
|
-
* 对于内部链接,它使用 `getLocalizedUrl`
|
|
1555
|
-
*
|
|
1556
|
-
* 它使用标准的 <a> 标签,但可以通过 preact-iso 的 `route` 触发客户端导航。
|
|
1163
|
+
* 一个自定义 Link 组件,根据当前语言环境自适应 href 属性。
|
|
1164
|
+
* 对于内部链接,它使用 `getLocalizedUrl` 为 URL 添加语言环境前缀(例如 /fr/about)。
|
|
1165
|
+
* 这确保了导航保持在同一语言环境上下文中。
|
|
1557
1166
|
*/
|
|
1558
|
-
export const
|
|
1559
|
-
({ href, children,
|
|
1167
|
+
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
|
|
1168
|
+
({ href, children, ...props }, ref) => {
|
|
1560
1169
|
const { locale } = useLocale();
|
|
1561
|
-
const location = useLocation(); // 来自 preact-iso
|
|
1562
1170
|
const isExternalLink = checkIsExternalLink(href);
|
|
1563
1171
|
|
|
1172
|
+
// 如果链接是内部的且提供了有效的 href,则获取本地化 URL。
|
|
1564
1173
|
const hrefI18n =
|
|
1565
1174
|
href && !isExternalLink ? getLocalizedUrl(href, locale) : href;
|
|
1566
1175
|
|
|
1567
|
-
const handleClick = (event: JSX.TargetedMouseEvent<HTMLAnchorElement>) => {
|
|
1568
|
-
if (onClick) {
|
|
1569
|
-
onClick(event);
|
|
1570
|
-
}
|
|
1571
|
-
if (
|
|
1572
|
-
!isExternalLink &&
|
|
1573
|
-
href && // 确保 href 已定义
|
|
1574
|
-
event.button === 0 && // 左键点击
|
|
1575
|
-
!event.metaKey &&
|
|
1576
|
-
!event.ctrlKey &&
|
|
1577
|
-
!event.shiftKey &&
|
|
1578
|
-
!event.altKey && // 标准修饰键检查
|
|
1579
|
-
!props.target // 不在新标签页/窗口打开
|
|
1580
|
-
) {
|
|
1581
|
-
event.preventDefault();
|
|
1582
|
-
if (location.url !== hrefI18n) {
|
|
1583
|
-
// 仅当 URL 不同时才导航
|
|
1584
|
-
route(hrefI18n, replace); // 使用 preact-iso 的 route
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
};
|
|
1588
|
-
|
|
1589
1176
|
return (
|
|
1590
|
-
<a href={hrefI18n} ref={ref}
|
|
1177
|
+
<a href={hrefI18n} ref={ref} {...props}>
|
|
1591
1178
|
{children}
|
|
1592
1179
|
</a>
|
|
1593
1180
|
);
|
|
1594
1181
|
}
|
|
1595
1182
|
);
|
|
1183
|
+
|
|
1184
|
+
Link.displayName = "Link";
|
|
1596
1185
|
```
|
|
1597
1186
|
|
|
1598
|
-
```jsx fileName="src/components/
|
|
1187
|
+
```jsx fileName="src/components/Link.jsx" codeFormat="esm"
|
|
1599
1188
|
import { getLocalizedUrl } from "intlayer";
|
|
1600
1189
|
import { useLocale } from "preact-intlayer";
|
|
1601
|
-
import { useLocation, route } from "preact-iso"; // 从 preact-iso 导入
|
|
1602
1190
|
import { forwardRef } from "preact/compat";
|
|
1603
|
-
import { h } from "preact"; // 用于 JSX
|
|
1604
1191
|
|
|
1192
|
+
/**
|
|
1193
|
+
* 检查给定 URL 是否为外部链接的实用函数。
|
|
1194
|
+
* 如果 URL 以 http:// 或 https:// 开头,则被视为外部链接。
|
|
1195
|
+
*/
|
|
1605
1196
|
export const checkIsExternalLink = (href) => /^https?:\/\//.test(href ?? "");
|
|
1606
1197
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1198
|
+
/**
|
|
1199
|
+
* 一个自定义 Link 组件,根据当前语言环境自适应 href 属性。
|
|
1200
|
+
* 对于内部链接,它使用 `getLocalizedUrl` 为 URL 添加语言环境前缀(例如 /fr/about)。
|
|
1201
|
+
* 这确保了导航保持在同一语言环境上下文中。
|
|
1202
|
+
*/
|
|
1203
|
+
export const Link = forwardRef(({ href, children, ...props }, ref) => {
|
|
1204
|
+
const { locale } = useLocale();
|
|
1205
|
+
const isExternalLink = checkIsExternalLink(href);
|
|
1612
1206
|
|
|
1613
|
-
|
|
1614
|
-
|
|
1207
|
+
// 如果链接是内部的且提供了有效的 href,则获取本地化 URL。
|
|
1208
|
+
const hrefI18n =
|
|
1209
|
+
href && !isExternalLink ? getLocalizedUrl(href, locale) : href;
|
|
1615
1210
|
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
href &&
|
|
1623
|
-
event.button === 0 &&
|
|
1624
|
-
!event.metaKey &&
|
|
1625
|
-
!event.ctrlKey &&
|
|
1626
|
-
!event.shiftKey &&
|
|
1627
|
-
!event.altKey &&
|
|
1628
|
-
!props.target
|
|
1629
|
-
) {
|
|
1630
|
-
event.preventDefault();
|
|
1631
|
-
if (location.url !== hrefI18n) {
|
|
1632
|
-
route(hrefI18n, replace);
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
};
|
|
1211
|
+
return (
|
|
1212
|
+
<a href={hrefI18n} ref={ref} {...props}>
|
|
1213
|
+
{children}
|
|
1214
|
+
</a>
|
|
1215
|
+
);
|
|
1216
|
+
});
|
|
1636
1217
|
|
|
1637
|
-
|
|
1638
|
-
<a href={hrefI18n} ref={ref} onClick={handleClick} {...props}>
|
|
1639
|
-
{children}
|
|
1640
|
-
</a>
|
|
1641
|
-
);
|
|
1642
|
-
}
|
|
1643
|
-
);
|
|
1218
|
+
Link.displayName = "Link";
|
|
1644
1219
|
```
|
|
1645
1220
|
|
|
1646
|
-
```jsx fileName="src/components/
|
|
1221
|
+
```jsx fileName="src/components/Link.cjsx" codeFormat="commonjs"
|
|
1647
1222
|
const { getLocalizedUrl } = require("intlayer");
|
|
1648
1223
|
const { useLocale } = require("preact-intlayer");
|
|
1649
|
-
const { useLocation, route } = require("preact-iso"); // 从 preact-iso 导入
|
|
1650
1224
|
const { forwardRef } = require("preact/compat");
|
|
1651
|
-
const { h } = require("preact"); // 用于 JSX
|
|
1652
1225
|
|
|
1226
|
+
/**
|
|
1227
|
+
* 检查给定 URL 是否为外部链接的实用函数。
|
|
1228
|
+
* 如果 URL 以 http:// 或 https:// 开头,则被视为外部链接。
|
|
1229
|
+
*/
|
|
1653
1230
|
const checkIsExternalLink = (href) => /^https?:\/\//.test(href ?? "");
|
|
1654
1231
|
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
!props.target
|
|
1679
|
-
) {
|
|
1680
|
-
event.preventDefault();
|
|
1681
|
-
if (location.url !== hrefI18n) {
|
|
1682
|
-
route(hrefI18n, replace);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
};
|
|
1232
|
+
/**
|
|
1233
|
+
* 一个自定义 Link 组件,根据当前语言环境自适应 href 属性。
|
|
1234
|
+
* 对于内部链接,它使用 `getLocalizedUrl` 为 URL 添加语言环境前缀(例如 /fr/about)。
|
|
1235
|
+
* 这确保了导航保持在同一语言环境上下文中。
|
|
1236
|
+
*/
|
|
1237
|
+
const Link = forwardRef(({ href, children, ...props }, ref) => {
|
|
1238
|
+
const { locale } = useLocale();
|
|
1239
|
+
const isExternalLink = checkIsExternalLink(href);
|
|
1240
|
+
|
|
1241
|
+
// 如果链接是内部的且提供了有效的 href,则获取本地化 URL。
|
|
1242
|
+
const hrefI18n =
|
|
1243
|
+
href && !isExternalLink ? getLocalizedUrl(href, locale) : href;
|
|
1244
|
+
|
|
1245
|
+
return h(
|
|
1246
|
+
"a",
|
|
1247
|
+
{
|
|
1248
|
+
href: hrefI18n,
|
|
1249
|
+
ref: ref,
|
|
1250
|
+
...props,
|
|
1251
|
+
},
|
|
1252
|
+
children
|
|
1253
|
+
);
|
|
1254
|
+
});
|
|
1686
1255
|
|
|
1687
|
-
|
|
1688
|
-
<a href={hrefI18n} ref={ref} onClick={handleClick} {...props}>
|
|
1689
|
-
{children}
|
|
1690
|
-
</a>
|
|
1691
|
-
);
|
|
1692
|
-
}
|
|
1693
|
-
);
|
|
1256
|
+
Link.displayName = "Link";
|
|
1694
1257
|
|
|
1695
|
-
module.exports = {
|
|
1258
|
+
module.exports = { Link, checkIsExternalLink };
|
|
1696
1259
|
```
|
|
1697
1260
|
|
|
1698
1261
|
#### 工作原理
|
|
1699
1262
|
|
|
1700
1263
|
- **检测外部链接**:
|
|
1701
|
-
辅助函数 `checkIsExternalLink`
|
|
1702
|
-
-
|
|
1703
|
-
`useLocale`
|
|
1264
|
+
辅助函数 `checkIsExternalLink` 确定 URL 是否为外部。外部链接保持不变,因为它们不需要本地化。
|
|
1265
|
+
- **检索当前语言环境**:
|
|
1266
|
+
`useLocale` 钩子提供当前的语言环境(例如,法语为 `fr`)。
|
|
1704
1267
|
- **本地化 URL**:
|
|
1705
|
-
|
|
1706
|
-
- **客户端导航**:
|
|
1707
|
-
`handleClick` 函数会检查链接是否为内部链接以及是否应阻止标准导航。如果是,则使用 `preact-iso` 的 `route` 函数(通过 `useLocation` 获取或直接导入)来执行客户端导航。这提供了类似 SPA 的行为,而无需完全重新加载页面。
|
|
1268
|
+
对于内部链接(即非外部链接),使用 `getLocalizedUrl` 自动为 URL 添加当前语言环境的前缀。这意味着如果您的用户处于法语环境,将 `/about` 作为 `href` 传递将使其转换为 `/fr/about`。
|
|
1708
1269
|
- **返回链接**:
|
|
1709
|
-
该组件返回一个带有本地化 URL
|
|
1270
|
+
该组件返回一个带有本地化 URL 的 `<a>` 元素,确保导航与语言环境保持一致。
|
|
1271
|
+
|
|
1272
|
+
### (可选)第 11 步:渲染 Markdown 和 HTML
|
|
1273
|
+
|
|
1274
|
+
Intlayer 支持在 Preact 中渲染 Markdown 和 HTML 内容。
|
|
1275
|
+
|
|
1276
|
+
您可以通过使用 `.use()` 方法自定义 Markdown 和 HTML 内容的渲染。此方法允许您覆盖特定标签的默认渲染。
|
|
1277
|
+
|
|
1278
|
+
```tsx
|
|
1279
|
+
import { useIntlayer } from "preact-intlayer";
|
|
1280
|
+
|
|
1281
|
+
const { myMarkdownContent, myHtmlContent } = useIntlayer("my-component");
|
|
1282
|
+
|
|
1283
|
+
// ...
|
|
1284
|
+
|
|
1285
|
+
return (
|
|
1286
|
+
<div>
|
|
1287
|
+
{/* 基本渲染 */}
|
|
1288
|
+
{myMarkdownContent}
|
|
1289
|
+
|
|
1290
|
+
{/* Markdown 的自定义渲染 */}
|
|
1291
|
+
{myMarkdownContent.use({
|
|
1292
|
+
h1: (props) => <h1 style={{ color: "red" }} {...props} />,
|
|
1293
|
+
})}
|
|
1294
|
+
|
|
1295
|
+
{/* HTML 的基本渲染 */}
|
|
1296
|
+
{myHtmlContent}
|
|
1297
|
+
|
|
1298
|
+
{/* HTML 的自定义渲染 */}
|
|
1299
|
+
{myHtmlContent.use({
|
|
1300
|
+
b: (props) => <strong style={{ color: "blue" }} {...props} />,
|
|
1301
|
+
})}
|
|
1302
|
+
</div>
|
|
1303
|
+
);
|
|
1304
|
+
```
|
|
1710
1305
|
|
|
1711
1306
|
### 配置 TypeScript
|
|
1712
1307
|
|
|
1713
1308
|
Intlayer 使用模块增强来利用 TypeScript 的优势,使您的代码库更健壮。
|
|
1714
1309
|
|
|
1715
|
-

|
|
1716
1311
|
|
|
1717
|
-

|
|
1718
1313
|
|
|
1719
1314
|
确保您的 TypeScript 配置包含自动生成的类型。
|
|
1720
1315
|
|
|
@@ -1724,7 +1319,7 @@ Intlayer 使用模块增强来利用 TypeScript 的优势,使您的代码库
|
|
|
1724
1319
|
"compilerOptions": {
|
|
1725
1320
|
// ...
|
|
1726
1321
|
"jsx": "react-jsx",
|
|
1727
|
-
"jsxImportSource": "preact", // 推荐用于 Preact 10
|
|
1322
|
+
"jsxImportSource": "preact", // 推荐用于 Preact 10+
|
|
1728
1323
|
// ...
|
|
1729
1324
|
},
|
|
1730
1325
|
"include": [
|
|
@@ -1734,13 +1329,13 @@ Intlayer 使用模块增强来利用 TypeScript 的优势,使您的代码库
|
|
|
1734
1329
|
}
|
|
1735
1330
|
```
|
|
1736
1331
|
|
|
1737
|
-
> 确保您的 `tsconfig.json`
|
|
1332
|
+
> 确保您的 `tsconfig.json` 已为 Preact 设置,特别是 `jsx` 和 `jsxImportSource`;如果不使用 `preset-vite` 的默认值,对于较旧的 Preact 版本,还需要设置 `jsxFactory`/`jsxFragmentFactory`。
|
|
1738
1333
|
|
|
1739
1334
|
### Git 配置
|
|
1740
1335
|
|
|
1741
|
-
建议忽略 Intlayer 生成的文件。这样可以避免将它们提交到您的 Git
|
|
1336
|
+
建议忽略 Intlayer 生成的文件。这样可以避免将它们提交到您的 Git 仓库。
|
|
1742
1337
|
|
|
1743
|
-
|
|
1338
|
+
为此,您可以在 `.gitignore` 文件中添加以下指令:
|
|
1744
1339
|
|
|
1745
1340
|
```plaintext
|
|
1746
1341
|
# 忽略 Intlayer 生成的文件
|
|
@@ -1753,19 +1348,19 @@ Intlayer 使用模块增强来利用 TypeScript 的优势,使您的代码库
|
|
|
1753
1348
|
|
|
1754
1349
|
[从 VS Code 市场安装](https://marketplace.visualstudio.com/items?itemName=intlayer.intlayer-vs-code-extension)
|
|
1755
1350
|
|
|
1756
|
-
|
|
1351
|
+
此扩展提供:
|
|
1757
1352
|
|
|
1758
|
-
-
|
|
1759
|
-
-
|
|
1760
|
-
-
|
|
1761
|
-
-
|
|
1353
|
+
- **自动补全** 翻译键。
|
|
1354
|
+
- **实时错误检测** 缺失的翻译。
|
|
1355
|
+
- **内联预览** 翻译内容。
|
|
1356
|
+
- **快速操作** 轻松创建和更新翻译。
|
|
1762
1357
|
|
|
1763
|
-
|
|
1358
|
+
有关如何使用该扩展的更多详细信息,请参考 [Intlayer VS Code 扩展文档](https://intlayer.org/doc/vs-code-extension)。
|
|
1764
1359
|
|
|
1765
1360
|
---
|
|
1766
1361
|
|
|
1767
|
-
###
|
|
1362
|
+
### 深入了解
|
|
1768
1363
|
|
|
1769
|
-
|
|
1364
|
+
要进一步了解,您可以实现 [可视化编辑器](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/intlayer_visual_editor.md) 或使用 [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/intlayer_CMS.md) 将内容外部化。
|
|
1770
1365
|
|
|
1771
1366
|
---
|