--- createdAt: 2026-08-12 updatedAt: 2026-08-13 title: Plugin ESLint | Regras de lint para o Intlayer description: Detecte strings codificadas diretamente, chamadas dinâmicas que o compilador do Intlayer não consegue otimizar e conteúdo de dicionário não utilizado com eslint-plugin-intlayer. Compatível com ESLint e oxlint, no React, Vue, Svelte, Angular e Astro. keywords: - Intlayer - ESLint - oxlint - Linting - i18n - Internacionalização - no-raw-text - Strings codificadas diretamente - Traduções não utilizadas - Conteúdo morto - React - Vue - Svelte - Angular slugs: - doc - eslint history: - version: 9.3.1 date: 2026-08-12 changes: "Histórico inicial" author: aymericzip --- # Plugin ESLint x OXLint O `eslint-plugin-intlayer` detecta os tipos de erros de i18n que o TypeScript não consegue identificar: 1. **Texto codificado diretamente (hardcoded)** que nunca chegou a um dicionário. 2. **Chamadas dinâmicas** que passam na verificação de tipos e são executadas, mas que o compilador do Intlayer não consegue otimizar. 3. **Conteúdo morto** — dicionários e campos que nada no projeto lê (ativação opcional). Chaves de dicionário desconhecidas, caminhos de campos desconhecidos e idiomas ausentes já são erros de compilação, portanto o plugin não os repete. ## Instalação ```bash packageManager="npm" npm install --save-dev eslint-plugin-intlayer ``` ```bash packageManager="pnpm" pnpm add --save-dev eslint-plugin-intlayer ``` ```bash packageManager="yarn" yarn add --dev eslint-plugin-intlayer ``` Requer o ESLint 9 ou superior (flat config). O ESLint 10 é compatível. ## Utilização O plugin funciona tanto no ESLint quanto no [oxlint](https://oxc.rs) — com as mesmas regras e as mesmas opções. ```javascript fileName="eslint.config.mjs" codeFormat="esm" import intlayer from "eslint-plugin-intlayer"; export default [...intlayer.configs.recommended]; ``` Ou espalhe uma configuração e defina você mesmo as severidades: ```javascript fileName="eslint.config.mjs" codeFormat="esm" import intlayer from "eslint-plugin-intlayer"; export default [ ...intlayer.configs.recommended, { rules: { "intlayer/no-raw-text": "warn", "intlayer/static-dictionary-key": "error", "intlayer/no-dynamic-field-access": "error", "intlayer/enforce-adapter-import": "warn", "intlayer/no-unused-content": "warn", }, }, ]; ``` ```json fileName=".oxlintrc.json" { "jsPlugins": ["eslint-plugin-intlayer"], "rules": { "intlayer/no-raw-text": "warn", "intlayer/static-dictionary-key": "error", "intlayer/no-dynamic-field-access": "error", "intlayer/enforce-adapter-import": "warn" } } ``` Duas ressalvas: o suporte a plugins JS no oxlint ainda está em versão alfa e o oxlint não suporta parsers customizados — portanto, arquivos `.vue`, `.svelte`, `.astro` e templates do Angular não são verificados lá. Execute o oxlint nos seus arquivos JS/TS/JSX e mantenha o ESLint para o restante. O `no-unused-content` foi omitido acima de propósito: ele precisa do diretório de trabalho e do caminho do arquivo analisado a partir do contexto da regra, o que a ponte alfa de plugins JS não garante. Execute-o no ESLint. ### Configurações | Configuração | `no-raw-text` | `static-dictionary-key` | `no-dynamic-field-access` | `enforce-adapter-import` | `no-unused-content` | | --------------- | ------------------------------ | ----------------------- | ------------------------- | ------------------------ | ------------------- | | `recommended` | warn | error | error | off | off | | `strict` | error (+ literais fora de JSX) | error | error | error | off | | `contract-only` | off | error | error | off | off | A configuração `recommended` mantém deliberadamente `no-raw-text` como `warn`: apontá-la para uma base de código existente traz à tona todas as strings não traduzidas de uma só vez, o que não deve quebrar a sua compilação logo no primeiro dia. O `enforce-adapter-import` fica desativado por padrão — ative-o explicitamente se desejar. O `no-unused-content` fica desativado em todas as configurações, inclusive na `strict`. É a única regra que lê sua configuração do Intlayer e percorre seus arquivos de código no disco; portanto, ativá-la deve ser uma escolha consciente e não algo imposto por uma predefinição. ## Regras ### `no-raw-text` Reporta texto voltado ao usuário que não esteja declarado em um dicionário. Ele usa a mesma detecção do `intlayer extract`, portanto nomes de marcas, classes CSS e identificadores técnicos são ignorados. ```jsx // ✗ Reportado

