@intlayer/docs 9.0.0-canary.7 → 9.0.0-canary.8
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 +0 -20
- package/dist/cjs/generated/docs.entry.cjs.map +1 -1
- package/dist/esm/generated/docs.entry.mjs +0 -20
- package/dist/esm/generated/docs.entry.mjs.map +1 -1
- package/dist/types/generated/docs.entry.d.ts +0 -1
- package/dist/types/generated/docs.entry.d.ts.map +1 -1
- package/docs/ar/dynamic_dictionaries/index.md +24 -18
- package/docs/ar/dynamic_dictionaries/variants.md +280 -22
- package/docs/ar/plugins/sync-json.md +0 -1
- package/docs/ar/releases/v9.md +35 -12
- package/docs/de/dynamic_dictionaries/index.md +22 -22
- package/docs/de/dynamic_dictionaries/variants.md +272 -14
- package/docs/de/plugins/sync-json.md +0 -1
- package/docs/de/releases/v9.md +35 -12
- package/docs/en/dynamic_dictionaries/index.md +20 -14
- package/docs/en/dynamic_dictionaries/variants.md +266 -8
- package/docs/en/plugins/sync-json.md +0 -1
- package/docs/en/releases/v9.md +35 -12
- package/docs/en-GB/dynamic_dictionaries/index.md +21 -15
- package/docs/en-GB/dynamic_dictionaries/variants.md +268 -10
- package/docs/en-GB/plugins/sync-json.md +0 -1
- package/docs/en-GB/releases/v9.md +35 -12
- package/docs/es/dynamic_dictionaries/index.md +25 -19
- package/docs/es/dynamic_dictionaries/variants.md +275 -17
- package/docs/es/plugins/sync-json.md +0 -1
- package/docs/es/releases/v9.md +35 -12
- package/docs/fr/dynamic_dictionaries/index.md +26 -20
- package/docs/fr/dynamic_dictionaries/variants.md +273 -15
- package/docs/fr/plugins/sync-json.md +0 -1
- package/docs/fr/releases/v9.md +35 -12
- package/docs/hi/dynamic_dictionaries/index.md +28 -22
- package/docs/hi/dynamic_dictionaries/variants.md +282 -24
- package/docs/hi/plugins/sync-json.md +0 -1
- package/docs/hi/releases/v9.md +35 -12
- package/docs/id/dynamic_dictionaries/index.md +25 -19
- package/docs/id/dynamic_dictionaries/variants.md +274 -16
- package/docs/id/plugins/sync-json.md +0 -1
- package/docs/id/releases/v9.md +35 -12
- package/docs/it/dynamic_dictionaries/index.md +26 -20
- package/docs/it/dynamic_dictionaries/variants.md +273 -15
- package/docs/it/plugins/sync-json.md +0 -1
- package/docs/it/releases/v9.md +35 -12
- package/docs/ja/dynamic_dictionaries/index.md +27 -21
- package/docs/ja/dynamic_dictionaries/variants.md +276 -18
- package/docs/ja/plugins/sync-json.md +0 -1
- package/docs/ja/releases/v9.md +35 -12
- package/docs/ko/dynamic_dictionaries/index.md +23 -17
- package/docs/ko/dynamic_dictionaries/variants.md +273 -15
- package/docs/ko/plugins/sync-json.md +0 -1
- package/docs/ko/releases/v9.md +35 -12
- package/docs/pl/dynamic_dictionaries/index.md +26 -20
- package/docs/pl/dynamic_dictionaries/variants.md +275 -17
- package/docs/pl/plugins/sync-json.md +0 -1
- package/docs/pl/releases/v9.md +35 -12
- package/docs/pt/dynamic_dictionaries/index.md +25 -19
- package/docs/pt/dynamic_dictionaries/variants.md +276 -18
- package/docs/pt/plugins/sync-json.md +0 -1
- package/docs/pt/releases/v9.md +35 -12
- package/docs/ru/dynamic_dictionaries/index.md +25 -19
- package/docs/ru/dynamic_dictionaries/variants.md +277 -19
- package/docs/ru/plugins/sync-json.md +0 -1
- package/docs/ru/releases/v9.md +35 -12
- package/docs/tr/dynamic_dictionaries/index.md +27 -21
- package/docs/tr/dynamic_dictionaries/variants.md +274 -16
- package/docs/tr/plugins/sync-json.md +0 -1
- package/docs/tr/releases/v9.md +35 -12
- package/docs/uk/dynamic_dictionaries/index.md +25 -19
- package/docs/uk/dynamic_dictionaries/variants.md +277 -19
- package/docs/uk/plugins/sync-json.md +0 -1
- package/docs/uk/releases/v9.md +35 -12
- package/docs/vi/dynamic_dictionaries/index.md +28 -22
- package/docs/vi/dynamic_dictionaries/variants.md +278 -20
- package/docs/vi/plugins/sync-json.md +0 -1
- package/docs/vi/releases/v9.md +35 -12
- package/docs/zh/dynamic_dictionaries/index.md +24 -18
- package/docs/zh/dynamic_dictionaries/variants.md +274 -16
- package/docs/zh/plugins/sync-json.md +0 -1
- package/docs/zh/releases/v9.md +35 -12
- package/package.json +6 -6
- package/src/generated/docs.entry.ts +0 -20
- package/docs/ar/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/de/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/en/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/en-GB/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/es/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/fr/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/hi/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/id/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/it/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/ja/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/ko/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/pl/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/pt/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/ru/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/tr/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/uk/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/vi/dynamic_dictionaries/dynamic_content.md +0 -271
- package/docs/zh/dynamic_dictionaries/dynamic_content.md +0 -271
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2026-06-12
|
|
3
|
-
updatedAt: 2026-06-
|
|
4
|
-
title: Biến
|
|
5
|
-
description:
|
|
3
|
+
updatedAt: 2026-06-26
|
|
4
|
+
title: Biến thể
|
|
5
|
+
description: Dùng trường metadata variant trong các tệp nội dung Intlayer để khai báo các lựa chọn nội dung được đặt tên hoặc có cấu trúc — thử nghiệm A/B, banner theo mùa, nội dung gắn feature flag, bản ghi CMS, nội dung riêng theo người dùng — và chuyển đổi giữa chúng trong thời gian chạy mà không cần đổi mã.
|
|
6
6
|
keywords:
|
|
7
|
-
- Biến
|
|
7
|
+
- Biến thể
|
|
8
8
|
- Thử nghiệm A/B
|
|
9
|
-
-
|
|
10
|
-
- Nội
|
|
9
|
+
- Feature Flag
|
|
10
|
+
- Nội dung động
|
|
11
|
+
- Bản ghi động
|
|
12
|
+
- CMS
|
|
11
13
|
- Intlayer
|
|
12
14
|
- Quốc tế hóa
|
|
13
15
|
slugs:
|
|
@@ -17,17 +19,27 @@ slugs:
|
|
|
17
19
|
history:
|
|
18
20
|
- version: 9.0.0
|
|
19
21
|
date: 2026-06-12
|
|
20
|
-
changes: "Phát hành tính năng
|
|
22
|
+
changes: "Phát hành tính năng biến thể"
|
|
23
|
+
- version: 9.1.0
|
|
24
|
+
date: 2026-06-26
|
|
25
|
+
changes: "`variant` giờ chấp nhận một chuỗi hoặc một đối tượng — `meta` / bản ghi động trước đây được khai báo dưới dạng biến thể đối tượng"
|
|
21
26
|
author: aymericzip
|
|
22
27
|
---
|
|
23
28
|
|
|
24
|
-
# Biến
|
|
29
|
+
# Biến thể
|
|
25
30
|
|
|
26
|
-
Một **biến thể**
|
|
31
|
+
Một **biến thể** là một tập hợp các tệp nội dung dùng chung `key` từ điển nhưng mỗi tệp mang một giá trị `variant` khác nhau. Intlayer phục vụ tệp phù hợp dựa trên bộ chọn được truyền cho `useIntlayer`.
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
Giá trị `variant` có thể có **hai dạng**:
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
- **Một chuỗi** — một lựa chọn được đặt tên duy nhất (thử nghiệm A/B, banner theo mùa, feature flag).
|
|
36
|
+
- **Một đối tượng** — một bộ phân biệt có cấu trúc được định địa chỉ bằng một tập hợp trường (bản ghi CMS, nội dung riêng theo người dùng, bất kỳ nội dung nào được khóa bằng một ID mờ). Toàn bộ đối tượng chính là danh tính: bộ chọn phải cung cấp một đối tượng **bằng nhau** để phân giải mục.
|
|
37
|
+
|
|
38
|
+
> Dạng đối tượng thay thế trường `meta` trước đây. Ở bất cứ đâu trước kia bạn viết `meta: { id, … }`, hãy viết `variant: { id, … }`, và chọn nó bằng `{ variant: { id, … } }`.
|
|
39
|
+
|
|
40
|
+
## Biến thể được đặt tên (chuỗi)
|
|
41
|
+
|
|
42
|
+
Mỗi tệp đại diện cho một lựa chọn được đặt tên. Bỏ qua `variant` (hoặc đặt thành `"default"`) đánh dấu nó là dự phòng.
|
|
31
43
|
|
|
32
44
|
```ts fileName="hero-banner.content.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
33
45
|
import { t, type Dictionary } from "intlayer";
|
|
@@ -65,9 +77,9 @@ const dictionary = {
|
|
|
65
77
|
export default dictionary;
|
|
66
78
|
```
|
|
67
79
|
|
|
68
|
-
|
|
80
|
+
### Sử dụng biến thể được đặt tên
|
|
69
81
|
|
|
70
|
-
|
|
82
|
+
#### Biến thể mặc định
|
|
71
83
|
|
|
72
84
|
<Tabs group="framework">
|
|
73
85
|
<Tab label="React" value="react">
|
|
@@ -209,7 +221,7 @@ export default dictionary;
|
|
|
209
221
|
</Tab>
|
|
210
222
|
</Tabs>
|
|
211
223
|
|
|
212
|
-
|
|
224
|
+
#### Biến thể được đặt tên
|
|
213
225
|
|
|
214
226
|
```tsx
|
|
215
227
|
const { headline, cta } = useIntlayer("hero-banner", {
|
|
@@ -217,18 +229,264 @@ const { headline, cta } = useIntlayer("hero-banner", {
|
|
|
217
229
|
});
|
|
218
230
|
```
|
|
219
231
|
|
|
220
|
-
|
|
232
|
+
#### Biến thể được đặt tên với locale tường minh
|
|
221
233
|
|
|
222
234
|
```tsx
|
|
223
235
|
const content = useIntlayer("hero-banner", {
|
|
224
236
|
variant: "black_friday",
|
|
225
|
-
locale: "
|
|
237
|
+
locale: "fr",
|
|
226
238
|
});
|
|
227
239
|
```
|
|
228
240
|
|
|
241
|
+
## Biến thể đối tượng (có cấu trúc)
|
|
242
|
+
|
|
243
|
+
Một biến thể đối tượng định địa chỉ nội dung bằng một tập hợp cặp khóa-giá trị tùy ý được khai báo trong trường `variant` — giúp mô hình hóa bản ghi CMS, nội dung riêng theo người dùng, hoặc bất kỳ nội dung nào có khóa là một ID mờ. **Toàn bộ đối tượng** chính là danh tính: bộ chọn phải cung cấp một đối tượng bằng nhau để mục được phân giải.
|
|
244
|
+
|
|
245
|
+
```ts fileName="product.abc.content.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
246
|
+
import { t, type Dictionary } from "intlayer";
|
|
247
|
+
|
|
248
|
+
const dictionary = {
|
|
249
|
+
key: "product",
|
|
250
|
+
variant: { id: "prod_abc", userId: "user_123" },
|
|
251
|
+
content: {
|
|
252
|
+
name: t({ en: "Widget Pro", fr: "Widget Pro" }),
|
|
253
|
+
description: t({ en: "The best widget.", fr: "Le meilleur widget." }),
|
|
254
|
+
},
|
|
255
|
+
} satisfies Dictionary;
|
|
256
|
+
|
|
257
|
+
export default dictionary;
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```ts fileName="product.abcd.content.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
261
|
+
import { t, type Dictionary } from "intlayer";
|
|
262
|
+
|
|
263
|
+
const dictionary = {
|
|
264
|
+
key: "product",
|
|
265
|
+
variant: { id: "prod_abcd", userId: "user_123" },
|
|
266
|
+
content: {
|
|
267
|
+
name: t({ en: "Widget Lite", fr: "Widget Lite" }),
|
|
268
|
+
description: t({ en: "A lighter option.", fr: "Une option plus légère." }),
|
|
269
|
+
},
|
|
270
|
+
} satisfies Dictionary;
|
|
271
|
+
|
|
272
|
+
export default dictionary;
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Sử dụng biến thể đối tượng
|
|
276
|
+
|
|
277
|
+
Truyền đối tượng khớp cho `variant`. Mọi trường được khai báo trên từ điển phải được cung cấp và bằng nhau; nếu không, kết quả là `null`. Thứ tự các trường không quan trọng.
|
|
278
|
+
|
|
279
|
+
<Tabs group="framework">
|
|
280
|
+
<Tab label="React" value="react">
|
|
281
|
+
```tsx fileName="Product.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
282
|
+
import { useIntlayer } from "react-intlayer";
|
|
283
|
+
|
|
284
|
+
export const Product = ({
|
|
285
|
+
productId,
|
|
286
|
+
userId,
|
|
287
|
+
}: {
|
|
288
|
+
productId: string;
|
|
289
|
+
userId: string;
|
|
290
|
+
}) => {
|
|
291
|
+
const content = useIntlayer("product", {
|
|
292
|
+
variant: { id: productId, userId },
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
if (!content) return null;
|
|
296
|
+
|
|
297
|
+
return <p>{content.description}</p>;
|
|
298
|
+
};
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
</Tab>
|
|
302
|
+
<Tab label="Next.js" value="nextjs">
|
|
303
|
+
```tsx fileName="Product.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
304
|
+
import { useIntlayer } from "next-intlayer";
|
|
305
|
+
|
|
306
|
+
export const Product = ({
|
|
307
|
+
productId,
|
|
308
|
+
userId,
|
|
309
|
+
}: {
|
|
310
|
+
productId: string;
|
|
311
|
+
userId: string;
|
|
312
|
+
}) => {
|
|
313
|
+
const content = useIntlayer("product", {
|
|
314
|
+
variant: { id: productId, userId },
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
if (!content) return null;
|
|
318
|
+
|
|
319
|
+
return <p>{content.description}</p>;
|
|
320
|
+
};
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
</Tab>
|
|
324
|
+
<Tab label="Vue" value="vue">
|
|
325
|
+
```vue fileName="Product.vue" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
326
|
+
<script setup>
|
|
327
|
+
import { useIntlayer } from "vue-intlayer";
|
|
328
|
+
|
|
329
|
+
const props = defineProps({
|
|
330
|
+
productId: String,
|
|
331
|
+
userId: String,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const content = useIntlayer("product", {
|
|
335
|
+
variant: { id: props.productId, userId: props.userId },
|
|
336
|
+
});
|
|
337
|
+
</script>
|
|
338
|
+
|
|
339
|
+
<template>
|
|
340
|
+
<p v-if="content">{{ content.description }}</p>
|
|
341
|
+
</template>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
</Tab>
|
|
345
|
+
<Tab label="Svelte" value="svelte">
|
|
346
|
+
```svelte fileName="Product.svelte" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
347
|
+
<script lang="ts">
|
|
348
|
+
import { useIntlayer } from "svelte-intlayer";
|
|
349
|
+
|
|
350
|
+
export let productId: string;
|
|
351
|
+
export let userId: string;
|
|
352
|
+
|
|
353
|
+
const content = useIntlayer("product", {
|
|
354
|
+
variant: { id: productId, userId },
|
|
355
|
+
});
|
|
356
|
+
</script>
|
|
357
|
+
|
|
358
|
+
{#if $content}
|
|
359
|
+
<p>{$content.description}</p>
|
|
360
|
+
{/if}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
</Tab>
|
|
364
|
+
<Tab label="Preact" value="preact">
|
|
365
|
+
```tsx fileName="Product.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
366
|
+
import { useIntlayer } from "preact-intlayer";
|
|
367
|
+
|
|
368
|
+
export const Product = ({
|
|
369
|
+
productId,
|
|
370
|
+
userId,
|
|
371
|
+
}: {
|
|
372
|
+
productId: string;
|
|
373
|
+
userId: string;
|
|
374
|
+
}) => {
|
|
375
|
+
const content = useIntlayer("product", {
|
|
376
|
+
variant: { id: productId, userId },
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
if (!content) return null;
|
|
380
|
+
|
|
381
|
+
return <p>{content.description}</p>;
|
|
382
|
+
};
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
</Tab>
|
|
386
|
+
<Tab label="Solid" value="solid">
|
|
387
|
+
```tsx fileName="Product.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
388
|
+
import { useIntlayer } from "solid-intlayer";
|
|
389
|
+
|
|
390
|
+
export const Product = (props: {
|
|
391
|
+
productId: string;
|
|
392
|
+
userId: string;
|
|
393
|
+
}) => {
|
|
394
|
+
const content = useIntlayer("product", {
|
|
395
|
+
variant: { id: props.productId, userId: props.userId },
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
return (
|
|
399
|
+
<>
|
|
400
|
+
{content() && <p>{content().description}</p>}
|
|
401
|
+
</>
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
</Tab>
|
|
407
|
+
<Tab label="Angular" value="angular">
|
|
408
|
+
```typescript fileName="product.component.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
409
|
+
import { Component, Input, OnInit } from "@angular/core";
|
|
410
|
+
import { useIntlayer } from "angular-intlayer";
|
|
411
|
+
|
|
412
|
+
@Component({
|
|
413
|
+
selector: "app-product",
|
|
414
|
+
template: `
|
|
415
|
+
@if (content()) {
|
|
416
|
+
<p>{{ content().description }}</p>
|
|
417
|
+
}
|
|
418
|
+
`,
|
|
419
|
+
})
|
|
420
|
+
export class ProductComponent implements OnInit {
|
|
421
|
+
@Input() productId!: string;
|
|
422
|
+
@Input() userId!: string;
|
|
423
|
+
|
|
424
|
+
content: any;
|
|
425
|
+
|
|
426
|
+
ngOnInit() {
|
|
427
|
+
this.content = useIntlayer("product", {
|
|
428
|
+
variant: { id: this.productId, userId: this.userId },
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
</Tab>
|
|
435
|
+
<Tab label="Vanilla JS" value="vanilla">
|
|
436
|
+
```javascript fileName="product.js"
|
|
437
|
+
import { useIntlayer } from "vanilla-intlayer";
|
|
438
|
+
|
|
439
|
+
const content = useIntlayer("product", {
|
|
440
|
+
variant: { id: "prod_abcd", userId: "user_123" },
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
if (content) {
|
|
444
|
+
document.body.innerHTML = `<p>${content.description}</p>`;
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
</Tab>
|
|
449
|
+
</Tabs>
|
|
450
|
+
|
|
451
|
+
#### Với locale tường minh
|
|
452
|
+
|
|
453
|
+
```tsx
|
|
454
|
+
const content = useIntlayer("product", {
|
|
455
|
+
variant: { id: "prod_abc", userId: "user_123" },
|
|
456
|
+
locale: "fr",
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
#### Thiếu trường — không khớp
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
// Trả về null: thiếu `userId`, nên đối tượng không khớp với biến thể đã khai báo
|
|
464
|
+
const content = useIntlayer("product", { variant: { id: "prod_abc" } });
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Chế độ tải
|
|
468
|
+
|
|
469
|
+
Biến thể đối tượng thường được tải lười. Đặt `importMode` trên từ điển để kiểm soát điều này:
|
|
470
|
+
|
|
471
|
+
```ts contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
472
|
+
const dictionary = {
|
|
473
|
+
key: "product",
|
|
474
|
+
importMode: "fetch", // or "dynamic"
|
|
475
|
+
variant: { id: "prod_abc", userId: "user_123" },
|
|
476
|
+
content: { … },
|
|
477
|
+
} satisfies Dictionary;
|
|
478
|
+
|
|
479
|
+
export default dictionary;
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Xem [tối ưu hóa bundle](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/bundle_optimization.md) để biết chi tiết về các chế độ `static`, `dynamic` và `fetch`.
|
|
483
|
+
|
|
229
484
|
## Các trường hợp sử dụng điển hình
|
|
230
485
|
|
|
231
|
-
-
|
|
232
|
-
-
|
|
233
|
-
- Thông điệp
|
|
234
|
-
-
|
|
486
|
+
- Thử nghiệm A/B nội dung được điều khiển bằng khóa thí nghiệm
|
|
487
|
+
- Banner theo mùa hoặc khuyến mãi
|
|
488
|
+
- Thông điệp gắn feature flag
|
|
489
|
+
- Chiến dịch tiếp thị riêng theo locale
|
|
490
|
+
- Nội dung tiếp thị theo từng sản phẩm được quản lý trong CMS
|
|
491
|
+
- Nội dung riêng theo người dùng hoặc tài khoản
|
|
492
|
+
- Bất kỳ nội dung nào được khóa bằng một ID mờ trong thời gian chạy
|
|
@@ -108,7 +108,6 @@ const config: IntlayerConfig = {
|
|
|
108
108
|
* Will load, and write the output and translations back to the JSON files in the locales directory
|
|
109
109
|
*/
|
|
110
110
|
syncJSON({
|
|
111
|
-
format: "i18next",
|
|
112
111
|
source: ({ key, locale }) => `./locales/${locale}/${key}.json`,
|
|
113
112
|
priority: 0,
|
|
114
113
|
format: "i18next",
|
package/docs/vi/releases/v9.md
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2026-06-14
|
|
3
|
-
updatedAt: 2026-06-
|
|
3
|
+
updatedAt: 2026-06-26
|
|
4
4
|
title: Intlayer v9 Mới - Có gì mới?
|
|
5
|
-
description: Khám phá các tính năng mới trong Intlayer v9. Giới thiệu các package tương thích drop-in cho các thư viện i18n phổ biến và hỗ trợ Collections
|
|
5
|
+
description: Khám phá các tính năng mới trong Intlayer v9. Giới thiệu các package tương thích drop-in cho các thư viện i18n phổ biến và hỗ trợ Collections và Variants.
|
|
6
6
|
keywords:
|
|
7
7
|
- Intlayer
|
|
8
8
|
- Compatibility
|
|
9
9
|
- Migration
|
|
10
10
|
- Collections
|
|
11
11
|
- Variants
|
|
12
|
-
- Dynamic Records
|
|
13
12
|
- i18next
|
|
14
13
|
- next-intl
|
|
15
14
|
- vue-i18n
|
|
@@ -22,7 +21,7 @@ author: aymericzip
|
|
|
22
21
|
|
|
23
22
|
# Intlayer v9 Mới - Có gì mới?
|
|
24
23
|
|
|
25
|
-
Chào mừng bạn đến với Intlayer v9! Bản phát hành lớn này đánh dấu một cột mốc quan trọng trong việc đơn giản hóa lộ trình migration sang Intlayer với các **Compat Adapter Packages** dành cho các thư viện i18n phổ biến (`react-i18next`, `next-intl`, `vue-i18n`, v.v.) và bổ sung hỗ trợ cho các cấu trúc nội dung phong phú: **Collections
|
|
24
|
+
Chào mừng bạn đến với Intlayer v9! Bản phát hành lớn này đánh dấu một cột mốc quan trọng trong việc đơn giản hóa lộ trình migration sang Intlayer với các **Compat Adapter Packages** dành cho các thư viện i18n phổ biến (`react-i18next`, `next-intl`, `vue-i18n`, v.v.) và bổ sung hỗ trợ cho các cấu trúc nội dung phong phú: **Collections** và **Variants**.
|
|
26
25
|
|
|
27
26
|
## Mục lục
|
|
28
27
|
|
|
@@ -41,8 +40,10 @@ Bằng cách thay thế các import, bạn sẽ nhận được tất cả các
|
|
|
41
40
|
- `@intlayer/i18next`
|
|
42
41
|
- `@intlayer/react-i18next`
|
|
43
42
|
- `@intlayer/next-intl`
|
|
43
|
+
- `@intlayer/react-intl`
|
|
44
44
|
- `@intlayer/next-i18next`
|
|
45
45
|
- `@intlayer/vue-i18n`
|
|
46
|
+
- `@intlayer/lingui`
|
|
46
47
|
|
|
47
48
|
Ví dụ, chỉ cần thay đổi:
|
|
48
49
|
|
|
@@ -100,7 +101,7 @@ Tất cả các compat adapter giờ đây đều định tuyến việc xử l
|
|
|
100
101
|
|
|
101
102
|
---
|
|
102
103
|
|
|
103
|
-
## Chi tiết tính năng: Collections
|
|
104
|
+
## Chi tiết tính năng: Collections & Variants
|
|
104
105
|
|
|
105
106
|
Intlayer v9 mở rộng vượt ra ngoài các đối tượng key-value tĩnh, cho phép các dictionary khai báo các cấu trúc layout động được định kiểu đầy đủ (fully typed) từ đầu đến cuối.
|
|
106
107
|
|
|
@@ -144,6 +145,8 @@ const faq = useIntlayer("faq", { item: 1 }); // -> { question: string, answer: s
|
|
|
144
145
|
|
|
145
146
|
Cung cấp các bản thử nghiệm A/B, seasonal header, feature flag, hoặc các landing page tùy chỉnh:
|
|
146
147
|
|
|
148
|
+
#### Các biến thể chuỗi (ví dụ: thử nghiệm A/B)
|
|
149
|
+
|
|
147
150
|
```ts fileName="hero.content.ts"
|
|
148
151
|
import { t, type Dictionary } from "intlayer";
|
|
149
152
|
|
|
@@ -167,16 +170,14 @@ export default {
|
|
|
167
170
|
const banner = useIntlayer("hero-banner", { variant: "black_friday" });
|
|
168
171
|
```
|
|
169
172
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Định nghĩa các dictionary có các mục nhập được tải động tại runtime thông qua các query ID:
|
|
173
|
+
#### Các biến thể đối tượng (ví dụ: Dynamic Records)
|
|
173
174
|
|
|
174
175
|
```ts fileName="product.content.ts"
|
|
175
176
|
import { t, type Dictionary } from "intlayer";
|
|
176
177
|
|
|
177
178
|
export default {
|
|
178
179
|
key: "product-copy",
|
|
179
|
-
|
|
180
|
+
variant: {
|
|
180
181
|
id: "prod_123",
|
|
181
182
|
category: "books",
|
|
182
183
|
},
|
|
@@ -229,7 +230,7 @@ export default defineConfig({
|
|
|
229
230
|
});
|
|
230
231
|
```
|
|
231
232
|
|
|
232
|
-
- **Compiler**: Kích hoạt tự động khi `compiler.enabled` được đặt và một đường dẫn `compiler.output` được cấu hình. Bạn không còn cần phải đăng ký `intlayerCompiler()` riêng biệt.
|
|
233
|
+
- **Compiler**: Kích hoạt tự động khi `compiler.enabled` được đặt thành `true` và một đường dẫn `compiler.output` được cấu hình. Bạn không còn cần phải đăng ký `intlayerCompiler()` riêng biệt.
|
|
233
234
|
- **Proxy**: Kích hoạt tự động dựa trên tùy chọn `routing.enableProxy` mới (`true` theo mặc định). Nó kết nối middleware phát hiện ngôn ngữ / chuyển hướng / ghi đè trong phát triển, xem trước và SSR sản xuất. Bạn không còn cần phải đăng ký `intlayerProxy()` riêng biệt.
|
|
234
235
|
|
|
235
236
|
### Tùy chọn `routing.enableProxy`
|
|
@@ -252,6 +253,27 @@ Các plugin độc lập `intlayerCompiler()` và `intlayerProxy()` vẫn đư
|
|
|
252
253
|
|
|
253
254
|
---
|
|
254
255
|
|
|
256
|
+
## Compiler bị tắt theo mặc định
|
|
257
|
+
|
|
258
|
+
Kể từ Intlayer v9, **compiler bị tắt theo mặc định** (`compiler.enabled` hiện mặc định là `false`). Để bật trích xuất các file `.content.ts` của bạn tại thời điểm build, hãy đặt `compiler.enabled: true` trong cấu hình của bạn:
|
|
259
|
+
|
|
260
|
+
```ts fileName="intlayer.config.ts"
|
|
261
|
+
import type { IntlayerConfig } from "intlayer";
|
|
262
|
+
|
|
263
|
+
const config: IntlayerConfig = {
|
|
264
|
+
compiler: {
|
|
265
|
+
enabled: true, // Mặc định: false — bắt buộc để bật compiler kể từ v9
|
|
266
|
+
output: ({ fileName }) => `./${fileName}.content.ts`,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
export default config;
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Khi compiler bị tắt, Intlayer bỏ qua bước trích xuất tại thời điểm build và dựa vào các dictionary mà bạn đã khai báo. Chỉ bật nó khi bạn muốn plugin bundler (`@intlayer/swc`, `@intlayer/babel`, hoặc plugin Vite `intlayer()`) tự động trích xuất nội dung.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
255
277
|
## React Native: import từ một package duy nhất
|
|
256
278
|
|
|
257
279
|
Trong ứng dụng React Native hoặc Expo, bạn không còn cần phải xoay sở giữa `react-intlayer` và `react-native-intlayer` nữa. Package `react-native-intlayer` hiện **xuất lại toàn bộ API của `react-intlayer`** (các hook, tiện ích và các đường dẫn phụ `/format`, `/html`, và `/markdown`), và `IntlayerProvider` của nó tự động áp dụng các polyfill của React Native.
|
|
@@ -278,13 +300,14 @@ npm install intlayer react-native-intlayer
|
|
|
278
300
|
|
|
279
301
|
Nếu bạn đang nâng cấp từ v8, lưu ý rằng v9 không bao gồm các breaking change. Tuy nhiên, dưới đây là các thay đổi chính:
|
|
280
302
|
|
|
303
|
+
- **Compiler bị tắt theo mặc định**: `compiler.enabled` hiện mặc định là `false`. Nếu bạn dựa vào việc trích xuất các file `.content.ts` tại thời điểm build, hãy đặt `compiler.enabled: true` trong `intlayer.config.ts` của bạn.
|
|
281
304
|
- **Locales & Dialects**: Nếu sử dụng các dependency i18n bên ngoài, hãy thêm các compat adapter plugin tương ứng của chúng vào cấu hình hoặc thiết lập bundler của bạn để tự động rewrite các import.
|
|
282
|
-
- **Custom Selectors**: Khi gọi `useIntlayer`, tham số thứ hai giờ đây được dành riêng cho một đối tượng option chứa `{ locale, item, variant
|
|
305
|
+
- **Custom Selectors**: Khi gọi `useIntlayer`, tham số thứ hai giờ đây được dành riêng cho một đối tượng option chứa `{ locale, item, variant }`. Nếu trước đây bạn đã truyền trực tiếp một chuỗi locale, bạn vẫn có thể làm như vậy, nhưng việc sử dụng đối tượng options được khuyến nghị cho các lựa chọn nâng cao.
|
|
283
306
|
|
|
284
307
|
---
|
|
285
308
|
|
|
286
309
|
## Liên kết hữu ích
|
|
287
310
|
|
|
288
311
|
- [Hướng dẫn về Compat Adapter Packages](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/compat/index.md)
|
|
289
|
-
- [Dynamic Dictionaries - Collections
|
|
312
|
+
- [Dynamic Dictionaries - Collections & Variants](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/dynamic_dictionaries/index.md)
|
|
290
313
|
- [Tài liệu tham khảo cấu hình](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/configuration.md)
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2026-06-12
|
|
3
|
-
updatedAt: 2026-06-
|
|
3
|
+
updatedAt: 2026-06-26
|
|
4
4
|
title: 动态字典
|
|
5
|
-
description: Intlayer
|
|
5
|
+
description: Intlayer 动态字典功能(集合与变体)的概览,用于构建灵活的、由运行时驱动的 i18n 内容。
|
|
6
6
|
keywords:
|
|
7
7
|
- 动态字典
|
|
8
8
|
- 集合
|
|
9
9
|
- 变体
|
|
10
|
-
- 动态记录
|
|
11
10
|
- Intlayer
|
|
12
11
|
- 国际化
|
|
13
12
|
slugs:
|
|
@@ -17,32 +16,39 @@ slugs:
|
|
|
17
16
|
history:
|
|
18
17
|
- version: 9.0.0
|
|
19
18
|
date: 2026-06-12
|
|
20
|
-
changes: "
|
|
19
|
+
changes: "动态字典功能发布"
|
|
20
|
+
- version: 9.1.0
|
|
21
|
+
date: 2026-06-26
|
|
22
|
+
changes: "将动态记录合并到变体中——`variant` 现在接受字符串或对象"
|
|
21
23
|
author: aymericzip
|
|
22
24
|
---
|
|
23
25
|
|
|
24
26
|
# 动态字典
|
|
25
27
|
|
|
26
|
-
Intlayer
|
|
28
|
+
Intlayer 支持两种机制来表达超出每个键单一静态字典的内容。每种机制都通过内容文件中的**顶层元数据字段**声明;无需包装函数。
|
|
27
29
|
|
|
28
|
-
| 功能
|
|
29
|
-
|
|
|
30
|
-
| [集合](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dynamic_dictionaries/collections.md)
|
|
31
|
-
| [变体](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dynamic_dictionaries/variants.md)
|
|
32
|
-
| [动态记录](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dynamic_dictionaries/dynamic_content.md) | `meta: { id, … }` | `{ id, … }` |
|
|
30
|
+
| 功能 | 元数据字段 | `useIntlayer` 中的选择器 |
|
|
31
|
+
| --------------------------------------------------------------------------------------------------------- | --------------------------------------- | ----------------------------------------------- |
|
|
32
|
+
| [集合](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dynamic_dictionaries/collections.md) | `item: N` | `{ item: N }` |
|
|
33
|
+
| [变体](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dynamic_dictionaries/variants.md) | `variant: "name"` _或_ `variant: { … }` | `{ variant: "name" }` _或_ `{ variant: { … } }` |
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
两者都可与 locale 参数组合,并支持通过 `importMode` 进行选择性 / 惰性加载。
|
|
35
36
|
|
|
36
|
-
##
|
|
37
|
+
## 何时使用哪一个
|
|
37
38
|
|
|
38
|
-
- **集合** —
|
|
39
|
-
- **变体** —
|
|
40
|
-
-
|
|
39
|
+
- **集合** — 在单独文件中管理的有序项目列表(FAQ 条目、博客文章、产品)。
|
|
40
|
+
- **变体** — 具名或结构化的内容替代项:
|
|
41
|
+
- 用于 A/B 测试、季节性横幅或功能开关的**字符串**变体;
|
|
42
|
+
- 用于 CMS 记录、用户特定文案,或由一组字段寻址的任何内容的**对象**变体(即以前的“动态记录”)。
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
> 早期版本为按记录键控的内容提供单独的 `meta` 字段。它已合并到 `variant` 中:向 `variant` 传递一个**对象**,而不是使用 `meta`。
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
## 选择器消歧
|
|
47
|
+
|
|
48
|
+
一个键可以同时声明两个维度(例如每个项目都有变体的集合)。它们按以下顺序解析:
|
|
45
49
|
|
|
46
50
|
```
|
|
47
|
-
variant →
|
|
51
|
+
variant → item
|
|
48
52
|
```
|
|
53
|
+
|
|
54
|
+
因此,对 variant × item 键使用 `{ variant: "promo" }` 会以数组形式返回所有 promo 项目,而添加 `{ item: 2 }` 则将其缩小到单个条目。
|