@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 Plugin | Lint-regels voor Intlayer
|
|
5
|
+
description: Detecteer hardgecodeerde strings, dynamische aanroepen die de Intlayer-compiler niet kan optimaliseren en ongebruikte woordenboekinhoud met eslint-plugin-intlayer. Werkt met ESLint en oxlint op React, Vue, Svelte, Angular en Astro.
|
|
6
|
+
keywords:
|
|
7
|
+
- Intlayer
|
|
8
|
+
- ESLint
|
|
9
|
+
- oxlint
|
|
10
|
+
- Linting
|
|
11
|
+
- i18n
|
|
12
|
+
- Internationalisering
|
|
13
|
+
- no-raw-text
|
|
14
|
+
- Hardgecodeerde strings
|
|
15
|
+
- Ongebruikte vertalingen
|
|
16
|
+
- Dode inhoud
|
|
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: "Initiële geschiedenis"
|
|
28
|
+
author: aymericzip
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# ESLint x OXLint Plugin
|
|
32
|
+
|
|
33
|
+
`eslint-plugin-intlayer` detecteert het soort i18n-fouten dat TypeScript niet kan opmerken:
|
|
34
|
+
|
|
35
|
+
1. **Hardgecodeerde tekst** die nooit in een woordenboek is opgenomen.
|
|
36
|
+
2. **Dynamische aanroepen** die type-checks doorstaan en functioneren, maar die de Intlayer-compiler niet kan optimaliseren.
|
|
37
|
+
3. **Dode inhoud (Dead content)** — woordenboeken en velden die nergens in het project worden gelezen (opt-in).
|
|
38
|
+
|
|
39
|
+
Onbekende woordenboeksleutels, onbekende veldpaden en ontbrekende locales zijn al compilatiefouten, dus de plugin herhaalt deze niet.
|
|
40
|
+
|
|
41
|
+
## Installatie
|
|
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
|
+
Vereist ESLint 9 of nieuwer (flat config).
|
|
56
|
+
|
|
57
|
+
## Gebruik
|
|
58
|
+
|
|
59
|
+
De plugin werkt in zowel ESLint als [oxlint](https://oxc.rs) — dezelfde regels, dezelfde opties.
|
|
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
|
+
Of schakel regels één voor één in:
|
|
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
|
+
Twee kanttekeningen: de JS-pluginondersteuning in oxlint is nog in alfa en oxlint ondersteunt geen aangepaste parsers — dus `.vue`-, `.svelte`-, `.astro`-bestanden en Angular-templates worden daar niet gecontroleerd. Voer oxlint uit op uw JS/TS/JSX-bestanden en behoud ESLint voor de rest.
|
|
105
|
+
|
|
106
|
+
`no-unused-content` is hierboven opzettelijk weggelaten: het vereist de werkmap en het gecontroleerde bestandspad uit de regelcontext, wat de alfa JS-plugin-bridge niet garandeert. Voer dit uit onder ESLint.
|
|
107
|
+
|
|
108
|
+
</Tab>
|
|
109
|
+
</Tabs>
|
|
110
|
+
|
|
111
|
+
### Configuraties
|
|
112
|
+
|
|
113
|
+
| Configuratie | `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 (+ niet-JSX literals) | error | error | error | off |
|
|
117
|
+
| `contract-only` | off | error | error | off | off |
|
|
118
|
+
|
|
119
|
+
`recommended` houdt `no-raw-text` opzettelijk op `warn`: als u dit toepast op een bestaande codebase, worden alle onvertaalde strings in één keer zichtbaar, wat uw build niet vanaf dag één zou moeten laten mislukken.
|
|
120
|
+
|
|
121
|
+
`enforce-adapter-import` staat standaard uit — schakel dit expliciet in als u het wilt gebruiken.
|
|
122
|
+
|
|
123
|
+
`no-unused-content` staat uit in elke configuratie, inclusief `strict`. Het is de enige regel die uw Intlayer-configuratie leest en bronbestanden van schijf doorzoekt, dus het inschakelen ervan moet een bewuste keuze zijn in plaats van iets wat een preset automatisch doet.
|
|
124
|
+
|
|
125
|
+
## Regels
|
|
126
|
+
|
|
127
|
+
### `no-raw-text`
|
|
128
|
+
|
|
129
|
+
Meldt gebruikersgerichte tekst die niet in een woordenboek is gedeclareerd. Het gebruikt dezelfde detectie als `intlayer extract`, waardoor merknamen, CSS-klassen en technische identifiers worden genegeerd.
|
|
130
|
+
|
|
131
|
+
```jsx
|
|
132
|
+
// ✗ Gemeld
|
|
133
|
+
<h1>Welcome to our documentation</h1>
|
|
134
|
+
<input placeholder="Enter your email address" />
|
|
135
|
+
|
|
136
|
+
// ✓ Goed
|
|
137
|
+
const { title } = useIntlayer("home");
|
|
138
|
+
<h1>{title}</h1>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Bestanden voor inhoudsdeclaratie (`*.content.ts`, …) worden overgeslagen.
|
|
142
|
+
|
|
143
|
+
Om een heel bestand in één keer te herstellen, voert u `npx intlayer extract` uit en laat u de compiler de strings voor u naar een woordenboek verplaatsen.
|
|
144
|
+
|
|
145
|
+
**Opties**
|
|
146
|
+
|
|
147
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
148
|
+
{
|
|
149
|
+
"intlayer/no-raw-text": [
|
|
150
|
+
"warn",
|
|
151
|
+
{
|
|
152
|
+
// Attributen waarvan de waarde gebruikersgerichte tekst is.
|
|
153
|
+
// Standaard: title, placeholder, alt, aria-label, label
|
|
154
|
+
attributes: ["title", "placeholder", "alt", "aria-label", "label"],
|
|
155
|
+
|
|
156
|
+
// Elementen waarvan de inhoud nooit gebruikersgerichte tekst is.
|
|
157
|
+
// Standaard: code, pre, script, style
|
|
158
|
+
ignoreElements: ["code", "pre", "script", "style"],
|
|
159
|
+
|
|
160
|
+
// Reguliere expressies voor tekst die nooit gemeld mag worden.
|
|
161
|
+
ignorePatterns: ["^Powered by"],
|
|
162
|
+
|
|
163
|
+
// Meld ook string-literals buiten markup. Standaard: false
|
|
164
|
+
includeStringLiterals: false,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `static-dictionary-key`
|
|
171
|
+
|
|
172
|
+
Vereist dat de woordenboeksleutel een string-literal is.
|
|
173
|
+
|
|
174
|
+
De compiler kan een woordenboek alleen vooraf laden wanneer deze de sleutel direct op de aanroeplocatie kan lezen. Bij een berekende sleutel wordt de optimalisatie stilzwijgend overgeslagen en worden in plaats daarvan alle woordenboeken gebundeld.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// ✗ Gemeld
|
|
178
|
+
useIntlayer(dictionaryKey);
|
|
179
|
+
useIntlayer(`home-${suffix}`);
|
|
180
|
+
getTranslations({ namespace: page });
|
|
181
|
+
|
|
182
|
+
// ✗ Een variabele is nog steeds geen literal
|
|
183
|
+
const key = "home";
|
|
184
|
+
useIntlayer(key);
|
|
185
|
+
|
|
186
|
+
// ✓ Goed
|
|
187
|
+
useIntlayer("home");
|
|
188
|
+
getTranslations({ namespace: "home" });
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Dit geldt voor `useIntlayer`, `getIntlayer` en elke compatibiliteitsadapter (`useTranslation`, `useTranslations`, `formatMessage`, `<FormattedMessage id>`, `<Trans i18nKey>`, …).
|
|
192
|
+
|
|
193
|
+
### `no-dynamic-field-access`
|
|
194
|
+
|
|
195
|
+
Vereist dat het veld dat u uit een woordenboek leest statisch bekend is.
|
|
196
|
+
|
|
197
|
+
De compiler verwijdert velden waarvan hij niet ziet dat ze worden gebruikt. Een dynamisch berekende toegang is onzichtbaar voor de compiler, waardoor het lezen tijdens runtime `undefined` kan retourneren.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// ✗ Gemeld
|
|
201
|
+
const content = useIntlayer("home");
|
|
202
|
+
content[fieldName];
|
|
203
|
+
|
|
204
|
+
const t = useTranslations("home");
|
|
205
|
+
t(messageKey);
|
|
206
|
+
|
|
207
|
+
// ✓ Goed
|
|
208
|
+
content.title;
|
|
209
|
+
content["title"];
|
|
210
|
+
content.items[0];
|
|
211
|
+
t("hero.title");
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `enforce-adapter-import`
|
|
215
|
+
|
|
216
|
+
Geeft de voorkeur aan de `@intlayer/*` compatibiliteitsadapter boven het originele pakket. Het origineel verwijst alleen naar Intlayer als de bundler-alias is geconfigureerd; de adapter doet dit altijd. Automatisch te herstellen met `--fix`.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// ✗ Gemeld
|
|
220
|
+
import { useTranslation } from "react-i18next";
|
|
221
|
+
import { getTranslations } from "next-intl/server";
|
|
222
|
+
|
|
223
|
+
// ✓ Goed
|
|
224
|
+
import { useTranslation } from "@intlayer/react-i18next";
|
|
225
|
+
import { getTranslations } from "@intlayer/next-intl/server";
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### `no-unused-content`
|
|
229
|
+
|
|
230
|
+
**Standaard uitgeschakeld.** Meldt inhoud die nergens in uw project wordt gelezen, plus woordenboeksleutels die op meer dan één plek zijn gedeclareerd.
|
|
231
|
+
|
|
232
|
+
```typescript fileName="src/home.content.ts"
|
|
233
|
+
export default {
|
|
234
|
+
key: "home", // ✗ Gemeld wanneer geen enkele aanroeper in het project om "home" vraagt
|
|
235
|
+
content: {
|
|
236
|
+
title: t({ nl: "Titel", en: "Title" }),
|
|
237
|
+
|
|
238
|
+
// ✗ Gemeld wanneer niets `hero` leest
|
|
239
|
+
hero: {
|
|
240
|
+
subtitle: t({ nl: "Subtitel", en: "Subtitle" }),
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
In tegenstelling tot de andere regels kan deze regel niet alleen oordelen op basis van het geopende bestand — een veld is alleen ongebruikt ten opzichte van het hele project. Bij de eerste inhoudsdeclaratie van een lint-run laadt deze uw Intlayer-configuratie, scant de bronbestanden die de configuratie declareert (`build.traversePattern`, `compiler.transformPattern`) en voert dezelfde gebruiksanalysator uit die `@intlayer/lsp` en de doorhaling "ongebruikt" in de VS Code-extensie aandrijft. Het resultaat wordt gedurende `cacheTtl` milliseconden in de cache opgeslagen, zodat de scan eenmaal per run plaatsvindt in plaats van per bestand.
|
|
247
|
+
|
|
248
|
+
**Opties**
|
|
249
|
+
|
|
250
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
251
|
+
{
|
|
252
|
+
"intlayer/no-unused-content": [
|
|
253
|
+
"warn",
|
|
254
|
+
{
|
|
255
|
+
// Meld woordenboeksleutels waarnaar niets verwijst. Standaard: true
|
|
256
|
+
reportUnusedDictionaries: true,
|
|
257
|
+
|
|
258
|
+
// Meld inhoudsvelden die niets leest. Standaard: true
|
|
259
|
+
reportUnusedFields: true,
|
|
260
|
+
|
|
261
|
+
// Meld sleutels die op meer dan één plek zijn gedeclareerd. Standaard: true
|
|
262
|
+
reportDuplicateKeys: true,
|
|
263
|
+
|
|
264
|
+
// Reguliere expressies voor veldpaden die nooit gemeld mogen worden.
|
|
265
|
+
ignoreFields: ["^meta"],
|
|
266
|
+
|
|
267
|
+
// Projectroot waar de scan begint. Standaard: de werkmap van ESLint
|
|
268
|
+
baseDir: process.cwd(),
|
|
269
|
+
|
|
270
|
+
// Hoe lang een projectscan wordt hergebruikt, in ms. Standaard: 30000
|
|
271
|
+
cacheTtl: 30000,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Verlaag `cacheTtl` wanneer u lint vanuit een langlopende editorserver en wilt dat bewerkingen sneller worden weergegeven; stel `baseDir` in wanneer een enkele lint-run meerdere Intlayer-projecten in een monorepo omvat.
|
|
278
|
+
|
|
279
|
+
> **Neigt naar stilte.** Een vals-positief resultaat zou hier een vertaling verwijderen, dus er wordt niets gemeld wanneer het woordenboek wordt gebruikt op een manier die de analyse niet kan volgen: het inhoudsobject in zijn geheel doorgegeven, een vertaalfunctie die eraan is gebonden (`const t = useTranslations("home")`), een declaratie bereikt via een directe import (`useDictionary(myDictionary)`), een `nest()` vanuit een ander woordenboek of een veldenlijst die niet-exhaustief is gemaakt door een spread. Single-file componenten (`.vue`, `.svelte`, `.astro`) tellen alsof ze elk veld gebruiken van de woordenboeken die ze vermelden, omdat hun scriptblokken hier niet worden geparseerd.
|
|
280
|
+
|
|
281
|
+
`reportDuplicateKeys` leest de niet-samengevoegde woordenboeken die de build wegschrijft onder `.intlayer/`, dus het blijft stil totdat het project ten minste eenmaal is gebouwd. Twee declaraties die een sleutel delen worden samengevoegd, wat een legitiem patroon is — het rapport bestaat omdat een veld dat aan beide zijden is gedefinieerd stilzwijgend slechts een van de twee waarden behoudt.
|
|
282
|
+
|
|
283
|
+
De analyser wordt geladen vanuit `@intlayer/lsp`, dat als ESM wordt gedistribueerd. De regel vereist daarom een Node-versie die een ES-module kan `require()`-en — Node 20.19+ of 22.12+. Op oudere versies meldt het niets in plaats van de lint-run te laten mislukken.
|
|
284
|
+
|
|
285
|
+
## Frameworks
|
|
286
|
+
|
|
287
|
+
Elke regel werkt in alle Intlayer-integraties, inclusief binnen Vue-, Svelte- en Angular-templates. U hoeft ESLint alleen te vertellen welke parser elk bestandstype leest.
|
|
288
|
+
|
|
289
|
+
| Framework | Bestanden | 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-templates | `.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
|
+
Installeer alleen de parsers die uw project nodig heeft.
|
|
335
|
+
|
|
336
|
+
> **Bekende beperking.** In Vue- en Angular-templates wordt een expressie zoals `{{ content[key] }}` niet gecontroleerd door `no-dynamic-field-access`. Dynamische aanroepen geschreven in het scriptblok worden normaal opgevangen.
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
---
|
|
2
|
+
createdAt: 2026-08-12
|
|
3
|
+
updatedAt: 2026-08-12
|
|
4
|
+
title: Wtyczka ESLint | Reguły lintera dla Intlayer
|
|
5
|
+
description: Wykrywaj zahardkodowane ciągi znaków, wywołania dynamiczne, których kompilator Intlayer nie jest w stanie zoptymalizować, oraz nieużywaną zawartość słowników dzięki eslint-plugin-intlayer. Działa z ESLint i oxlint w React, Vue, Svelte, Angular i Astro.
|
|
6
|
+
keywords:
|
|
7
|
+
- Intlayer
|
|
8
|
+
- ESLint
|
|
9
|
+
- oxlint
|
|
10
|
+
- Linting
|
|
11
|
+
- i18n
|
|
12
|
+
- Internacjonalizacja
|
|
13
|
+
- no-raw-text
|
|
14
|
+
- Zahardkodowane ciągi znaków
|
|
15
|
+
- Nieużywane tłumaczenia
|
|
16
|
+
- Martwa zawartość
|
|
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: "Inicjalna historia"
|
|
28
|
+
author: aymericzip
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Wtyczka ESLint x OXLint
|
|
32
|
+
|
|
33
|
+
`eslint-plugin-intlayer` wychwytuje rodzaje błędów i18n, których TypeScript nie jest w stanie wykryć:
|
|
34
|
+
|
|
35
|
+
1. **Zahardkodowany tekst**, który nigdy nie trafił do słownika.
|
|
36
|
+
2. **Dynamiczne wywołania**, które przechodzą sprawdzanie typów i działają, ale których kompilator Intlayer nie potrafi zoptymalizować.
|
|
37
|
+
3. **Martwa zawartość (Dead content)** — słowniki i pola, których nic w projekcie nie odczytuje (opcjonalne).
|
|
38
|
+
|
|
39
|
+
Nieznane klucze słowników, nieznane ścieżki pól oraz brakujące ustawienia regionalne stanowią już błędy kompilacji, więc wtyczka ich nie powiela.
|
|
40
|
+
|
|
41
|
+
## Instalacja
|
|
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
|
+
Wymaga ESLint w wersji 9 lub nowszej (flat config).
|
|
56
|
+
|
|
57
|
+
## Użycie
|
|
58
|
+
|
|
59
|
+
Wtyczka działa zarówno w ESLint, jak i [oxlint](https://oxc.rs) — te same reguły, te same opcje.
|
|
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
|
+
Lub włączaj reguły pojedynczo:
|
|
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
|
+
Dwa zastrzeżenia: obsługa wtyczek JS w oxlint jest w fazie alfa, a oxlint nie obsługuje niestandardowych parserów — pliki `.vue`, `.svelte`, `.astro` oraz szablony Angular nie są tam sprawdzane. Uruchamiaj oxlint na plikach JS/TS/JSX, a ESLint pozostaw dla reszty.
|
|
105
|
+
|
|
106
|
+
Reguła `no-unused-content` została celowo pominięta powyżej: wymaga ona katalogu roboczego i ścieżki do sprawdzanego pliku z kontekstu reguły, czego mostek wtyczek JS w fazie alfa nie gwarantuje. Uruchamiaj ją pod ESLintem.
|
|
107
|
+
|
|
108
|
+
</Tab>
|
|
109
|
+
</Tabs>
|
|
110
|
+
|
|
111
|
+
### Konfiguracje
|
|
112
|
+
|
|
113
|
+
| Konfiguracja | `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 (+ literały poza JSX) | error | error | error | off |
|
|
117
|
+
| `contract-only` | off | error | error | off | off |
|
|
118
|
+
|
|
119
|
+
`recommended` celowo utrzymuje `no-raw-text` na poziomie `warn`: uruchomienie jej na istniejącej bazie kodu ujawnia wszystkie nieprzetłumaczone ciągi znaków naraz, co nie powinno blokować procesu budowania od pierwszego dnia.
|
|
120
|
+
|
|
121
|
+
`enforce-adapter-import` jest domyślnie wyłączona — włącz ją jawnie, jeśli tego potrzebujesz.
|
|
122
|
+
|
|
123
|
+
`no-unused-content` jest wyłączona w każdej konfiguracji, w tym `strict`. Jest to jedyna reguła, która odczytuje konfigurację Intlayer i przeszukuje pliki źródłowe na dysku, więc jej włączenie powinno być świadomym wyborem, a nie domyślnym zachowaniem zestawu.
|
|
124
|
+
|
|
125
|
+
## Reguły
|
|
126
|
+
|
|
127
|
+
### `no-raw-text`
|
|
128
|
+
|
|
129
|
+
Zgłasza tekst widoczny dla użytkownika, który nie jest zadeklarowany w słowniku. Używa tej samej metody detekcji co `intlayer extract`, dzięki czemu nazwy marek, klasy CSS i identyfikatory techniczne są ignorowane.
|
|
130
|
+
|
|
131
|
+
```jsx
|
|
132
|
+
// ✗ Zgłoszone
|
|
133
|
+
<h1>Welcome to our documentation</h1>
|
|
134
|
+
<input placeholder="Enter your email address" />
|
|
135
|
+
|
|
136
|
+
// ✓ Prawidłowo
|
|
137
|
+
const { title } = useIntlayer("home");
|
|
138
|
+
<h1>{title}</h1>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Pliki deklaracji zawartości (`*.content.ts`, …) są pomijane.
|
|
142
|
+
|
|
143
|
+
Aby naprawić cały plik naraz, uruchom `npx intlayer extract`, a kompilator automatycznie przeniesie ciągi znaków do słownika.
|
|
144
|
+
|
|
145
|
+
**Opcje**
|
|
146
|
+
|
|
147
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
148
|
+
{
|
|
149
|
+
"intlayer/no-raw-text": [
|
|
150
|
+
"warn",
|
|
151
|
+
{
|
|
152
|
+
// Atrybuty, których wartością jest tekst widoczny dla użytkownika.
|
|
153
|
+
// Domyślnie: title, placeholder, alt, aria-label, label
|
|
154
|
+
attributes: ["title", "placeholder", "alt", "aria-label", "label"],
|
|
155
|
+
|
|
156
|
+
// Elementy, których zawartość nigdy nie jest tekstem widocznym dla użytkownika.
|
|
157
|
+
// Domyślnie: code, pre, script, style
|
|
158
|
+
ignoreElements: ["code", "pre", "script", "style"],
|
|
159
|
+
|
|
160
|
+
// Wyrażenia regularne dla tekstu, który nigdy nie powinien być zgłaszany.
|
|
161
|
+
ignorePatterns: ["^Powered by"],
|
|
162
|
+
|
|
163
|
+
// Zgłaszaj także literały łańcuchowe poza markupem. Domyślnie: false
|
|
164
|
+
includeStringLiterals: false,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `static-dictionary-key`
|
|
171
|
+
|
|
172
|
+
Wymaga, aby klucz słownika był literałem łańcuchowym.
|
|
173
|
+
|
|
174
|
+
Kompilator może wstępnie załadować słownik tylko wtedy, gdy może bezpośrednio odczytać klucz w miejscu wywołania. W przypadku obliczanego klucza optymalizacja jest po cichu pomijana i zamiast tego dołączane są wszystkie słowniki.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// ✗ Zgłoszone
|
|
178
|
+
useIntlayer(dictionaryKey);
|
|
179
|
+
useIntlayer(`home-${suffix}`);
|
|
180
|
+
getTranslations({ namespace: page });
|
|
181
|
+
|
|
182
|
+
// ✗ Zmienna nadal nie jest literałem
|
|
183
|
+
const key = "home";
|
|
184
|
+
useIntlayer(key);
|
|
185
|
+
|
|
186
|
+
// ✓ Prawidłowo
|
|
187
|
+
useIntlayer("home");
|
|
188
|
+
getTranslations({ namespace: "home" });
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Dotyczy to `useIntlayer`, `getIntlayer` oraz każdego adaptera kompatybilności (`useTranslation`, `useTranslations`, `formatMessage`, `<FormattedMessage id>`, `<Trans i18nKey>`, …).
|
|
192
|
+
|
|
193
|
+
### `no-dynamic-field-access`
|
|
194
|
+
|
|
195
|
+
Wymaga, aby pole odczytywane ze słownika było znane statycznie.
|
|
196
|
+
|
|
197
|
+
Kompilator usuwa pola, których użycia nie zarejestruje. Dostęp dynamiczny jest dla niego niewidoczny, więc odczyt może zwrócić `undefined` w czasie wykonywania.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// ✗ Zgłoszone
|
|
201
|
+
const content = useIntlayer("home");
|
|
202
|
+
content[fieldName];
|
|
203
|
+
|
|
204
|
+
const t = useTranslations("home");
|
|
205
|
+
t(messageKey);
|
|
206
|
+
|
|
207
|
+
// ✓ Prawidłowo
|
|
208
|
+
content.title;
|
|
209
|
+
content["title"];
|
|
210
|
+
content.items[0];
|
|
211
|
+
t("hero.title");
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `enforce-adapter-import`
|
|
215
|
+
|
|
216
|
+
Preferuje adapter kompatybilności `@intlayer/*` zamiast oryginalnego pakietu. Oryginalny pakiet rozwiązuje się do Intlayer tylko wtedy, gdy skonfigurowany jest alias bundlera; adapter działa zawsze. Możliwość automatycznej naprawy za pomocą `--fix`.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// ✗ Zgłoszone
|
|
220
|
+
import { useTranslation } from "react-i18next";
|
|
221
|
+
import { getTranslations } from "next-intl/server";
|
|
222
|
+
|
|
223
|
+
// ✓ Prawidłowo
|
|
224
|
+
import { useTranslation } from "@intlayer/react-i18next";
|
|
225
|
+
import { getTranslations } from "@intlayer/next-intl/server";
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### `no-unused-content`
|
|
229
|
+
|
|
230
|
+
**Domyślnie wyłączona.** Zgłasza zawartość, której nic w projekcie nie odczytuje, oraz klucze słowników zadeklarowane w więcej niż jednym miejscu.
|
|
231
|
+
|
|
232
|
+
```typescript fileName="src/home.content.ts"
|
|
233
|
+
export default {
|
|
234
|
+
key: "home", // ✗ Zgłaszane, gdy żadne wywołanie w projekcie nie odpytuje o "home"
|
|
235
|
+
content: {
|
|
236
|
+
title: t({ pl: "Tytuł", en: "Title" }),
|
|
237
|
+
|
|
238
|
+
// ✗ Zgłaszane, gdy nic nie odczytuje `hero`
|
|
239
|
+
hero: {
|
|
240
|
+
subtitle: t({ pl: "Podtytuł", en: "Subtitle" }),
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
W przeciwieństwie do innych reguł, ta nie jest w stanie ocenić sytuacji wyłącznie na podstawie sprawdzanego pliku — pole jest nieużywane tylko w kontekście całego projektu. Przy pierwszej deklaracji zawartości podczas działania lintera wczytuje konfigurację Intlayer, skanuje pliki źródłowe wskazane przez tę konfigurację (`build.traversePattern`, `compiler.transformPattern`) i uruchamia ten sam analizator użycia, który zasila `@intlayer/lsp` oraz przekreślenie „nieużywane” w rozszerzeniu VS Code. Wynik jest buforowany przez `cacheTtl` milisekund, więc skanowanie odbywa się raz na uruchomienie, a nie dla każdego pliku.
|
|
247
|
+
|
|
248
|
+
**Opcje**
|
|
249
|
+
|
|
250
|
+
```javascript fileName="eslint.config.mjs" codeFormat="esm"
|
|
251
|
+
{
|
|
252
|
+
"intlayer/no-unused-content": [
|
|
253
|
+
"warn",
|
|
254
|
+
{
|
|
255
|
+
// Zgłaszaj klucze słowników, do których nic się nie odwołuje. Domyślnie: true
|
|
256
|
+
reportUnusedDictionaries: true,
|
|
257
|
+
|
|
258
|
+
// Zgłaszaj pola zawartości, których nic nie odczytuje. Domyślnie: true
|
|
259
|
+
reportUnusedFields: true,
|
|
260
|
+
|
|
261
|
+
// Zgłaszaj klucze zadeklarowane w więcej niż jednym miejscu. Domyślnie: true
|
|
262
|
+
reportDuplicateKeys: true,
|
|
263
|
+
|
|
264
|
+
// Wyrażenia regularne dla ścieżek pól, które nigdy nie powinny być zgłaszane.
|
|
265
|
+
ignoreFields: ["^meta"],
|
|
266
|
+
|
|
267
|
+
// Katalog główny projektu, od którego zaczyna się skanowanie. Domyślnie: katalog roboczy ESLint
|
|
268
|
+
baseDir: process.cwd(),
|
|
269
|
+
|
|
270
|
+
// Czas ponownego użycia skanu projektu (w ms). Domyślnie: 30000
|
|
271
|
+
cacheTtl: 30000,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Zmniejsz `cacheTtl`, gdy korzystasz z lintera działającego jako serwer edytora i chcesz szybciej widzieć zmiany; ustaw `baseDir`, gdy jedno uruchomienie lintera obejmuje kilka projektów Intlayer w monorepo.
|
|
278
|
+
|
|
279
|
+
> **Preferuje brak zgłoszenia w razie wątpliwości.** Fałszywy alarm w tym miejscu mógłby usunąć potrzebne tłumaczenie, dlatego nic nie jest zgłaszane, gdy słownik jest używany w sposób, którego analiza nie potrafi prześledzić: przekazanie całego obiektu zawartości, powiązana z niego funkcja tłumacząca (`const t = useTranslations("home")`), deklaracja dostępna przez bezpośredni import (`useDictionary(myDictionary)`), `nest()` z innego słownika lub lista pól, która stała się niepełna przez operator spread. Komponenty jednoplikowe (`.vue`, `.svelte`, `.astro`) są traktowane jako używające każdego pola wymienionych słowników, ponieważ ich bloki skryptów nie są tu parsowane.
|
|
280
|
+
|
|
281
|
+
`reportDuplicateKeys` odczytuje niescalone słowniki, które proces budowania zapisuje w `.intlayer/`, więc zachowuje milczenie do momentu, aż projekt zostanie zbudowany przynajmniej raz. Dwie deklaracje dzielące ten sam klucz są scalane, co jest poprawnym wzorcem — raport istnieje, ponieważ pole zdefiniowane po obu stronach po cichu zachowuje tylko jedną z dwóch wartości.
|
|
282
|
+
|
|
283
|
+
Analizator jest ładowany z `@intlayer/lsp`, który jest dystrybuowany jako ESM. Reguła wymaga zatem wersji Node obsługującej `require()` dla modułów ES — Node 20.19+ lub 22.12+. Na starszych wersjach reguła nic nie zgłasza, zamiast powodować błąd działania lintera.
|
|
284
|
+
|
|
285
|
+
## Frameworki
|
|
286
|
+
|
|
287
|
+
Każda reguła działa we wszystkich integracjach Intlayer, w tym wewnątrz szablonów Vue, Svelte i Angular. Wystarczy wskazać ESLintowi, który parser obsługuje dany typ pliku.
|
|
288
|
+
|
|
289
|
+
| Framework | Pliki | 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
|
+
| Szablony 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
|
+
Instaluj tylko te parsery, których wymaga Twój projekt.
|
|
335
|
+
|
|
336
|
+
> **Znane ograniczenie.** W szablonach Vue i Angular wyrażenie takie jak `{{ content[key] }}` nie jest sprawdzane przez `no-dynamic-field-access`. Odczyty dynamiczne zapisane w bloku script są wykrywane w normalny sposób.
|