Welcome to our documentation

// ✓ Correto const { title } = useIntlayer("home");

{title}

``` Arquivos de declaração de conteúdo (`*.content.ts`, …) são ignorados. Para corrigir um arquivo inteiro de uma só vez, execute `npx intlayer extract` e deixe o compilador mover as strings para um dicionário para você. **Opções** ```javascript fileName="eslint.config.mjs" codeFormat="esm" { "intlayer/no-raw-text": [ "warn", { // Atributos cujo valor é texto voltado ao usuário. // Padrão: title, placeholder, alt, aria-label, label attributes: ["title", "placeholder", "alt", "aria-label", "label"], // Elementos cujo conteúdo nunca é texto voltado ao usuário. // Padrão: code, pre, script, style ignoreElements: ["code", "pre", "script", "style"], // Expressões regulares para textos que nunca devem ser reportados. ignorePatterns: ["^Powered by"], // Também reportar literais de string fora do markup. Padrão: false includeStringLiterals: false, }, ], } ``` ### `static-dictionary-key` Exige que a chave do dicionário seja uma string literal. O compilador só consegue pré-carregar um dicionário quando pode ler a chave diretamente no local da chamada. Com uma chave calculada, ele pula silenciosamente a otimização e inclui todos os dicionários no bundle. ```typescript // ✗ Reportado useIntlayer(dictionaryKey); useIntlayer(`home-${suffix}`); getTranslations({ namespace: page }); // ✗ Uma variável ainda não é um literal const key = "home"; useIntlayer(key); // ✓ Correto useIntlayer("home"); getTranslations({ namespace: "home" }); ``` Isso se aplica ao `useIntlayer`, `getIntlayer` e a todos os adaptadores de compatibilidade (`useTranslation`, `useTranslations`, `formatMessage`, ``, ``, …). ### `no-dynamic-field-access` Exige que o campo lido de um dicionário seja conhecido estaticamente. O compilador remove campos que não são identificados como utilizados. Um acesso computado é invisível para ele, portanto a leitura pode retornar `undefined` em tempo de execução. ```typescript // ✗ Reportado const content = useIntlayer("home"); content[fieldName]; const t = useTranslations("home"); t(messageKey); // ✓ Correto content.title; content["title"]; content.items[0]; t("hero.title"); ``` ### `enforce-adapter-import` Prefere o adaptador de compatibilidade `@intlayer/*` ao pacote original. O original só é resolvido para o Intlayer quando o alias do empacotador está configurado; o adaptador sempre funciona. Corrigível automaticamente com `--fix`. ```typescript // ✗ Reportado import { useTranslation } from "react-i18next"; import { getTranslations } from "next-intl/server"; // ✓ Correto import { useTranslation } from "@intlayer/react-i18next"; import { getTranslations } from "@intlayer/next-intl/server"; ``` ### `no-unused-content` **Desativada por padrão.** Reporta conteúdo que nada em seu projeto lê, além de chaves de dicionário declaradas em mais de um local. ```typescript fileName="src/home.content.ts" export default { key: "home", // ✗ Reportado se nenhum chamador no projeto solicitar "home" content: { title: t({ pt: "Título", en: "Title" }), // ✗ Reportado se nada ler `hero` hero: { subtitle: t({ pt: "Subtítulo", en: "Subtitle" }), }, }, }; ``` Ao contrário das outras regras, esta não pode responder apenas com base no arquivo analisado — um campo só é considerado não utilizado em relação ao projeto inteiro. Na primeira declaração de conteúdo de uma execução do linter, ela carrega a sua configuração do Intlayer, busca os arquivos de código declarados por essa configuração (`build.traversePattern`, `compiler.transformPattern`) e executa o mesmo analisador de uso que alimenta o `@intlayer/lsp` e o tachado de "não utilizado" na extensão do VS Code. O resultado é armazenado em cache por `cacheTtl` milissegundos, para que a varredura ocorra uma vez por execução e não a cada arquivo. **Opções** ```javascript fileName="eslint.config.mjs" codeFormat="esm" { "intlayer/no-unused-content": [ "warn", { // Reportar chaves de dicionário que nada referencia. Padrão: true reportUnusedDictionaries: true, // Reportar campos de conteúdo que nada lê. Padrão: true reportUnusedFields: true, // Reportar chaves declaradas em mais de um lugar. Padrão: true reportDuplicateKeys: true, // Expressões regulares para caminhos de campos que nunca devem ser reportados. ignoreFields: ["^meta"], // Raiz do projeto a partir de onde a verificação começa. Padrão: diretório de trabalho do ESLint baseDir: process.cwd(), // Tempo de reutilização de uma varredura de projeto, em ms. Padrão: 30000 cacheTtl: 30000, }, ], } ``` Diminua `cacheTtl` ao executar o lint a partir de um servidor de editor de longa duração e quiser que as alterações apareçam mais rápido; defina `baseDir` quando uma única execução de lint cobrir vários projetos Intlayer em um monorepo. > **Tende ao silêncio.** Um falso positivo aqui apagaria uma tradução; portanto, nada é reportado quando o dicionário é consumido de uma forma que a análise não consiga rastrear: o objeto de conteúdo passado por completo, uma função de tradução vinculada a partir dele (`const t = useTranslations("home")`), uma declaração acessada por importação direta (`useDictionary(myDictionary)`), um `nest()` de outro dicionário ou uma lista de campos tornada não exaustiva por um spread. Componentes de arquivo único (`.vue`, `.svelte`, `.astro`) são considerados como usuários de todos os campos dos dicionários mencionados, pois seus blocos de script não são analisados aqui. O `reportDuplicateKeys` lê os dicionários não mesclados que o build grava em `.intlayer/`, portanto permanece em silêncio até que o projeto tenha sido construído pelo menos uma vez. Duas declarações compartilhando uma chave são mescladas, o que é um padrão válido — o aviso existe porque um campo definido em ambos os lados mantém silenciosamente apenas um dos dois valores. O analisador é carregado a partir do `@intlayer/lsp`, distribuído como ESM. A regra requer, portanto, uma versão do Node compatível com `require()` em módulos ES — Node 20.19+ ou 22.12+. Em versões anteriores, ela não reporta nada em vez de falhar a execução do lint. ## Frameworks Todas as regras funcionam em todas as integrações do Intlayer, inclusive dentro de templates Vue, Svelte e Angular. Você só precisa informar ao ESLint qual parser lê cada tipo de arquivo. | Framework | Arquivos | Parser | | ------------------------- | ----------------- | --------------------------------- | | React, Preact, Solid, Lit | `.jsx` `.tsx` | `typescript-eslint` | | Next.js | `.jsx` `.tsx` | `typescript-eslint` | | Vue, Nuxt | `.vue` | `vue-eslint-parser` | | Svelte, SvelteKit | `.svelte` | `svelte-eslint-parser` | | Angular | `.ts` | `typescript-eslint` | | Templates do Angular | `.component.html` | `@angular-eslint/template-parser` | | Astro | `.astro` | `astro-eslint-parser` | ```javascript fileName="eslint.config.mjs" codeFormat="esm" import intlayer from "eslint-plugin-intlayer"; import tseslint from "typescript-eslint"; import vueParser from "vue-eslint-parser"; import svelteParser from "svelte-eslint-parser"; import angularTemplateParser from "@angular-eslint/template-parser"; export default [ ...intlayer.configs.recommended, { files: ["**/*.{ts,tsx,jsx}"], languageOptions: { parser: tseslint.parser }, }, { files: ["**/*.vue"], languageOptions: { parser: vueParser, parserOptions: { parser: tseslint.parser }, }, }, { files: ["**/*.svelte"], languageOptions: { parser: svelteParser, parserOptions: { parser: tseslint.parser }, }, }, { files: ["**/*.component.html"], languageOptions: { parser: angularTemplateParser }, }, ]; ``` Instale apenas os parsers de que seu projeto precisa. > **Limitação conhecida.** Em templates do Vue e Angular, uma expressão como `{{ content[key] }}` não é verificada pelo `no-dynamic-field-access`. Leituras dinâmicas escritas no bloco script são identificadas normalmente.