@intlayer/docs 9.3.1 → 9.3.2
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/eslint.md +336 -0
- package/docs/ar/intlayer_with_react_router_v7_fs_routes.md +1 -1
- package/docs/bn/eslint.md +336 -0
- package/docs/cs/eslint.md +336 -0
- package/docs/de/eslint.md +336 -0
- package/docs/en/eslint.md +336 -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/eslint.md +336 -0
- package/docs/fr/eslint.md +336 -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/eslint.md +336 -0
- package/docs/it/eslint.md +336 -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/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/eslint.md +336 -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/eslint.md +336 -0
- package/docs/pt/intlayer_with_astro.md +1 -114
- package/docs/ru/eslint.md +336 -0
- package/docs/tr/eslint.md +336 -0
- package/docs/uk/eslint.md +336 -0
- package/docs/uk/packages/angular-intlayer/exports.md +2 -2
- package/docs/ur/eslint.md +336 -0
- package/docs/vi/eslint.md +336 -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/eslint.md +336 -0
- package/package.json +6 -6
- package/src/generated/docs.entry.ts +20 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-08-12
|
|
3
|
+
updatedAt: 2026-08-12
|
|
4
|
+
title: ESLint Eklentisi | Intlayer için Lint Kuralları
|
|
5
|
+
description: eslint-plugin-intlayer ile sabit kodlanmış metinleri, Intlayer derleyicisinin optimize edemediği dinamik çağrıları ve kullanılmayan sözlük içeriğini yakalayın. React, Vue, Svelte, Angular ve Astro genelinde ESLint ve oxlint ile çalışır.
|
|
6
|
+
keywords:
|
|
7
|
+
- Intlayer
|
|
8
|
+
- ESLint
|
|
9
|
+
- oxlint
|
|
10
|
+
- Linting
|
|
11
|
+
- i18n
|
|
12
|
+
- Uluslararasılaştırma
|
|
13
|
+
- no-raw-text
|
|
14
|
+
- Sabit kodlanmış metinler
|
|
15
|
+
- Kullanılmayan çeviriler
|
|
16
|
+
- Ölü içerik
|
|
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: "Başlangıç geçmişi"
|
|
28
|
+
author: aymericzip
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# ESLint x OXLint Eklentisi
|
|
32
|
+
|
|
33
|
+
`eslint-plugin-intlayer`, TypeScript'in yakalayamadığı i18n hatalarını tespit eder:
|
|
34
|
+
|
|
35
|
+
1. Bir sözlüğe hiç eklenmemiş **sabit kodlanmış metinler (hardcoded text)**.
|
|
36
|
+
2. Tip kontrolünden geçen ve çalışan ancak Intlayer derleyicisinin optimize edemediği **dinamik çağrılar**.
|
|
37
|
+
3. **Ölü içerik (Dead content)** — projedeki hiçbir yerin okumadığı sözlükler ve alanlar (isteğe bağlı).
|
|
38
|
+
|
|
39
|
+
Bilinmeyen sözlük anahtarları, bilinmeyen alan yolları ve eksik yerel ayarlar zaten derleme hataları olduğundan, eklenti bunları tekrar bildirmez.
|
|
40
|
+
|
|
41
|
+
## Kurulum
|
|
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
|
+
ESLint 9 veya üzerini gerektirir (flat config).
|
|
56
|
+
|
|
57
|
+
## Kullanım
|
|
58
|
+
|
|
59
|
+
Eklenti hem ESLint hem de [oxlint](https://oxc.rs) üzerinde aynı kurallar ve aynı seçeneklerle çalışır.
|
|
60
|
+
|
|
61
|
+
<Tabs defaultTab="eslint">
|
|
62
|
+
<Tab label="ESLint" value="eslint">
|
|
63
|
+
|
|
64
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
65
|
+
import intlayer from "eslint-plugin-intlayer";
|
|
66
|
+
|
|
67
|
+
export default [...intlayer.configs.recommended];
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Veya kuralları tek tek etkinleştirin:
|
|
71
|
+
|
|
72
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
73
|
+
import intlayer from "eslint-plugin-intlayer";
|
|
74
|
+
|
|
75
|
+
export default [
|
|
76
|
+
{
|
|
77
|
+
plugins: { intlayer },
|
|
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
|
+
İki uyarı: oxlint'in JS eklenti desteği henüz alfa aşamasındadır ve oxlint özel ayrıştırıcıları (custom parsers) desteklemez — bu nedenle `.vue`, `.svelte`, `.astro` ve Angular şablonları orada denetlenmez. JS/TS/JSX dosyalarınız için oxlint'i çalıştırın ve geri kalanı için ESLint'i kullanın.
|
|
105
|
+
|
|
106
|
+
`no-unused-content` yukarıda kasıtlı olarak hariç tutulmuştur: kural bağlamından çalışma dizinine ve denetlenen dosya yoluna ihtiyaç duyar; alfa JS eklenti köprüsü bunu garanti etmez. ESLint altında çalıştırın.
|
|
107
|
+
|
|
108
|
+
</Tab>
|
|
109
|
+
</Tabs>
|
|
110
|
+
|
|
111
|
+
### Yapılandırmalar (Configs)
|
|
112
|
+
|
|
113
|
+
| Yapılandırma | `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 (+ JSX dışı dizeler) | error | error | error | off |
|
|
117
|
+
| `contract-only` | off | error | error | off | off |
|
|
118
|
+
|
|
119
|
+
`recommended`, `no-raw-text` kuralını kasıtlı olarak `warn` seviyesinde tutar: bunu mevcut bir kod tabanına yöneltmek tüm çevrilmemiş dizeleri aynı anda ortaya çıkarır ve bu durum derlemenizi ilk günden bozmamalıdır.
|
|
120
|
+
|
|
121
|
+
`enforce-adapter-import` varsayılan olarak kapalıdır — istiyorsanız açıkça etkinleştirin.
|
|
122
|
+
|
|
123
|
+
`no-unused-content`, `strict` dahil tüm yapılandırmalarda kapalıdır. Intlayer yapılandırmanızı okuyan ve kaynak dosyalarınızı diskten tarayan tek kuraldır; bu nedenle açılması, bir ön ayarın sizin yerinize yapmasından ziyade bilinçli bir seçim olmalıdır.
|
|
124
|
+
|
|
125
|
+
## Kurallar
|
|
126
|
+
|
|
127
|
+
### `no-raw-text`
|
|
128
|
+
|
|
129
|
+
Bir sözlükte bildirilmemiş kullanıcıya yönelik metinleri bildirir. `intlayer extract` ile aynı algılamayı kullanır; bu nedenle marka adları, CSS sınıfları ve teknik tanımlayıcılar yoksayılır.
|
|
130
|
+
|
|
131
|
+
```jsx
|
|
132
|
+
// ✗ Bildirildi
|
|
133
|
+
<h1>Welcome to our documentation</h1>
|
|
134
|
+
<input placeholder="Enter your email address" />
|
|
135
|
+
|
|
136
|
+
// ✓ Sorunsuz
|
|
137
|
+
const { title } = useIntlayer("home");
|
|
138
|
+
<h1>{title}</h1>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
İçerik bildirim dosyaları (`*.content.ts`, …) atlanır.
|
|
142
|
+
|
|
143
|
+
Tüm bir dosyayı tek seferde düzeltmek için `npx intlayer extract` komutunu çalıştırın ve derleyicinin dizeleri sizin için bir sözlüğe taşımasına izin verin.
|
|
144
|
+
|
|
145
|
+
**Seçenekler**
|
|
146
|
+
|
|
147
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
148
|
+
{
|
|
149
|
+
"intlayer/no-raw-text": [
|
|
150
|
+
"warn",
|
|
151
|
+
{
|
|
152
|
+
// Değeri kullanıcıya yönelik metin olan öznitelikler.
|
|
153
|
+
// Varsayılan: title, placeholder, alt, aria-label, label
|
|
154
|
+
attributes: ["title", "placeholder", "alt", "aria-label", "label"],
|
|
155
|
+
|
|
156
|
+
// İçeriği asla kullanıcıya yönelik metin olmayan öğeler.
|
|
157
|
+
// Varsayılan: code, pre, script, style
|
|
158
|
+
ignoreElements: ["code", "pre", "script", "style"],
|
|
159
|
+
|
|
160
|
+
// Asla bildirilmeyecek metinler için düzenli ifadeler.
|
|
161
|
+
ignorePatterns: ["^Powered by"],
|
|
162
|
+
|
|
163
|
+
// Biçimlendirme dışındaki dize sabitlerini de bildirin. Varsayılan: false
|
|
164
|
+
includeStringLiterals: false,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `static-dictionary-key`
|
|
171
|
+
|
|
172
|
+
Sözlük anahtarının bir dize sabiti olmasını gerektirir.
|
|
173
|
+
|
|
174
|
+
Derleyici, bir sözlüğü yalnızca çağrı noktasında anahtarı doğrudan okuyabildiğinde önceden yükleyebilir. Hesaplanmış bir anahtarla optimizasyonu sessizce atlar ve bunun yerine her sözlüğü paketler.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// ✗ Bildirildi
|
|
178
|
+
useIntlayer(dictionaryKey);
|
|
179
|
+
useIntlayer(`home-${suffix}`);
|
|
180
|
+
getTranslations({ namespace: page });
|
|
181
|
+
|
|
182
|
+
// ✗ Değişken hala bir dize sabiti değildir
|
|
183
|
+
const key = "home";
|
|
184
|
+
useIntlayer(key);
|
|
185
|
+
|
|
186
|
+
// ✓ Sorunsuz
|
|
187
|
+
useIntlayer("home");
|
|
188
|
+
getTranslations({ namespace: "home" });
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Bu durum `useIntlayer`, `getIntlayer` ve tüm uyumluluk bağdaştırıcıları (`useTranslation`, `useTranslations`, `formatMessage`, `<FormattedMessage id>`, `<Trans i18nKey>`, …) için geçerlidir.
|
|
192
|
+
|
|
193
|
+
### `no-dynamic-field-access`
|
|
194
|
+
|
|
195
|
+
Bir sözlükten okuduğunuz alanın statik olarak bilinmesini gerektirir.
|
|
196
|
+
|
|
197
|
+
Derleyici, kullanıldığını görmediği alanları kaldırır. Hesaplanmış bir erişim onun için görünmezdir, bu nedenle okuma işlemi çalışma zamanında `undefined` döndürebilir.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// ✗ Bildirildi
|
|
201
|
+
const content = useIntlayer("home");
|
|
202
|
+
content[fieldName];
|
|
203
|
+
|
|
204
|
+
const t = useTranslations("home");
|
|
205
|
+
t(messageKey);
|
|
206
|
+
|
|
207
|
+
// ✓ Sorunsuz
|
|
208
|
+
content.title;
|
|
209
|
+
content["title"];
|
|
210
|
+
content.items[0];
|
|
211
|
+
t("hero.title");
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `enforce-adapter-import`
|
|
215
|
+
|
|
216
|
+
Orijinal paket yerine `@intlayer/*` uyumluluk bağdaştırıcısını tercih eder. Orijinal paket yalnızca paketleyici takma adı yapılandırıldığında Intlayer'a çözümlenir; bağdaştırıcı her zaman çözümlenir. `--fix` ile otomatik düzeltilebilir.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// ✗ Bildirildi
|
|
220
|
+
import { useTranslation } from "react-i18next";
|
|
221
|
+
import { getTranslations } from "next-intl/server";
|
|
222
|
+
|
|
223
|
+
// ✓ Sorunsuz
|
|
224
|
+
import { useTranslation } from "@intlayer/react-i18next";
|
|
225
|
+
import { getTranslations } from "@intlayer/next-intl/server";
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### `no-unused-content`
|
|
229
|
+
|
|
230
|
+
**Varsayılan olarak kapalıdır.** Projenizdeki hiçbir yerin okumadığı içeriği ve birden fazla yerde bildirilen sözlük anahtarlarını bildirir.
|
|
231
|
+
|
|
232
|
+
```typescript fileName="src/home.content.ts"
|
|
233
|
+
export default {
|
|
234
|
+
key: "home", // ✗ Projede hiçbir çağıran "home" istemediğinde bildirilir
|
|
235
|
+
content: {
|
|
236
|
+
title: t({ tr: "Başlık", en: "Title" }),
|
|
237
|
+
|
|
238
|
+
// ✗ `hero` alanını hiçbir şey okumadığında bildirilir
|
|
239
|
+
hero: {
|
|
240
|
+
subtitle: t({ tr: "Alt Başlık", en: "Subtitle" }),
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Diğer kuralların aksine, bu kural yalnızca önündeki dosyadan karar veremez — bir alan yalnızca tüm projeye göre kullanılmamış sayılır. Bir lint çalıştırmasının ilk içerik bildiriminde Intlayer yapılandırmanızı yükler, bu yapılandırmanın bildirdiği kaynak dosyaları (`build.traversePattern`, `compiler.transformPattern`) tarar ve `@intlayer/lsp` ile VS Code uzantısındaki "kullanılmayan" üstü çizili metni destekleyen aynı kullanım çözümleyicisini çalıştırır. Sonuç `cacheTtl` milisaniye boyunca önbelleğe alınır, böylece tarama dosya başına değil çalıştırma başına bir kez gerçekleşir.
|
|
247
|
+
|
|
248
|
+
**Seçenekler**
|
|
249
|
+
|
|
250
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
251
|
+
{
|
|
252
|
+
"intlayer/no-unused-content": [
|
|
253
|
+
"warn",
|
|
254
|
+
{
|
|
255
|
+
// Hiçbir şeyin başvurmadığı sözlük anahtarlarını bildirin. Varsayılan: true
|
|
256
|
+
reportUnusedDictionaries: true,
|
|
257
|
+
|
|
258
|
+
// Hiçbir şeyin okumadığı içerik alanlarını bildirin. Varsayılan: true
|
|
259
|
+
reportUnusedFields: true,
|
|
260
|
+
|
|
261
|
+
// Birden fazla yerde bildirilen anahtarları bildirin. Varsayılan: true
|
|
262
|
+
reportDuplicateKeys: true,
|
|
263
|
+
|
|
264
|
+
// Asla bildirilmeyecek alan yolları için düzenli ifadeler.
|
|
265
|
+
ignoreFields: ["^meta"],
|
|
266
|
+
|
|
267
|
+
// Taramanın başlayacağı proje kökü. Varsayılan: ESLint çalışma dizini
|
|
268
|
+
baseDir: process.cwd(),
|
|
269
|
+
|
|
270
|
+
// Bir proje taramasının yeniden kullanılma süresi (ms). Varsayılan: 30000
|
|
271
|
+
cacheTtl: 30000,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Uzun süre çalışan bir düzenleyici sunucusundan lint işlemi yaparken ve düzenlemelerinizin daha erken yansımasını istediğinizde `cacheTtl` değerini düşürün; tek bir lint çalıştırması bir monorepodaki birkaç Intlayer projesini kapsadığında `baseDir` değerini ayarlayın.
|
|
278
|
+
|
|
279
|
+
> **Sessiz kalmaya meyillidir.** Buradaki yanlış bir pozitif sonuç bir çeviriyi silebilir; bu nedenle sözlük analizin izleyemeyeceği bir şekilde kullanıldığında hiçbir şey bildirilmez: içerik nesnesinin bir bütün olarak aktarılması, ondan bağlanan bir çevirici işlevi (`const t = useTranslations("home")`), doğrudan içe aktarma yoluyla ulaşılan bir bildirim (`useDictionary(myDictionary)`), başka bir sözlükten bir `nest()` veya bir yayma (spread) operatörü ile kapsamlı olmaktan çıkarılan bir alan listesi. Tek dosyalı bileşenler (`.vue`, `.svelte`, `.astro`), komut dosyası blokları burada ayrıştırılmadığı için bahsettikleri sözlüklerin her alanını kullanıyor sayılır.
|
|
280
|
+
|
|
281
|
+
`reportDuplicateKeys`, derlemenin `.intlayer/` altına yazdığı birleştirilmemiş sözlükleri okur, bu nedenle proje en az bir kez derlenene kadar sessiz kalır. Bir anahtarı paylaşan iki bildirim birleştirilir ve bu meşru bir kalıptır — bu raporlama mekanizması, her iki tarafta tanımlanan bir alanın sessizce iki değerden yalnızca birini koruması nedeniyle mevcuttur.
|
|
282
|
+
|
|
283
|
+
Çözümleyici, ESM olarak dağıtılan `@intlayer/lsp` paketinden yüklenir. Bu nedenle kural, bir ES modülünü `require()` edebilen bir Node sürümüne ihtiyaç duyar — Node 20.19+ veya 22.12+. Daha eski sürümlerde lint çalıştırmasını başarısız kılmak yerine hiçbir şey bildirmez.
|
|
284
|
+
|
|
285
|
+
## Çerçeveler (Frameworks)
|
|
286
|
+
|
|
287
|
+
Her kural, Vue, Svelte ve Angular şablonları dahil olmak üzere tüm Intlayer entegrasyonlarında çalışır. ESLint'e yalnızca her dosya türünü hangi ayrıştırıcının okuyacağını belirtmeniz gerekir.
|
|
288
|
+
|
|
289
|
+
| Çerçeve | Dosyalar | Ayrıştırıcı (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
|
+
| Angular Şablonları | `.component.html` | `@angular-eslint/template-parser` |
|
|
297
|
+
| Astro | `.astro` | `astro-eslint-parser` |
|
|
298
|
+
|
|
299
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
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
|
+
Yalnızca projenizin ihtiyaç duyduğu ayrıştırıcıları yükleyin.
|
|
335
|
+
|
|
336
|
+
> **Bilinen sınırlama.** Vue ve Angular şablonlarında `{{ content[key] }}` gibi bir ifade `no-dynamic-field-access` tarafından kontrol edilmez. Script bloğunda yazılan dinamik okumalar normal şekilde yakalanır.
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-08-12
|
|
3
|
+
updatedAt: 2026-08-12
|
|
4
|
+
title: Плагін ESLint | Правила лінтингу для Intlayer
|
|
5
|
+
description: Знаходьте жорстко закодовані рядки, динамічні виклики, які компілятор Intlayer не може оптимізувати, та невикористаний вміст словників за допомогою eslint-plugin-intlayer. Працює з ESLint та oxlint для React, Vue, Svelte, Angular та Astro.
|
|
6
|
+
keywords:
|
|
7
|
+
- Intlayer
|
|
8
|
+
- ESLint
|
|
9
|
+
- oxlint
|
|
10
|
+
- Лінтинг
|
|
11
|
+
- i18n
|
|
12
|
+
- Інтернаціоналізація
|
|
13
|
+
- no-raw-text
|
|
14
|
+
- Жорстко закодовані рядки
|
|
15
|
+
- Невикористані переклади
|
|
16
|
+
- Мертвий вміст
|
|
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: "Початкова історія"
|
|
28
|
+
author: aymericzip
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Плагін ESLint x OXLint
|
|
32
|
+
|
|
33
|
+
`eslint-plugin-intlayer` виявляє ті типи помилок i18n, які TypeScript не здатний помітити:
|
|
34
|
+
|
|
35
|
+
1. **Жорстко закодований текст**, який так і не потрапив до словника.
|
|
36
|
+
2. **Динамічні виклики**, які проходять перевірку типів і виконуються, але які компілятор Intlayer не може оптимізувати.
|
|
37
|
+
3. **Мертвий вміст (Dead content)** — словники та поля, які ніде в проєкті не зчитуються (за бажанням/opt-in).
|
|
38
|
+
|
|
39
|
+
Невідомі ключі словників, невідомі шляхи до полів та відсутні локалі вже є помилками компіляції, тому плагін не дублює їх повідомлення.
|
|
40
|
+
|
|
41
|
+
## Встановлення
|
|
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
|
+
Потрібен ESLint 9 або новішої версії (flat config).
|
|
56
|
+
|
|
57
|
+
## Використання
|
|
58
|
+
|
|
59
|
+
Плагін працює як в ESLint, так і в [oxlint](https://oxc.rs) — однакові правила, однакові параметри.
|
|
60
|
+
|
|
61
|
+
<Tabs defaultTab="eslint">
|
|
62
|
+
<Tab label="ESLint" value="eslint">
|
|
63
|
+
|
|
64
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
65
|
+
import intlayer from "eslint-plugin-intlayer";
|
|
66
|
+
|
|
67
|
+
export default [...intlayer.configs.recommended];
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Або вмикайте правила окремо:
|
|
71
|
+
|
|
72
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
73
|
+
import intlayer from "eslint-plugin-intlayer";
|
|
74
|
+
|
|
75
|
+
export default [
|
|
76
|
+
{
|
|
77
|
+
plugins: { intlayer },
|
|
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
|
+
Два застереження: підтримка JS-плагінів в oxlint все ще на стадії альфа, і oxlint не підтримує кастомні парсери — тому файли `.vue`, `.svelte`, `.astro` та шаблони Angular там не лінтяться. Запускайте oxlint для ваших файлів JS/TS/JSX, а для решти використовуйте ESLint.
|
|
105
|
+
|
|
106
|
+
Правило `no-unused-content` навмисно виключено вище: йому потрібні робоча директорія та шлях до перевіреного файлу з контексту правила, чого альфа-міст для JS-плагінів не гарантує. Запускайте його під ESLint.
|
|
107
|
+
|
|
108
|
+
</Tab>
|
|
109
|
+
</Tabs>
|
|
110
|
+
|
|
111
|
+
### Конфігурації (Configs)
|
|
112
|
+
|
|
113
|
+
| Конфігурація | `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 (+ рядкові літерали поза JSX) | error | error | error | off |
|
|
117
|
+
| `contract-only` | off | error | error | off | off |
|
|
118
|
+
|
|
119
|
+
`recommended` навмисно залишає `no-raw-text` зі статусом `warn`: застосування правила до наявної кодової бази виявить усі неперекладені рядки одночасно, що не повинно ламати збірку з першого ж дня.
|
|
120
|
+
|
|
121
|
+
`enforce-adapter-import` типово вимкнено — увімкніть його явно, якщо це необхідно.
|
|
122
|
+
|
|
123
|
+
`no-unused-content` вимкнено в усіх пресетах, включно зі `strict`. Це єдине правило, яке зчитує конфігурацію Intlayer і сканує вихідні файли з диска, тому його ввімкнення має бути свідомим вибором.
|
|
124
|
+
|
|
125
|
+
## Правила
|
|
126
|
+
|
|
127
|
+
### `no-raw-text`
|
|
128
|
+
|
|
129
|
+
Повідомляє про текст для користувача, який не оголошено у словнику. Використовує ту саму логіку виявлення, що й `intlayer extract`, тому назви брендів, класи CSS та технічні ідентифікатори ігноруються.
|
|
130
|
+
|
|
131
|
+
```jsx
|
|
132
|
+
// ✗ Повідомлено
|
|
133
|
+
<h1>Welcome to our documentation</h1>
|
|
134
|
+
<input placeholder="Enter your email address" />
|
|
135
|
+
|
|
136
|
+
// ✓ Усе добре
|
|
137
|
+
const { title } = useIntlayer("home");
|
|
138
|
+
<h1>{title}</h1>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Файли оголошення вмісту (`*.content.ts`, …) пропускаються.
|
|
142
|
+
|
|
143
|
+
Щоб виправити весь файл одночасно, виконайте `npx intlayer extract`, і компілятор автоматично перенесе рядки до словника.
|
|
144
|
+
|
|
145
|
+
**Параметри**
|
|
146
|
+
|
|
147
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
148
|
+
{
|
|
149
|
+
"intlayer/no-raw-text": [
|
|
150
|
+
"warn",
|
|
151
|
+
{
|
|
152
|
+
// Атрибути, значенням яких є текст для користувача.
|
|
153
|
+
// Типово: title, placeholder, alt, aria-label, label
|
|
154
|
+
attributes: ["title", "placeholder", "alt", "aria-label", "label"],
|
|
155
|
+
|
|
156
|
+
// Елементи, вміст яких ніколи не є текстом для користувача.
|
|
157
|
+
// Типово: code, pre, script, style
|
|
158
|
+
ignoreElements: ["code", "pre", "script", "style"],
|
|
159
|
+
|
|
160
|
+
// Регулярні вирази для тексту, про який ніколи не слід повідомляти.
|
|
161
|
+
ignorePatterns: ["^Powered by"],
|
|
162
|
+
|
|
163
|
+
// Повідомляти також про рядкові літерали поза розміткою. Типово: false
|
|
164
|
+
includeStringLiterals: false,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `static-dictionary-key`
|
|
171
|
+
|
|
172
|
+
Вимагає, щоб ключ словника був рядковим літералом.
|
|
173
|
+
|
|
174
|
+
Компілятор може попередньо завантажити словник лише тоді, коли може прочитати ключ безпосередньо в місці виклику. У разі використання обчислюваного ключа оптимізація мовчки пропускається, і замість цього в бандл включаються всі словники.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// ✗ Повідомлено
|
|
178
|
+
useIntlayer(dictionaryKey);
|
|
179
|
+
useIntlayer(`home-${suffix}`);
|
|
180
|
+
getTranslations({ namespace: page });
|
|
181
|
+
|
|
182
|
+
// ✗ Змінна все одно не є літералом
|
|
183
|
+
const key = "home";
|
|
184
|
+
useIntlayer(key);
|
|
185
|
+
|
|
186
|
+
// ✓ Усе добре
|
|
187
|
+
useIntlayer("home");
|
|
188
|
+
getTranslations({ namespace: "home" });
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Це стосується `useIntlayer`, `getIntlayer` та всіх адаптерів сумісності (`useTranslation`, `useTranslations`, `formatMessage`, `<FormattedMessage id>`, `<Trans i18nKey>`, …).
|
|
192
|
+
|
|
193
|
+
### `no-dynamic-field-access`
|
|
194
|
+
|
|
195
|
+
Вимагає, щоб поле, яке зчитується зі словника, було статично відомим.
|
|
196
|
+
|
|
197
|
+
Компілятор видаляє поля, використання яких він не виявив. Динамічний доступ для нього невидимий, тому читання може повернути `undefined` під час виконання.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// ✗ Повідомлено
|
|
201
|
+
const content = useIntlayer("home");
|
|
202
|
+
content[fieldName];
|
|
203
|
+
|
|
204
|
+
const t = useTranslations("home");
|
|
205
|
+
t(messageKey);
|
|
206
|
+
|
|
207
|
+
// ✓ Усе добре
|
|
208
|
+
content.title;
|
|
209
|
+
content["title"];
|
|
210
|
+
content.items[0];
|
|
211
|
+
t("hero.title");
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `enforce-adapter-import`
|
|
215
|
+
|
|
216
|
+
Віддає перевагу адаптеру сумісності `@intlayer/*` перед оригінальним пакетом. Оригінальний пакет переходить в Intlayer лише за наявності налаштованого псевдоніма бандлера; адаптер працює завжди. Підтримує автовиправлення через `--fix`.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// ✗ Повідомлено
|
|
220
|
+
import { useTranslation } from "react-i18next";
|
|
221
|
+
import { getTranslations } from "next-intl/server";
|
|
222
|
+
|
|
223
|
+
// ✓ Усе добре
|
|
224
|
+
import { useTranslation } from "@intlayer/react-i18next";
|
|
225
|
+
import { getTranslations } from "@intlayer/next-intl/server";
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### `no-unused-content`
|
|
229
|
+
|
|
230
|
+
**Типово вимкнено.** Повідомляє про вміст, який ніде в проєкті не зчитується, а також про ключі словників, оголошені в кількох місцях.
|
|
231
|
+
|
|
232
|
+
```typescript fileName="src/home.content.ts"
|
|
233
|
+
export default {
|
|
234
|
+
key: "home", // ✗ Повідомляється, якщо жоден виклик у проєкті не запитує "home"
|
|
235
|
+
content: {
|
|
236
|
+
title: t({ uk: "Заголовок", en: "Title" }),
|
|
237
|
+
|
|
238
|
+
// ✗ Повідомляється, якщо ніщо не зчитує `hero`
|
|
239
|
+
hero: {
|
|
240
|
+
subtitle: t({ uk: "Підзаголовок", en: "Subtitle" }),
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
На відміну від інших правил, це правило не може вирішити лише за поточним файлом — поле є невикористаним лише відносно всього проєкту. Під час першого оголошення вмісту під час лінтингу воно завантажує конфігурацію Intlayer, сканує вихідні файли за шляхами з конфігурації (`build.traversePattern`, `compiler.transformPattern`) і запускає той самий аналізатор використання, який живить `@intlayer/lsp` та закреслення «невикористаного» в розширенні VS Code. Результат кешується на `cacheTtl` мілісекунд, тому сканування відбувається один раз за запуск, а не для кожного файлу.
|
|
247
|
+
|
|
248
|
+
**Параметри**
|
|
249
|
+
|
|
250
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
251
|
+
{
|
|
252
|
+
"intlayer/no-unused-content": [
|
|
253
|
+
"warn",
|
|
254
|
+
{
|
|
255
|
+
// Повідомляти про ключі словників, на які ніщо не посилається. Типово: true
|
|
256
|
+
reportUnusedDictionaries: true,
|
|
257
|
+
|
|
258
|
+
// Повідомляти про поля вмісту, які ніщо не зчитує. Типово: true
|
|
259
|
+
reportUnusedFields: true,
|
|
260
|
+
|
|
261
|
+
// Повідомляти про продубльовані ключі, оголошені в кількох місцях. Типово: true
|
|
262
|
+
reportDuplicateKeys: true,
|
|
263
|
+
|
|
264
|
+
// Регулярні вирази для шляхів полів, про які ніколи не слід повідомляти.
|
|
265
|
+
ignoreFields: ["^meta"],
|
|
266
|
+
|
|
267
|
+
// Корінь проєкту, з якого починається сканування. Типово: робоча директорія ESLint
|
|
268
|
+
baseDir: process.cwd(),
|
|
269
|
+
|
|
270
|
+
// Час повторного використання результату сканування проєкту (у мс). Типово: 30000
|
|
271
|
+
cacheTtl: 30000,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Зменште `cacheTtl`, якщо ви запускаєте лінтинг із довгоживучого сервера редактора і хочете швидше бачити зміни; встановіть `baseDir`, коли один запуск лінтингу охоплює кілька проєктів Intlayer у монорепозиторії.
|
|
278
|
+
|
|
279
|
+
> **Схильне до мінімізації помилкових спрацьовувань.** Хибне спрацьовування тут призведе до видалення потрібного перекладу, тому нічого не повідомляється, якщо словник використовується способом, який аналіз не може відстежити: об'єкт вмісту передано повністю, прив'язана функція перекладача (`const t = useTranslations("home")`), оголошення отримано через прямий імпорт (`useDictionary(myDictionary)`), виклик `nest()` з іншого словника або список полів, який став невичерпним через оператор spread. Однофайлові компоненти (`.vue`, `.svelte`, `.astro`) вважаються такими, що використовують кожне поле згаданих словників, оскільки їхні блоки скриптів тут не парсяться.
|
|
280
|
+
|
|
281
|
+
`reportDuplicateKeys` зчитує необ'єднані словники, які збірка записує у `.intlayer/`, тому воно залишається неактивним, доки проєкт не буде зібрано принаймні один раз. Два оголошення з однаковим ключем об'єднуються, що є коректним шаблоном — звіт формується тому, що поле, визначене з обох боків, непомітно зберігає лише одне з двох значень.
|
|
282
|
+
|
|
283
|
+
Аналізатор завантажується з `@intlayer/lsp`, який постачається як ESM. Тому правилу потрібна версія Node, здатна виконувати `require()` для ES-модулів — Node 20.19+ або 22.12+. На старіших версіях воно нічого не повідомляє, щоб не зупиняти процес лінтингу.
|
|
284
|
+
|
|
285
|
+
## Фреймворки
|
|
286
|
+
|
|
287
|
+
Кожне правило працює в усіх інтеграціях Intlayer, включно з шаблонами Vue, Svelte та Angular. Потрібно лише вказати ESLint, який парсер зчитує кожен тип файлів.
|
|
288
|
+
|
|
289
|
+
| Фреймворк | Файли | Парсер |
|
|
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
|
+
| Шаблони Angular | `.component.html` | `@angular-eslint/template-parser` |
|
|
297
|
+
| Astro | `.astro` | `astro-eslint-parser` |
|
|
298
|
+
|
|
299
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
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
|
+
Встановлюйте лише ті парсери, які потрібні вашому проєкту.
|
|
335
|
+
|
|
336
|
+
> **Відоме обмеження.** У шаблонах Vue та Angular вираз на кшталт `{{ content[key] }}` не перевіряється правилом `no-dynamic-field-access`. Динамічні звернення всередині блоку script виявляються у звичайному режимі.
|