@intlayer/docs 9.3.1 → 9.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/ar/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/de/nextjs-multilingual-seo-comparison.md +9 -9
- package/blog/en/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/en-GB/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/es/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/fr/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/hi/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/id/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/it/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/ja/nextjs-multilingual-seo-comparison.md +9 -9
- package/blog/ko/nextjs-multilingual-seo-comparison.md +9 -9
- package/blog/pl/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/pt/nextjs-multilingual-seo-comparison.md +9 -9
- package/blog/ru/nextjs-multilingual-seo-comparison.md +9 -9
- package/blog/tr/nextjs-multilingual-seo-comparison.md +9 -9
- package/blog/uk/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/vi/nextjs-multilingual-seo-comparison.md +10 -10
- package/blog/zh/nextjs-multilingual-seo-comparison.md +10 -10
- 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/bundle_optimization.md +1 -1
- package/docs/ar/configuration.md +32 -9
- package/docs/ar/dictionary/content_file.md +0 -22
- package/docs/ar/dictionary/function_fetching.md +23 -0
- package/docs/ar/eslint.md +336 -0
- package/docs/ar/intlayer_with_react_router_v7_fs_routes.md +1 -1
- package/docs/bn/configuration.md +34 -9
- package/docs/bn/eslint.md +336 -0
- package/docs/cs/bundle_optimization.md +1 -1
- package/docs/cs/configuration.md +33 -9
- package/docs/cs/eslint.md +336 -0
- package/docs/de/bundle_optimization.md +1 -1
- package/docs/de/configuration.md +34 -9
- package/docs/de/dictionary/content_file.md +0 -22
- package/docs/de/dictionary/function_fetching.md +23 -0
- package/docs/de/eslint.md +336 -0
- package/docs/en/bundle_optimization.md +1 -1
- package/docs/en/configuration.md +34 -9
- package/docs/en/dictionary/content_file.md +0 -22
- package/docs/en/dictionary/function_fetching.md +23 -0
- package/docs/en/eslint.md +336 -0
- package/docs/en/packages/intlayer/getLocalizedPath.md +70 -19
- package/docs/en-GB/configuration.md +33 -9
- package/docs/en-GB/dictionary/content_file.md +0 -22
- package/docs/en-GB/dictionary/function_fetching.md +23 -0
- package/docs/en-GB/eslint.md +336 -0
- package/docs/en-GB/intlayer_with_create_react_app.md +32 -35
- package/docs/en-GB/intlayer_with_react_router_v7_fs_routes.md +1 -1
- package/docs/es/bundle_optimization.md +1 -1
- package/docs/es/configuration.md +35 -9
- package/docs/es/dictionary/content_file.md +0 -22
- package/docs/es/dictionary/function_fetching.md +23 -0
- package/docs/es/eslint.md +336 -0
- package/docs/fr/bundle_optimization.md +1 -1
- package/docs/fr/configuration.md +35 -9
- package/docs/fr/dictionary/content_file.md +0 -22
- package/docs/fr/dictionary/function_fetching.md +23 -0
- package/docs/fr/eslint.md +336 -0
- package/docs/hi/configuration.md +35 -9
- package/docs/hi/dictionary/content_file.md +0 -22
- package/docs/hi/dictionary/function_fetching.md +23 -0
- package/docs/hi/eslint.md +336 -0
- package/docs/hi/intlayer_with_react_router_v7_fs_routes.md +1 -1
- package/docs/hi/intlayer_with_vite+svelte.md +2 -2
- package/docs/id/configuration.md +35 -9
- package/docs/id/dictionary/content_file.md +0 -22
- package/docs/id/dictionary/function_fetching.md +23 -0
- package/docs/id/eslint.md +336 -0
- package/docs/it/bundle_optimization.md +1 -1
- package/docs/it/configuration.md +35 -9
- package/docs/it/dictionary/content_file.md +0 -22
- package/docs/it/dictionary/function_fetching.md +23 -0
- package/docs/it/eslint.md +336 -0
- package/docs/ja/configuration.md +30 -9
- package/docs/ja/dictionary/content_file.md +0 -22
- package/docs/ja/dictionary/function_fetching.md +23 -0
- package/docs/ja/eslint.md +336 -0
- package/docs/ja/intlayer_with_react_router_v7.md +1 -146
- package/docs/ja/intlayer_with_vite+react.md +5 -1
- package/docs/ko/configuration.md +31 -9
- package/docs/ko/dictionary/content_file.md +0 -22
- package/docs/ko/dictionary/function_fetching.md +23 -0
- package/docs/ko/eslint.md +336 -0
- package/docs/ko/intlayer_with_lynx+react.md +4 -0
- package/docs/ko/intlayer_with_react_router_v7_fs_routes.md +1 -1
- package/docs/ko/intlayer_with_storybook.md +5 -5
- package/docs/nl/configuration.md +33 -9
- package/docs/nl/eslint.md +336 -0
- package/docs/pl/bundle_optimization.md +1 -1
- package/docs/pl/configuration.md +34 -9
- package/docs/pl/dictionary/content_file.md +0 -22
- package/docs/pl/dictionary/function_fetching.md +23 -0
- package/docs/pl/eslint.md +336 -0
- package/docs/pl/intlayer_with_astro.md +1 -114
- package/docs/pl/migration_from_i18next_to_intlayer.md +4 -0
- package/docs/pl/migration_from_next-i18next_to_intlayer.md +8 -4
- package/docs/pl/migration_from_next-intl_to_intlayer.md +11 -5
- package/docs/pl/migration_from_nuxtjs_i18n_to_intlayer.md +8 -4
- package/docs/pl/migration_from_react-i18next_to_intlayer.md +8 -4
- package/docs/pl/migration_from_vue-i18n_to_intlayer.md +4 -0
- package/docs/pt/bundle_optimization.md +1 -1
- package/docs/pt/configuration.md +34 -9
- package/docs/pt/dictionary/content_file.md +0 -22
- package/docs/pt/dictionary/function_fetching.md +23 -0
- package/docs/pt/eslint.md +336 -0
- package/docs/pt/intlayer_with_astro.md +1 -114
- package/docs/ru/bundle_optimization.md +1 -1
- package/docs/ru/configuration.md +34 -9
- package/docs/ru/dictionary/content_file.md +0 -22
- package/docs/ru/dictionary/function_fetching.md +23 -0
- package/docs/ru/eslint.md +336 -0
- package/docs/tr/bundle_optimization.md +1 -1
- package/docs/tr/configuration.md +33 -9
- package/docs/tr/dictionary/content_file.md +0 -22
- package/docs/tr/dictionary/function_fetching.md +23 -0
- package/docs/tr/eslint.md +336 -0
- package/docs/uk/configuration.md +35 -9
- package/docs/uk/dictionary/content_file.md +0 -22
- package/docs/uk/dictionary/function_fetching.md +23 -0
- package/docs/uk/eslint.md +336 -0
- package/docs/uk/packages/angular-intlayer/exports.md +2 -2
- package/docs/ur/configuration.md +35 -9
- package/docs/ur/eslint.md +336 -0
- package/docs/vi/bundle_optimization.md +1 -1
- package/docs/vi/configuration.md +33 -9
- package/docs/vi/dictionary/content_file.md +0 -22
- package/docs/vi/dictionary/function_fetching.md +23 -0
- package/docs/vi/eslint.md +336 -0
- package/docs/zh/bundle_optimization.md +1 -1
- package/docs/zh/configuration.md +29 -9
- package/docs/zh/dictionary/content_file.md +0 -22
- package/docs/zh/dictionary/function_fetching.md +23 -0
- package/docs/zh/eslint.md +336 -0
- package/docs/zh/intlayer_with_create_react_app.md +4 -0
- package/docs/zh/intlayer_with_lynx+react.md +4 -0
- package/docs/zh/intlayer_with_nextjs_14.md +0 -2
- package/docs/zh/intlayer_with_nextjs_15.md +0 -2
- package/docs/zh/intlayer_with_nextjs_page_router.md +0 -2
- package/docs/zh/intlayer_with_nuxt.md +1 -1
- package/docs/zh/intlayer_with_react_router_v7.md +4 -0
- package/docs/zh/intlayer_with_react_router_v7_fs_routes.md +4 -0
- package/docs/zh/intlayer_with_solid_start.md +1 -1
- package/docs/zh/intlayer_with_vite+vue.md +0 -2
- package/docs/zh-TW/bundle_optimization.md +1 -1
- package/docs/zh-TW/eslint.md +336 -0
- package/package.json +7 -7
- package/src/generated/docs.entry.ts +20 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-08-12
|
|
3
|
+
updatedAt: 2026-08-13
|
|
4
|
+
title: Plugin ESLint | Quy tắc Lint cho Intlayer
|
|
5
|
+
description: Phát hiện chuỗi văn bản bị hardcode, các lệnh gọi động mà trình biên dịch Intlayer không thể tối ưu hóa và nội dung từ điển không sử dụng với eslint-plugin-intlayer. Hoạt động với ESLint và oxlint trên React, Vue, Svelte, Angular và Astro.
|
|
6
|
+
keywords:
|
|
7
|
+
- Intlayer
|
|
8
|
+
- ESLint
|
|
9
|
+
- oxlint
|
|
10
|
+
- Linting
|
|
11
|
+
- i18n
|
|
12
|
+
- Quốc tế hóa
|
|
13
|
+
- no-raw-text
|
|
14
|
+
- Chuỗi văn bản hardcoded
|
|
15
|
+
- Bản dịch không sử dụng
|
|
16
|
+
- Nội dung thừa
|
|
17
|
+
- React
|
|
18
|
+
- Vue
|
|
19
|
+
- Svelte
|
|
20
|
+
- Angular
|
|
21
|
+
slugs:
|
|
22
|
+
- doc
|
|
23
|
+
- eslint
|
|
24
|
+
history:
|
|
25
|
+
- version: 9.3.1
|
|
26
|
+
date: 2026-08-12
|
|
27
|
+
changes: "Lịch sử khởi tạo"
|
|
28
|
+
author: aymericzip
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Plugin ESLint x OXLint
|
|
32
|
+
|
|
33
|
+
`eslint-plugin-intlayer` giúp bắt các lỗi i18n mà TypeScript không thể phát hiện:
|
|
34
|
+
|
|
35
|
+
1. **Văn bản hardcode** chưa từng được đưa vào từ điển.
|
|
36
|
+
2. **Các lệnh gọi động** vượt qua kiểm tra kiểu và thực thi được, nhưng trình biên dịch Intlayer không thể tối ưu hóa.
|
|
37
|
+
3. **Nội dung thừa (Dead content)** — các từ điển và trường không có bất kỳ phần nào trong dự án đọc (tùy chọn kích hoạt).
|
|
38
|
+
|
|
39
|
+
Các khóa từ điển không xác định, đường dẫn trường không xác định và ngôn ngữ còn thiếu vốn đã là các lỗi biên dịch, vì vậy plugin sẽ không lặp lại chúng.
|
|
40
|
+
|
|
41
|
+
## Cài đặt
|
|
42
|
+
|
|
43
|
+
```bash packageManager="npm"
|
|
44
|
+
npm install --save-dev eslint-plugin-intlayer
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```bash packageManager="pnpm"
|
|
48
|
+
pnpm add --save-dev eslint-plugin-intlayer
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```bash packageManager="yarn"
|
|
52
|
+
yarn add --dev eslint-plugin-intlayer
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Yêu cầu ESLint 9 trở lên (flat config). ESLint 10 được hỗ trợ.
|
|
56
|
+
|
|
57
|
+
## Cách sử dụng
|
|
58
|
+
|
|
59
|
+
Plugin hoạt động trên cả ESLint và [oxlint](https://oxc.rs) — cùng quy tắc, cùng tùy chọn.
|
|
60
|
+
|
|
61
|
+
<Tabs defaultTab="eslint">
|
|
62
|
+
<Tab label="ESLint" value="eslint">
|
|
63
|
+
|
|
64
|
+
```javascript fileName="eslint.config.mjs"
|
|
65
|
+
import intlayer from "eslint-plugin-intlayer";
|
|
66
|
+
|
|
67
|
+
export default [...intlayer.configs.recommended];
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Hoặc trải một cấu hình và tự đặt mức độ nghiêm trọng:
|
|
71
|
+
|
|
72
|
+
```javascript fileName="eslint.config.mjs"
|
|
73
|
+
import intlayer from "eslint-plugin-intlayer";
|
|
74
|
+
|
|
75
|
+
export default [
|
|
76
|
+
...intlayer.configs.recommended,
|
|
77
|
+
{
|
|
78
|
+
rules: {
|
|
79
|
+
"intlayer/no-raw-text": "warn",
|
|
80
|
+
"intlayer/static-dictionary-key": "error",
|
|
81
|
+
"intlayer/no-dynamic-field-access": "error",
|
|
82
|
+
"intlayer/enforce-adapter-import": "warn",
|
|
83
|
+
"intlayer/no-unused-content": "warn",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
</Tab>
|
|
90
|
+
<Tab label="oxlint" value="oxlint">
|
|
91
|
+
|
|
92
|
+
```json fileName=".oxlintrc.json"
|
|
93
|
+
{
|
|
94
|
+
"jsPlugins": ["eslint-plugin-intlayer"],
|
|
95
|
+
"rules": {
|
|
96
|
+
"intlayer/no-raw-text": "warn",
|
|
97
|
+
"intlayer/static-dictionary-key": "error",
|
|
98
|
+
"intlayer/no-dynamic-field-access": "error",
|
|
99
|
+
"intlayer/enforce-adapter-import": "warn"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Hai lưu ý: hỗ trợ plugin JS của oxlint vẫn đang ở giai đoạn alpha và oxlint không hỗ trợ trình phân tích cú pháp tùy chỉnh — vì vậy các tệp `.vue`, `.svelte`, `.astro` và template Angular không được lint tại đó. Hãy chạy oxlint trên các tệp JS/TS/JSX của bạn và giữ lại ESLint cho phần còn lại.
|
|
105
|
+
|
|
106
|
+
`no-unused-content` được cố tình lược bỏ ở trên: nó cần thư mục làm việc và đường dẫn tệp được lint từ ngữ cảnh quy tắc, điều mà cầu nối plugin JS alpha chưa đảm bảo. Hãy chạy quy tắc này dưới ESLint.
|
|
107
|
+
|
|
108
|
+
</Tab>
|
|
109
|
+
</Tabs>
|
|
110
|
+
|
|
111
|
+
### Cấu hình (Configs)
|
|
112
|
+
|
|
113
|
+
| Cấu hình | `no-raw-text` | `static-dictionary-key` | `no-dynamic-field-access` | `enforce-adapter-import` | `no-unused-content` |
|
|
114
|
+
| --------------- | ------------------------- | ----------------------- | ------------------------- | ------------------------ | ------------------- |
|
|
115
|
+
| `recommended` | warn | error | error | off | off |
|
|
116
|
+
| `strict` | error (+ chuỗi ngoài JSX) | error | error | error | off |
|
|
117
|
+
| `contract-only` | off | error | error | off | off |
|
|
118
|
+
|
|
119
|
+
`recommended` cố ý giữ `no-raw-text` ở mức `warn`: việc áp dụng quy tắc này vào một codebase hiện có sẽ hiển thị tất cả các chuỗi chưa được dịch cùng một lúc, điều này không nên làm gián đoạn bản build của bạn ngay từ ngày đầu tiên.
|
|
120
|
+
|
|
121
|
+
`enforce-adapter-import` bị tắt theo mặc định — hãy bật rõ ràng nếu bạn muốn.
|
|
122
|
+
|
|
123
|
+
`no-unused-content` bị tắt trong mọi cấu hình, bao gồm cả `strict`. Đây là quy tắc duy nhất đọc cấu hình Intlayer của bạn và duyệt qua các tệp nguồn từ đĩa, vì vậy việc bật nó nên là một lựa chọn có chủ đích thay vì được thiết lập sẵn tự động.
|
|
124
|
+
|
|
125
|
+
## Các quy tắc
|
|
126
|
+
|
|
127
|
+
### `no-raw-text`
|
|
128
|
+
|
|
129
|
+
Báo cáo văn bản hiển thị cho người dùng không được khai báo trong từ điển. Quy tắc sử dụng cơ chế phát hiện giống như `intlayer extract`, do đó tên thương hiệu, lớp CSS và định danh kỹ thuật sẽ bị bỏ qua.
|
|
130
|
+
|
|
131
|
+
```jsx
|
|
132
|
+
// ✗ Bị báo cáo
|
|
133
|
+
<h1>Welcome to our documentation</h1>
|
|
134
|
+
<input placeholder="Enter your email address" />
|
|
135
|
+
|
|
136
|
+
// ✓ Hợp lệ
|
|
137
|
+
const { title } = useIntlayer("home");
|
|
138
|
+
<h1>{title}</h1>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Các tệp khai báo nội dung (`*.content.ts`, …) được bỏ qua.
|
|
142
|
+
|
|
143
|
+
Để sửa toàn bộ tệp cùng lúc, hãy chạy `npx intlayer extract` và để trình biên dịch chuyển các chuỗi vào từ điển giúp bạn.
|
|
144
|
+
|
|
145
|
+
**Tùy chọn**
|
|
146
|
+
|
|
147
|
+
```javascript fileName="eslint.config.mjs"
|
|
148
|
+
{
|
|
149
|
+
"intlayer/no-raw-text": [
|
|
150
|
+
"warn",
|
|
151
|
+
{
|
|
152
|
+
// Các thuộc tính có giá trị là văn bản hiển thị cho người dùng.
|
|
153
|
+
// Mặc định: title, placeholder, alt, aria-label, label
|
|
154
|
+
attributes: ["title", "placeholder", "alt", "aria-label", "label"],
|
|
155
|
+
|
|
156
|
+
// Các phần tử có nội dung không bao giờ là văn bản hiển thị cho người dùng.
|
|
157
|
+
// Mặc định: code, pre, script, style
|
|
158
|
+
ignoreElements: ["code", "pre", "script", "style"],
|
|
159
|
+
|
|
160
|
+
// Biểu thức chính quy cho văn bản không bao giờ bị báo cáo.
|
|
161
|
+
ignorePatterns: ["^Powered by"],
|
|
162
|
+
|
|
163
|
+
// Báo cáo cả chuỗi ký tự bên ngoài mã markup. Mặc định: false
|
|
164
|
+
includeStringLiterals: false,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `static-dictionary-key`
|
|
171
|
+
|
|
172
|
+
Yêu cầu khóa từ điển phải là một chuỗi ký tự cố định (string literal).
|
|
173
|
+
|
|
174
|
+
Trình biên dịch chỉ có thể tải trước từ điển khi có thể đọc trực tiếp khóa tại vị trí gọi. Với một khóa được tính toán động, nó sẽ âm thầm bỏ qua việc tối ưu hóa và đóng gói tất cả từ điển thay thế.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// ✗ Bị báo cáo
|
|
178
|
+
useIntlayer(dictionaryKey);
|
|
179
|
+
useIntlayer(`home-${suffix}`);
|
|
180
|
+
getTranslations({ namespace: page });
|
|
181
|
+
|
|
182
|
+
// ✗ Biến vẫn không phải là một chuỗi cố định
|
|
183
|
+
const key = "home";
|
|
184
|
+
useIntlayer(key);
|
|
185
|
+
|
|
186
|
+
// ✓ Hợp lệ
|
|
187
|
+
useIntlayer("home");
|
|
188
|
+
getTranslations({ namespace: "home" });
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Điều này áp dụng cho `useIntlayer`, `getIntlayer` và mọi adapter tương thích (`useTranslation`, `useTranslations`, `formatMessage`, `<FormattedMessage id>`, `<Trans i18nKey>`, …).
|
|
192
|
+
|
|
193
|
+
### `no-dynamic-field-access`
|
|
194
|
+
|
|
195
|
+
Yêu cầu trường bạn đọc từ từ điển phải được xác định tĩnh.
|
|
196
|
+
|
|
197
|
+
Trình biên dịch sẽ loại bỏ các trường mà nó không thấy được sử dụng. Một truy cập được tính toán động là vô hình đối với nó, do đó việc đọc có thể trả về `undefined` trong thời gian chạy.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// ✗ Bị báo cáo
|
|
201
|
+
const content = useIntlayer("home");
|
|
202
|
+
content[fieldName];
|
|
203
|
+
|
|
204
|
+
const t = useTranslations("home");
|
|
205
|
+
t(messageKey);
|
|
206
|
+
|
|
207
|
+
// ✓ Hợp lệ
|
|
208
|
+
content.title;
|
|
209
|
+
content["title"];
|
|
210
|
+
content.items[0];
|
|
211
|
+
t("hero.title");
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `enforce-adapter-import`
|
|
215
|
+
|
|
216
|
+
Ưu tiên adapter tương thích `@intlayer/*` hơn gói gốc. Gói gốc chỉ phân giải thành Intlayer khi alias của bundler được cấu hình; adapter luôn luôn thực hiện được. Có thể tự động sửa bằng `--fix`.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// ✗ Bị báo cáo
|
|
220
|
+
import { useTranslation } from "react-i18next";
|
|
221
|
+
import { getTranslations } from "next-intl/server";
|
|
222
|
+
|
|
223
|
+
// ✓ Hợp lệ
|
|
224
|
+
import { useTranslation } from "@intlayer/react-i18next";
|
|
225
|
+
import { getTranslations } from "@intlayer/next-intl/server";
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### `no-unused-content`
|
|
229
|
+
|
|
230
|
+
**Tắt theo mặc định.** Báo cáo nội dung không có bất kỳ phần nào trong dự án đọc, cùng với các khóa từ điển được khai báo ở nhiều nơi.
|
|
231
|
+
|
|
232
|
+
```typescript fileName="src/home.content.ts"
|
|
233
|
+
export default {
|
|
234
|
+
key: "home", // ✗ Bị báo cáo nếu không có nơi nào trong dự án yêu cầu "home"
|
|
235
|
+
content: {
|
|
236
|
+
title: t({ vi: "Tiêu đề", en: "Title" }),
|
|
237
|
+
|
|
238
|
+
// ✗ Bị báo cáo nếu không có nơi nào đọc `hero`
|
|
239
|
+
hero: {
|
|
240
|
+
subtitle: t({ vi: "Phụ đề", en: "Subtitle" }),
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Không giống như các quy tắc khác, quy tắc này không thể quyết định chỉ từ tệp đang kiểm tra — một trường chỉ được xem là không sử dụng khi so với toàn bộ dự án. Khi gặp khai báo nội dung đầu tiên trong một lần lint, nó sẽ tải cấu hình Intlayer, quét các tệp nguồn mà cấu hình đó khai báo (`build.traversePattern`, `compiler.transformPattern`) và chạy cùng bộ phân tích mức độ sử dụng đang vận hành `@intlayer/lsp` và tính năng gạch ngang "không sử dụng" trong tiện ích mở rộng VS Code. Kết quả được lưu vào bộ nhớ cache trong `cacheTtl` mili giây, do đó quá trình quét diễn ra một lần cho mỗi lượt chạy thay vì mỗi tệp.
|
|
247
|
+
|
|
248
|
+
**Tùy chọn**
|
|
249
|
+
|
|
250
|
+
```javascript fileName="eslint.config.mjs"
|
|
251
|
+
{
|
|
252
|
+
"intlayer/no-unused-content": [
|
|
253
|
+
"warn",
|
|
254
|
+
{
|
|
255
|
+
// Báo cáo các khóa từ điển không có nơi nào tham chiếu. Mặc định: true
|
|
256
|
+
reportUnusedDictionaries: true,
|
|
257
|
+
|
|
258
|
+
// Báo cáo các trường nội dung không có nơi nào đọc. Mặc định: true
|
|
259
|
+
reportUnusedFields: true,
|
|
260
|
+
|
|
261
|
+
// Báo cáo các khóa được khai báo ở nhiều nơi. Mặc định: true
|
|
262
|
+
reportDuplicateKeys: true,
|
|
263
|
+
|
|
264
|
+
// Biểu thức chính quy cho đường dẫn trường không bao giờ bị báo cáo.
|
|
265
|
+
ignoreFields: ["^meta"],
|
|
266
|
+
|
|
267
|
+
// Thư mục gốc của dự án bắt đầu quét. Mặc định: thư mục làm việc của ESLint
|
|
268
|
+
baseDir: process.cwd(),
|
|
269
|
+
|
|
270
|
+
// Thời gian một lần quét dự án được tái sử dụng, tính bằng ms. Mặc định: 30000
|
|
271
|
+
cacheTtl: 30000,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Giảm `cacheTtl` khi bạn lint từ một editor server hoạt động lâu dài và muốn các chỉnh sửa hiển thị sớm hơn; thiết lập `baseDir` khi một lần chạy lint trải rộng trên nhiều dự án Intlayer trong monorepo.
|
|
278
|
+
|
|
279
|
+
> **Thiên về sự an toàn (ít báo sai).** Một cảnh báo sai ở đây có thể dẫn đến việc xóa một bản dịch, vì vậy sẽ không có gì được báo cáo khi từ điển được sử dụng theo cách mà bộ phân tích không thể theo dõi: đối tượng nội dung được truyền nguyên vẹn, hàm dịch được liên kết từ đó (`const t = useTranslations("home")`), khai báo được truy cập qua import trực tiếp (`useDictionary(myDictionary)`), lệnh `nest()` từ từ điển khác, hoặc danh sách trường bị làm mờ bởi toán tử spread. Các component đơn tệp (`.vue`, `.svelte`, `.astro`) được tính là sử dụng mọi trường của từ điển mà chúng đề cập, vì các khối script của chúng không được phân tích cú pháp tại đây.
|
|
280
|
+
|
|
281
|
+
`reportDuplicateKeys` đọc các từ điển chưa hợp nhất mà bản build ghi dưới thư mục `.intlayer/`, do đó nó giữ im lặng cho đến khi dự án được build ít nhất một lần. Hai khai báo có chung một khóa sẽ được hợp nhất, đây là một mẫu hợp lệ — báo cáo tồn tại vì một trường được định nghĩa ở cả hai bên sẽ âm thầm chỉ giữ lại một trong hai giá trị.
|
|
282
|
+
|
|
283
|
+
Bộ phân tích được nạp từ `@intlayer/lsp`, phát hành dưới dạng ESM. Do đó quy tắc cần một phiên bản Node có thể `require()` module ES — Node 20.19+ hoặc 22.12+. Trên các phiên bản cũ hơn, nó sẽ không báo cáo gì thay vì làm hỏng lần chạy lint.
|
|
284
|
+
|
|
285
|
+
## Frameworks
|
|
286
|
+
|
|
287
|
+
Mọi quy tắc đều hoạt động trên tất cả các tích hợp của Intlayer, bao gồm bên trong template của Vue, Svelte và Angular. Bạn chỉ cần chỉ định cho ESLint parser nào xử lý từng loại tệp.
|
|
288
|
+
|
|
289
|
+
| Framework | Tệp | Parser |
|
|
290
|
+
| ------------------------- | ----------------- | --------------------------------- |
|
|
291
|
+
| React, Preact, Solid, Lit | `.jsx` `.tsx` | `typescript-eslint` |
|
|
292
|
+
| Next.js | `.jsx` `.tsx` | `typescript-eslint` |
|
|
293
|
+
| Vue, Nuxt | `.vue` | `vue-eslint-parser` |
|
|
294
|
+
| Svelte, SvelteKit | `.svelte` | `svelte-eslint-parser` |
|
|
295
|
+
| Angular | `.ts` | `typescript-eslint` |
|
|
296
|
+
| Template Angular | `.component.html` | `@angular-eslint/template-parser` |
|
|
297
|
+
| Astro | `.astro` | `astro-eslint-parser` |
|
|
298
|
+
|
|
299
|
+
```javascript fileName="eslint.config.mjs"
|
|
300
|
+
import intlayer from "eslint-plugin-intlayer";
|
|
301
|
+
import tseslint from "typescript-eslint";
|
|
302
|
+
import vueParser from "vue-eslint-parser";
|
|
303
|
+
import svelteParser from "svelte-eslint-parser";
|
|
304
|
+
import angularTemplateParser from "@angular-eslint/template-parser";
|
|
305
|
+
|
|
306
|
+
export default [
|
|
307
|
+
...intlayer.configs.recommended,
|
|
308
|
+
|
|
309
|
+
{
|
|
310
|
+
files: ["**/*.{ts,tsx,jsx}"],
|
|
311
|
+
languageOptions: { parser: tseslint.parser },
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
files: ["**/*.vue"],
|
|
315
|
+
languageOptions: {
|
|
316
|
+
parser: vueParser,
|
|
317
|
+
parserOptions: { parser: tseslint.parser },
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
files: ["**/*.svelte"],
|
|
322
|
+
languageOptions: {
|
|
323
|
+
parser: svelteParser,
|
|
324
|
+
parserOptions: { parser: tseslint.parser },
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
files: ["**/*.component.html"],
|
|
329
|
+
languageOptions: { parser: angularTemplateParser },
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Chỉ cài đặt các parser mà dự án của bạn cần.
|
|
335
|
+
|
|
336
|
+
> **Hạn chế đã biết.** Trong template của Vue và Angular, một biểu thức như `{{ content[key] }}` sẽ không được kiểm tra bởi `no-dynamic-field-access`. Các truy cập động được viết trong khối script vẫn được phát hiện bình thường.
|
|
@@ -159,7 +159,7 @@ pnpm add -D webpack-bundle-analyzer
|
|
|
159
159
|
bun add -d webpack-bundle-analyzer
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
-
```typescript fileName="webpack.config.ts
|
|
162
|
+
```typescript fileName="webpack.config.ts"
|
|
163
163
|
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
|
164
164
|
|
|
165
165
|
export default {
|
package/docs/zh/configuration.md
CHANGED
|
@@ -477,6 +477,24 @@ const config: IntlayerConfig = {
|
|
|
477
477
|
*/
|
|
478
478
|
purge: true,
|
|
479
479
|
|
|
480
|
+
/**
|
|
481
|
+
* 按使用它们的代码分割边界对各语言的字典分块进行分组,使懒加载页面只需一次请求即可获取其内容。
|
|
482
|
+
* 默认值:true
|
|
483
|
+
*
|
|
484
|
+
* 注意:
|
|
485
|
+
* - 仅适用于使用 `importMode: 'dynamic'` 的字典。
|
|
486
|
+
*/
|
|
487
|
+
chunkGrouping: true,
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* 让字典与使用它的分块一起加载,而不是等该分块渲染后再获取。读取将同步渲染而不是挂起,因此导航时不再闪烁加载状态。
|
|
491
|
+
* 默认值:true
|
|
492
|
+
*
|
|
493
|
+
* 注意:
|
|
494
|
+
* - 仅等待已解析的语言,因此页面只会下载它所呈现的语言。
|
|
495
|
+
*/
|
|
496
|
+
dictionariesPreload: true,
|
|
497
|
+
|
|
480
498
|
/**
|
|
481
499
|
* 生成的字典文件的输出格式。
|
|
482
500
|
* 默认值:['cjs', 'esm']
|
|
@@ -1056,15 +1074,17 @@ Intlayer 支持多个 AI 提供商,以提供最大的灵活性。当前支持
|
|
|
1056
1074
|
|
|
1057
1075
|
> 在优化期间,Intlayer 将替换字典调用以优化分块 (chunking),使生成的包仅导入实际使用的字典。
|
|
1058
1076
|
|
|
1059
|
-
| 字段
|
|
1060
|
-
|
|
|
1061
|
-
| `mode`
|
|
1062
|
-
| `optimize`
|
|
1063
|
-
| `minify`
|
|
1064
|
-
| `purge`
|
|
1065
|
-
| `checkTypes`
|
|
1066
|
-
| `
|
|
1067
|
-
| `
|
|
1077
|
+
| 字段 | 说明 | 类型 | 默认值 | 示例 | 备注 |
|
|
1078
|
+
| --------------------- | -------------------------------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1079
|
+
| `mode` | 控制构建模式。 | `'auto'` | <br/> `'manual'` | `'auto'` | `'manual'` | • `'auto'`: 构建在应用程序构建期间自动启动。<br/>• `'manual'`: 仅通过显式构建命令触发。<br/>• 可用于防止字典构建(例如避免在 Node.js 环境中运行)。 |
|
|
1080
|
+
| `optimize` | 控制是否应执行构建优化。 | `boolean` | `undefined` | `process.env.NODE_ENV === 'production'` | • 如果未定义,则在框架构建 (Vite/Next.js) 期间启动优化。<br/>• `true` 在开发模式下也会强制执行优化。<br/>• `false` 将其禁用。<br/>• 如果启用,则会替换字典调用以优化分块。<br/>• 需要 `@intlayer/babel` 和 `@intlayer/swc` 插件。 |
|
|
1081
|
+
| `minify` | 压缩字典以减小包体大小。 | `boolean` | `false` | | • 指定是否应最小化包内容。<br/>• 默认值:生产环境中为 `true`。<br/>• 如果禁用了 `optimize`,该选项将被忽略。<br/>• 如果 `editor.enabled` 为 true,该选项将被忽略。 |
|
|
1082
|
+
| `purge` | 清除字典中未使用的键。 | `boolean` | `false` | | • 指定是否应清理包内容。<br/>• 默认值:生产环境中为 `true`。<br/>• 如果禁用了 `optimize`,该选项将被忽略。 |
|
|
1083
|
+
| `checkTypes` | 指示构建是否应检查 TypeScript 类型并记录错误。 | `boolean` | `false` | | 可能会减慢构建过程。 |
|
|
1084
|
+
| `chunkGrouping` | 是否按使用它们的代码分割边界对各语言的字典分块进行分组。 | `boolean` | `true` | | • 不分组时,由众多组件构成的页面会为每个字典发出一次请求。<br/>• 从多个边界访问的字典会移入共享分块,因此任何页面都不会携带其他页面的内容。<br/>• 仅适用于使用 `importMode: 'dynamic'` 的字典。<br/>• 仅适用于客户端构建,且仅在打包时生效(开发模式除外)。 |
|
|
1085
|
+
| `dictionariesPreload` | 字典是否应与使用它的分块一起加载,而不是等该分块渲染后再获取。 | `boolean` | `true` | | • 生成的入口点在顶层等待浏览语言,因此懒加载路由在其内容就绪前不会被视为已加载。<br/>• 读取将同步渲染而不是挂起,因此导航时不再闪烁加载状态。<br/>• 仅等待已解析的语言,因此页面只会下载它所呈现的语言。<br/>• 仅适用于客户端构建中使用 `importMode: 'dynamic'` 的字典。<br/>• 需要支持顶层 await 的打包工具(Vite、esbuild)。 |
|
|
1086
|
+
| `outputFormat` | 控制字典的输出格式。 | `('esm' | 'cjs')[]` | `['esm', 'cjs']` | `['cjs']` | |
|
|
1087
|
+
| `traversePattern` | 优化期间指定要扫描的文件的模式。 | `string[]` | `['**/*.{tsx,ts,js,mjs,cjs,jsx,vue,svelte,svte}', '!**/node_modules/**', '!**/dist/**', '!**/.intlayer/**', '!**/*.config.*', '!**/*.test.*', '!**/*.spec.*', '!**/*.stories.*']` | `['src/**/*.{ts,tsx}', '../ui-library/**/*.{ts,tsx}', '!**/node_modules/**']` | • 通过将优化限制在相关文件来提高构建性能。<br/>• 如果 `optimize` 关闭则忽略。<br/>• 使用 glob 模式。 |
|
|
1068
1088
|
|
|
1069
1089
|
---
|
|
1070
1090
|
|
|
@@ -533,28 +533,6 @@ export default aboutPageMetaContent;
|
|
|
533
533
|
|
|
534
534
|
> 参见 [变体](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dynamic_dictionaries/variants.md) 了解更多信息。
|
|
535
535
|
|
|
536
|
-
#### `meta` (`Record<string, string | number | boolean>`)
|
|
537
|
-
|
|
538
|
-
与动态记录 (Dynamic Records) 配合使用,此字段允许通过不透明的 ID 声明 CMS 管理的的记录或在运行时获取的任意数据。字典身份由该 `meta` 字段中声明的任意键值对集定义。
|
|
539
|
-
|
|
540
|
-
**示例:**
|
|
541
|
-
|
|
542
|
-
```typescript
|
|
543
|
-
{
|
|
544
|
-
key: "product-copy",
|
|
545
|
-
meta: {
|
|
546
|
-
id: "prod_abc",
|
|
547
|
-
userId: "user_123"
|
|
548
|
-
},
|
|
549
|
-
content: {
|
|
550
|
-
name: "Widget Pro",
|
|
551
|
-
description: "The best widget."
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
> 参见 [动态记录](https://github.com/aymericzip/intlayer/blob/main/docs/docs/zh/dynamic_dictionaries/dynamic_content.md) 了解更多信息。
|
|
557
|
-
|
|
558
536
|
### CMS 属性
|
|
559
537
|
|
|
560
538
|
##### `version`(字符串)
|
|
@@ -87,6 +87,29 @@ export default asyncFunctionContent;
|
|
|
87
87
|
|
|
88
88
|
在这种情况下,`fakeFetch` 函数模拟了一个延迟以模拟服务器响应时间。Intlayer 会执行异步函数,并将结果用作 `text` 键的内容。
|
|
89
89
|
|
|
90
|
+
## 获取远程内容
|
|
91
|
+
|
|
92
|
+
你也可以直接将一个 Promise 赋值给内容字段。Intlayer 会在构建字典时等待它,并将解析后的值内联进字典:
|
|
93
|
+
|
|
94
|
+
```typescript fileName="**/*.content.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
|
|
95
|
+
import type { Dictionary } from "intlayer";
|
|
96
|
+
|
|
97
|
+
const remoteContent = {
|
|
98
|
+
key: "remote_content",
|
|
99
|
+
content: {
|
|
100
|
+
externalContent: fetch("https://example.com").then((res) => res.json()),
|
|
101
|
+
},
|
|
102
|
+
} satisfies Dictionary;
|
|
103
|
+
|
|
104
|
+
export default remoteContent;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```plaintext fileName="**/*.content.json" contentDeclarationFormat="json"
|
|
108
|
+
无法从JSON文件获取内容,请改用.ts或.js文件
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
> 该请求在构建时执行,因此获取到的数据是嵌入字典中的快照。重新构建字典即可更新它。
|
|
112
|
+
|
|
90
113
|
## 在 React 组件中使用基于函数的内容
|
|
91
114
|
|
|
92
115
|
要在 React 组件中使用基于函数的内容,您需要从 `react-intlayer` 导入 `useIntlayer` 并使用内容 ID 调用它以获取内容。示例如下:
|