@intlayer/docs 8.4.5 → 8.4.6
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 +20 -0
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +20 -0
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +1 -0
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/docs/ar/configuration.md +520 -722
- package/docs/ar/intlayer_with_storybook.md +521 -0
- package/docs/bn/configuration.md +922 -0
- package/docs/bn/intlayer_with_hono.md +428 -0
- package/docs/de/configuration.md +369 -743
- package/docs/de/intlayer_with_storybook.md +521 -0
- package/docs/en/configuration.md +181 -507
- package/docs/en/intlayer_with_storybook.md +521 -0
- package/docs/en-GB/configuration.md +456 -657
- package/docs/en-GB/intlayer_with_storybook.md +521 -0
- package/docs/es/configuration.md +379 -754
- package/docs/es/intlayer_with_storybook.md +521 -0
- package/docs/fr/configuration.md +376 -757
- package/docs/fr/intlayer_with_storybook.md +521 -0
- package/docs/hi/configuration.md +532 -728
- package/docs/hi/intlayer_with_storybook.md +521 -0
- package/docs/id/configuration.md +371 -684
- package/docs/id/intlayer_with_storybook.md +521 -0
- package/docs/it/configuration.md +397 -775
- package/docs/it/intlayer_with_storybook.md +521 -0
- package/docs/ja/configuration.md +525 -724
- package/docs/ja/intlayer_with_storybook.md +521 -0
- package/docs/ko/configuration.md +525 -724
- package/docs/ko/intlayer_with_storybook.md +521 -0
- package/docs/pl/configuration.md +430 -734
- package/docs/pl/intlayer_with_storybook.md +521 -0
- package/docs/pt/configuration.md +375 -746
- package/docs/pt/intlayer_with_storybook.md +521 -0
- package/docs/ru/configuration.md +532 -701
- package/docs/ru/intlayer_with_storybook.md +521 -0
- package/docs/tr/configuration.md +527 -719
- package/docs/tr/intlayer_with_storybook.md +521 -0
- package/docs/uk/configuration.md +425 -744
- package/docs/uk/intlayer_with_storybook.md +521 -0
- package/docs/ur/configuration.md +922 -0
- package/docs/ur/intlayer_with_hono.md +428 -0
- package/docs/vi/configuration.md +412 -753
- package/docs/vi/intlayer_with_storybook.md +521 -0
- package/docs/zh/configuration.md +521 -714
- package/docs/zh/intlayer_with_storybook.md +521 -0
- package/package.json +6 -6
- package/src/generated/docs.entry.ts +20 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-03-20
|
|
3
|
+
updatedAt: 2026-03-20
|
|
4
|
+
title: 如何在 Storybook 中设置 Intlayer
|
|
5
|
+
description: 学习如何使用 Intlayer 和 Storybook 使您的设计系统多语言化 — 编译内容声明、添加语言切换器,并以任何语言预览您的组件。
|
|
6
|
+
keywords:
|
|
7
|
+
- 国际化
|
|
8
|
+
- 文档
|
|
9
|
+
- Intlayer
|
|
10
|
+
- Storybook
|
|
11
|
+
- React
|
|
12
|
+
- i18n
|
|
13
|
+
- TypeScript
|
|
14
|
+
- Vite
|
|
15
|
+
- Webpack
|
|
16
|
+
slugs:
|
|
17
|
+
- doc
|
|
18
|
+
- storybook
|
|
19
|
+
history:
|
|
20
|
+
- version: 8.4.5
|
|
21
|
+
date: 2026-03-20
|
|
22
|
+
changes: Init doc
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# 在 Storybook 中使用 Intlayer
|
|
26
|
+
|
|
27
|
+
## 目录
|
|
28
|
+
|
|
29
|
+
<TOC/>
|
|
30
|
+
|
|
31
|
+
## 什么是 Intlayer?
|
|
32
|
+
|
|
33
|
+
**Intlayer** 是一个创新且开源的国际化(i18n)库,旨在简化现代 Web 应用程序的多语言支持。它在**组件级别**运行——每个组件拥有自己的内容声明——使翻译与使用它们的代码位于同一位置。
|
|
34
|
+
|
|
35
|
+
使用 Intlayer,您可以:
|
|
36
|
+
|
|
37
|
+
- **声明式管理翻译**:使用每个组件的内容文件。
|
|
38
|
+
- **获得完整的 TypeScript 支持**:通过自动生成的类型和 IDE 自动补全。
|
|
39
|
+
- **运行时切换语言**:无需重新加载页面。
|
|
40
|
+
- **自动翻译**:利用内置的 AI 服务商集成。
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 为什么要结合 Storybook 使用 Intlayer?
|
|
45
|
+
|
|
46
|
+
Storybook 是开发和记录 UI 组件的行业标准工具。通过将它与 Intlayer 结合使用,您可以:
|
|
47
|
+
|
|
48
|
+
- **直接在 Storybook 画布中预览每种语言**:使用工具栏切换器。
|
|
49
|
+
- **提前捕获缺失的翻译**:在进入生产环境之前修正问题。
|
|
50
|
+
- **记录多语言组件**:使用真实的、类型安全的内容,而不是硬编码的字符串。
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 逐步设置指南
|
|
55
|
+
|
|
56
|
+
<Tabs>
|
|
57
|
+
<Tab value="Vite Setup">
|
|
58
|
+
|
|
59
|
+
### 第一步:安装依赖项
|
|
60
|
+
|
|
61
|
+
```bash packageManager="npm"
|
|
62
|
+
npm install intlayer react-intlayer
|
|
63
|
+
npm install vite-intlayer --save-dev
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```bash packageManager="pnpm"
|
|
67
|
+
pnpm add intlayer react-intlayer
|
|
68
|
+
pnpm add vite-intlayer --save-dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```bash packageManager="yarn"
|
|
72
|
+
yarn add intlayer react-intlayer
|
|
73
|
+
yarn add vite-intlayer --save-dev
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```bash packageManager="bun"
|
|
77
|
+
bun add intlayer react-intlayer
|
|
78
|
+
bun add vite-intlayer --dev
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| 软件包 | 角色 |
|
|
82
|
+
| ---------------- | --------------------------------------------------- |
|
|
83
|
+
| `intlayer` | 核心 — 配置、内容编译、命令行工具 |
|
|
84
|
+
| `react-intlayer` | React 绑定 — `IntlayerProvider`, `useIntlayer` 钩子 |
|
|
85
|
+
| `vite-intlayer` | Vite 插件 — 监听并编译内容声明文件 |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### 第二步:创建 Intlayer 配置
|
|
90
|
+
|
|
91
|
+
在项目的根目录(或设计系统包内)创建 `intlayer.config.ts`:
|
|
92
|
+
|
|
93
|
+
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
94
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
95
|
+
|
|
96
|
+
const config: IntlayerConfig = {
|
|
97
|
+
internationalization: {
|
|
98
|
+
locales: [
|
|
99
|
+
Locales.ENGLISH,
|
|
100
|
+
Locales.FRENCH,
|
|
101
|
+
Locales.SPANISH,
|
|
102
|
+
// 根据需要添加更多语言
|
|
103
|
+
],
|
|
104
|
+
defaultLocale: Locales.ENGLISH,
|
|
105
|
+
},
|
|
106
|
+
content: {
|
|
107
|
+
contentDir: ["./src"], // 您的 *.content.ts 文件所在位置
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default config;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> 有关完整选项列表,请参见 [配置参考](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/configuration.md)。
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### 第三步:将 Vite 插件添加到 Storybook
|
|
119
|
+
|
|
120
|
+
Storybook 的 `viteFinal` 钩子允许您扩展内部的 Vite 配置。在此处导入并添加 `intlayer()` 插件:
|
|
121
|
+
|
|
122
|
+
```typescript fileName=".storybook/main.ts" codeFormat="typescript"
|
|
123
|
+
import type { StorybookConfig } from "@storybook/react-vite";
|
|
124
|
+
import { defineConfig, mergeConfig } from "vite";
|
|
125
|
+
import { intlayer } from "vite-intlayer";
|
|
126
|
+
|
|
127
|
+
const config: StorybookConfig = {
|
|
128
|
+
stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
|
129
|
+
addons: [
|
|
130
|
+
"@storybook/addon-essentials",
|
|
131
|
+
// …其他插件
|
|
132
|
+
],
|
|
133
|
+
framework: {
|
|
134
|
+
name: "@storybook/react-vite",
|
|
135
|
+
options: {},
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
async viteFinal(baseConfig, { configType }) {
|
|
139
|
+
const env = {
|
|
140
|
+
command: configType === "DEVELOPMENT" ? "serve" : "build",
|
|
141
|
+
mode: configType === "DEVELOPMENT" ? "development" : "production",
|
|
142
|
+
} as const;
|
|
143
|
+
|
|
144
|
+
const viteConfig = defineConfig(() => ({
|
|
145
|
+
plugins: [intlayer()],
|
|
146
|
+
}));
|
|
147
|
+
|
|
148
|
+
return mergeConfig(baseConfig, viteConfig(env));
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export default config;
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`intlayer()` 插件会监听您的 `*.content.ts` 文件,并在 Storybook 开发过程中文件发生变化时自动重新构建字典。
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### 第四步:添加 `IntlayerProvider` 装饰器和语言工具栏
|
|
160
|
+
|
|
161
|
+
Storybook 的 `preview` 文件是使用 `IntlayerProvider` 包裹每个 story,并在工具栏中暴露语言切换器的最佳场所:
|
|
162
|
+
|
|
163
|
+
```tsx fileName=".storybook/preview.tsx" codeFormat="typescript"
|
|
164
|
+
import type { Preview, StoryContext } from "@storybook/react";
|
|
165
|
+
import { IntlayerProvider } from "react-intlayer";
|
|
166
|
+
|
|
167
|
+
const preview: Preview = {
|
|
168
|
+
// 在 IntlayerProvider 中包裹每个 story
|
|
169
|
+
decorators: [
|
|
170
|
+
(Story, context: StoryContext) => {
|
|
171
|
+
const locale = context.globals.locale ?? "en";
|
|
172
|
+
return (
|
|
173
|
+
<IntlayerProvider locale={locale}>
|
|
174
|
+
<Story />
|
|
175
|
+
</IntlayerProvider>
|
|
176
|
+
);
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
|
|
180
|
+
// 在 Storybook 工具栏中暴露语言切换器
|
|
181
|
+
globalTypes: {
|
|
182
|
+
locale: {
|
|
183
|
+
description: "当前语言",
|
|
184
|
+
defaultValue: "en",
|
|
185
|
+
toolbar: {
|
|
186
|
+
title: "语言",
|
|
187
|
+
icon: "globe",
|
|
188
|
+
items: [
|
|
189
|
+
{ value: "en", title: "English" },
|
|
190
|
+
{ value: "fr", title: "Français" },
|
|
191
|
+
{ value: "es", title: "Español" },
|
|
192
|
+
],
|
|
193
|
+
dynamicTitle: true,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
parameters: {
|
|
199
|
+
controls: {
|
|
200
|
+
matchers: {
|
|
201
|
+
color: /(background|color)$/i,
|
|
202
|
+
date: /Date$/i,
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export default preview;
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
> `locale` 的值必须与您在 `intlayer.config.ts` 中声明的语言一致。
|
|
212
|
+
|
|
213
|
+
</Tab>
|
|
214
|
+
<Tab value="Webpack Setup">
|
|
215
|
+
|
|
216
|
+
### 第一步:安装依赖项
|
|
217
|
+
|
|
218
|
+
```bash packageManager="npm"
|
|
219
|
+
npm install intlayer react-intlayer
|
|
220
|
+
npm install @intlayer/webpack --save-dev
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```bash packageManager="pnpm"
|
|
224
|
+
pnpm add intlayer react-intlayer
|
|
225
|
+
pnpm add @intlayer/webpack --save-dev
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
```bash packageManager="yarn"
|
|
229
|
+
yarn add intlayer react-intlayer
|
|
230
|
+
yarn add @intlayer/webpack --save-dev
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```bash packageManager="bun"
|
|
234
|
+
bun add intlayer react-intlayer
|
|
235
|
+
bun add @intlayer/webpack --dev
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### 第二步:创建 Intlayer 配置
|
|
241
|
+
|
|
242
|
+
在项目的根目录创建 `intlayer.config.ts`:
|
|
243
|
+
|
|
244
|
+
```typescript fileName="intlayer.config.ts" codeFormat="typescript"
|
|
245
|
+
import { Locales, type IntlayerConfig } from "intlayer";
|
|
246
|
+
|
|
247
|
+
const config: IntlayerConfig = {
|
|
248
|
+
internationalization: {
|
|
249
|
+
locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
|
|
250
|
+
defaultLocale: Locales.ENGLISH,
|
|
251
|
+
},
|
|
252
|
+
content: {
|
|
253
|
+
contentDir: ["./src"],
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export default config;
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### 第三步:配置 Storybook 的 Webpack
|
|
263
|
+
|
|
264
|
+
对于基于 Webpack 的 Storybook 设置(例如 `@storybook/react-webpack5`),请通过 `webpackFinal` 扩展 Webpack 配置,以添加 Intlayer 别名和加载器:
|
|
265
|
+
|
|
266
|
+
```typescript fileName=".storybook/main.ts" codeFormat="typescript"
|
|
267
|
+
import type { StorybookConfig } from "@storybook/react-webpack5";
|
|
268
|
+
import { IntlayerWebpackPlugin } from "@intlayer/webpack";
|
|
269
|
+
|
|
270
|
+
const config: StorybookConfig = {
|
|
271
|
+
stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
|
272
|
+
addons: ["@storybook/addon-essentials"],
|
|
273
|
+
framework: {
|
|
274
|
+
name: "@storybook/react-webpack5",
|
|
275
|
+
options: {},
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
webpackFinal: async (baseConfig) => {
|
|
279
|
+
baseConfig.plugins = [
|
|
280
|
+
...(baseConfig.plugins ?? []),
|
|
281
|
+
new IntlayerWebpackPlugin(),
|
|
282
|
+
];
|
|
283
|
+
return baseConfig;
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export default config;
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### 第四步:添加 `IntlayerProvider` 装饰器和语言工具栏
|
|
293
|
+
|
|
294
|
+
与 Vite 设置相同 —— 在 `.storybook/preview.tsx` 中添加装饰器和全局语言类型:
|
|
295
|
+
|
|
296
|
+
```tsx fileName=".storybook/preview.tsx" codeFormat="typescript"
|
|
297
|
+
import type { Preview, StoryContext } from "@storybook/react";
|
|
298
|
+
import { IntlayerProvider } from "react-intlayer";
|
|
299
|
+
|
|
300
|
+
const preview: Preview = {
|
|
301
|
+
decorators: [
|
|
302
|
+
(Story, context: StoryContext) => {
|
|
303
|
+
const locale = context.globals.locale ?? "en";
|
|
304
|
+
return (
|
|
305
|
+
<IntlayerProvider locale={locale}>
|
|
306
|
+
<Story />
|
|
307
|
+
</IntlayerProvider>
|
|
308
|
+
);
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
|
|
312
|
+
globalTypes: {
|
|
313
|
+
locale: {
|
|
314
|
+
description: "当前语言",
|
|
315
|
+
defaultValue: "en",
|
|
316
|
+
toolbar: {
|
|
317
|
+
title: "语言",
|
|
318
|
+
icon: "globe",
|
|
319
|
+
items: [
|
|
320
|
+
{ value: "en", title: "English" },
|
|
321
|
+
{ value: "fr", title: "Français" },
|
|
322
|
+
{ value: "es", title: "Español" },
|
|
323
|
+
],
|
|
324
|
+
dynamicTitle: true,
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
export default preview;
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
</Tab>
|
|
334
|
+
</Tabs>
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 声明内容
|
|
339
|
+
|
|
340
|
+
在每个组件旁边创建一个 `*.content.ts` 文件。Intlayer 会在编译期间自动识别它。
|
|
341
|
+
|
|
342
|
+
```typescript fileName="src/components/CopyButton/CopyButton.content.ts" codeFormat="typescript"
|
|
343
|
+
import { type Dictionary, t } from "intlayer";
|
|
344
|
+
|
|
345
|
+
const copyButtonContent = {
|
|
346
|
+
key: "copy-button",
|
|
347
|
+
content: {
|
|
348
|
+
label: t({
|
|
349
|
+
en: "Copy content",
|
|
350
|
+
fr: "Copier le contenu",
|
|
351
|
+
es: "Copiar contenido",
|
|
352
|
+
}),
|
|
353
|
+
},
|
|
354
|
+
} satisfies Dictionary;
|
|
355
|
+
|
|
356
|
+
export default copyButtonContent;
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
```javascript fileName="src/components/CopyButton/CopyButton.content.mjs" codeFormat="esm"
|
|
360
|
+
import { t } from "intlayer";
|
|
361
|
+
|
|
362
|
+
/** @type {import('intlayer').Dictionary} */
|
|
363
|
+
const copyButtonContent = {
|
|
364
|
+
key: "copy-button",
|
|
365
|
+
content: {
|
|
366
|
+
label: t({
|
|
367
|
+
en: "Copy content",
|
|
368
|
+
fr: "Copier le contenu",
|
|
369
|
+
es: "Copiar contenido",
|
|
370
|
+
}),
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
export default copyButtonContent;
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
```javascript fileName="src/components/CopyButton/CopyButton.content.cjs" codeFormat="commonjs"
|
|
378
|
+
const { t } = require("intlayer");
|
|
379
|
+
|
|
380
|
+
/** @type {import('intlayer').Dictionary} */
|
|
381
|
+
const copyButtonContent = {
|
|
382
|
+
key: "copy-button",
|
|
383
|
+
content: {
|
|
384
|
+
label: t({
|
|
385
|
+
en: "Copy content",
|
|
386
|
+
fr: "Copier le contenu",
|
|
387
|
+
es: "Copiar contenido",
|
|
388
|
+
}),
|
|
389
|
+
},
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
module.exports = copyButtonContent;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
> 有关更多内容声明格式和功能,请参阅 [内容声明文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dictionary/content_file.md)。
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## 在组件中使用 `useIntlayer`
|
|
400
|
+
|
|
401
|
+
```tsx fileName="src/components/CopyButton/index.tsx" codeFormat="typescript"
|
|
402
|
+
"use client";
|
|
403
|
+
|
|
404
|
+
import { type FC } from "react";
|
|
405
|
+
import { useIntlayer } from "react-intlayer";
|
|
406
|
+
|
|
407
|
+
type CopyButtonProps = {
|
|
408
|
+
content: string;
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
export const CopyButton: FC<CopyButtonProps> = ({ content }) => {
|
|
412
|
+
const { label } = useIntlayer("copy-button");
|
|
413
|
+
|
|
414
|
+
return (
|
|
415
|
+
<button
|
|
416
|
+
onClick={() => navigator.clipboard.writeText(content)}
|
|
417
|
+
aria-label={label.value}
|
|
418
|
+
title={label.value}
|
|
419
|
+
>
|
|
420
|
+
复制
|
|
421
|
+
</button>
|
|
422
|
+
);
|
|
423
|
+
};
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
`useIntlayer` 会返回由最近的 `IntlayerProvider` 提供的当前语言的编译后的字典。在 Storybook 工具栏中切换语言会自动重新渲染对应的 story 并更新翻译。
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## 为国际化组件编写 Story
|
|
431
|
+
|
|
432
|
+
在配置好 `IntlayerProvider` 装饰器之后,您的 story 工作方式与以前完全相同。语言工具栏控制整个画布的当前语言:
|
|
433
|
+
|
|
434
|
+
```tsx fileName="src/components/CopyButton/CopyButton.stories.tsx" codeFormat="typescript"
|
|
435
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
436
|
+
import { CopyButton } from ".";
|
|
437
|
+
|
|
438
|
+
const meta: Meta<typeof CopyButton> = {
|
|
439
|
+
title: "Components/CopyButton",
|
|
440
|
+
component: CopyButton,
|
|
441
|
+
tags: ["autodocs"],
|
|
442
|
+
argTypes: {
|
|
443
|
+
content: { control: "text" },
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
export default meta;
|
|
448
|
+
type Story = StoryObj<typeof CopyButton>;
|
|
449
|
+
|
|
450
|
+
/** 默认 Story — 在工具栏中切换语言来预览翻译。 */
|
|
451
|
+
export const Default: Story = {
|
|
452
|
+
args: {
|
|
453
|
+
content: "npm install intlayer react-intlayer",
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
/** 在代码块内渲染该按钮,这是一个常见的现实用例。 */
|
|
458
|
+
export const InsideCodeBlock: Story = {
|
|
459
|
+
render: (args) => (
|
|
460
|
+
<div style={{ position: "relative", display: "inline-block" }}>
|
|
461
|
+
<pre style={{ background: "#1e1e1e", color: "#fff", padding: "1rem" }}>
|
|
462
|
+
<code>{args.content}</code>
|
|
463
|
+
</pre>
|
|
464
|
+
<CopyButton
|
|
465
|
+
content={args.content}
|
|
466
|
+
style={{ position: "absolute", top: 8, right: 8 }}
|
|
467
|
+
/>
|
|
468
|
+
</div>
|
|
469
|
+
),
|
|
470
|
+
args: {
|
|
471
|
+
content: "npx intlayer init",
|
|
472
|
+
},
|
|
473
|
+
};
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
> 每个 story 都会从工具栏继承 `locale` 全局变量,因此您可以在不更改任何 story 代码的情况下验证每种语言。
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## 在 Story 中测试翻译
|
|
481
|
+
|
|
482
|
+
使用 Storybook 的 `play` 函数来断言在指定语言下是否渲染了正确的翻译文本:
|
|
483
|
+
|
|
484
|
+
```tsx fileName="src/components/CopyButton/CopyButton.stories.tsx" codeFormat="typescript"
|
|
485
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
486
|
+
import { expect, within } from "@storybook/test";
|
|
487
|
+
import { CopyButton } from ".";
|
|
488
|
+
|
|
489
|
+
const meta: Meta<typeof CopyButton> = {
|
|
490
|
+
title: "Components/CopyButton",
|
|
491
|
+
component: CopyButton,
|
|
492
|
+
tags: ["autodocs"],
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
export default meta;
|
|
496
|
+
type Story = StoryObj<typeof CopyButton>;
|
|
497
|
+
|
|
498
|
+
export const AccessibleLabel: Story = {
|
|
499
|
+
args: { content: "Hello World" },
|
|
500
|
+
play: async ({ canvasElement }) => {
|
|
501
|
+
const canvas = within(canvasElement);
|
|
502
|
+
const button = canvas.getByRole("button");
|
|
503
|
+
|
|
504
|
+
// 验证按钮是否具有非空的无障碍名称
|
|
505
|
+
await expect(button).toHaveAccessibleName();
|
|
506
|
+
// 验证按钮未被禁用
|
|
507
|
+
await expect(button).not.toBeDisabled();
|
|
508
|
+
// 验证键盘可访问性
|
|
509
|
+
await expect(button).toHaveAttribute("tabindex", "0");
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## 其他资源
|
|
517
|
+
|
|
518
|
+
- [Intlayer 配置参考](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/configuration.md)
|
|
519
|
+
- [内容声明文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dictionary/content_file.md)
|
|
520
|
+
- [Intlayer 命令行界面文档](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/cli/index.md)
|
|
521
|
+
- [Storybook 文档](https://storybook.js.org/docs)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/docs",
|
|
3
|
-
"version": "8.4.
|
|
3
|
+
"version": "8.4.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Intlayer documentation",
|
|
6
6
|
"keywords": [
|
|
@@ -72,13 +72,13 @@
|
|
|
72
72
|
"watch": "webpack --config ./webpack.config.ts --watch"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@intlayer/config": "8.4.
|
|
76
|
-
"@intlayer/core": "8.4.
|
|
77
|
-
"@intlayer/types": "8.4.
|
|
75
|
+
"@intlayer/config": "8.4.6",
|
|
76
|
+
"@intlayer/core": "8.4.6",
|
|
77
|
+
"@intlayer/types": "8.4.6"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@intlayer/api": "8.4.
|
|
81
|
-
"@intlayer/cli": "8.4.
|
|
80
|
+
"@intlayer/api": "8.4.6",
|
|
81
|
+
"@intlayer/cli": "8.4.6",
|
|
82
82
|
"@types/node": "25.5.0",
|
|
83
83
|
"@utils/ts-config": "1.0.4",
|
|
84
84
|
"@utils/ts-config-types": "1.0.4",
|
|
@@ -1413,6 +1413,26 @@ export const docsEntry = {
|
|
|
1413
1413
|
vi: readLocale('intlayer_with_react_router_v7_fs_routes.md', 'vi'),
|
|
1414
1414
|
uk: readLocale('intlayer_with_react_router_v7_fs_routes.md', 'uk'),
|
|
1415
1415
|
} as unknown as Record<LocalesValues, Promise<string>>,
|
|
1416
|
+
'./docs/en/intlayer_with_storybook.md': {
|
|
1417
|
+
en: readLocale('intlayer_with_storybook.md', 'en'),
|
|
1418
|
+
ru: readLocale('intlayer_with_storybook.md', 'ru'),
|
|
1419
|
+
ja: readLocale('intlayer_with_storybook.md', 'ja'),
|
|
1420
|
+
fr: readLocale('intlayer_with_storybook.md', 'fr'),
|
|
1421
|
+
ko: readLocale('intlayer_with_storybook.md', 'ko'),
|
|
1422
|
+
zh: readLocale('intlayer_with_storybook.md', 'zh'),
|
|
1423
|
+
es: readLocale('intlayer_with_storybook.md', 'es'),
|
|
1424
|
+
de: readLocale('intlayer_with_storybook.md', 'de'),
|
|
1425
|
+
ar: readLocale('intlayer_with_storybook.md', 'ar'),
|
|
1426
|
+
it: readLocale('intlayer_with_storybook.md', 'it'),
|
|
1427
|
+
'en-GB': readLocale('intlayer_with_storybook.md', 'en-GB'),
|
|
1428
|
+
pt: readLocale('intlayer_with_storybook.md', 'pt'),
|
|
1429
|
+
hi: readLocale('intlayer_with_storybook.md', 'hi'),
|
|
1430
|
+
tr: readLocale('intlayer_with_storybook.md', 'tr'),
|
|
1431
|
+
pl: readLocale('intlayer_with_storybook.md', 'pl'),
|
|
1432
|
+
id: readLocale('intlayer_with_storybook.md', 'id'),
|
|
1433
|
+
vi: readLocale('intlayer_with_storybook.md', 'vi'),
|
|
1434
|
+
uk: readLocale('intlayer_with_storybook.md', 'uk'),
|
|
1435
|
+
} as unknown as Record<LocalesValues, Promise<string>>,
|
|
1416
1436
|
'./docs/en/intlayer_with_svelte_kit.md': {
|
|
1417
1437
|
en: readLocale('intlayer_with_svelte_kit.md', 'en'),
|
|
1418
1438
|
ru: readLocale('intlayer_with_svelte_kit.md', 'ru'),
|