Автор:
    Дата створення:2026-08-12Останнє оновлення:2026-08-13

    Плагін ESLint x OXLint

    eslint-plugin-intlayer виявляє ті типи помилок i18n, які TypeScript не здатний помітити:

    1. Жорстко закодований текст, який так і не потрапив до словника.
    2. Динамічні виклики, які проходять перевірку типів і виконуються, але які компілятор Intlayer не може оптимізувати.
    3. Мертвий вміст (Dead content) — словники та поля, які ніде в проєкті не зчитуються (за бажанням/opt-in).

    Невідомі ключі словників, невідомі шляхи до полів та відсутні локалі вже є помилками компіляції, тому плагін не дублює їх повідомлення.

    Встановлення

    bash
    npm install --save-dev eslint-plugin-intlayer

    Потрібен ESLint 9 або новішої версії (flat config). ESLint 10 підтримується.

    Використання

    Плагін працює як в ESLint, так і в oxlint — однакові правила, однакові параметри.

    Або розгорніть конфігурацію та задайте рівні самостійно:

    Конфігурації (Configs)

    Конфігурація no-raw-text static-dictionary-key no-dynamic-field-access enforce-adapter-import no-unused-content
    recommended warn error error off off
    strict error (+ рядкові літерали поза JSX) error error error off
    contract-only off error error off off

    recommended навмисно залишає no-raw-text зі статусом warn: застосування правила до наявної кодової бази виявить усі неперекладені рядки одночасно, що не повинно ламати збірку з першого ж дня.

    enforce-adapter-import типово вимкнено — увімкніть його явно, якщо це необхідно.

    no-unused-content вимкнено в усіх пресетах, включно зі strict. Це єдине правило, яке зчитує конфігурацію Intlayer і сканує вихідні файли з диска, тому його ввімкнення має бути свідомим вибором.

    Правила

    no-raw-text

    Повідомляє про текст для користувача, який не оголошено у словнику. Використовує ту саму логіку виявлення, що й intlayer extract, тому назви брендів, класи CSS та технічні ідентифікатори ігноруються.

    jsx
    // ✗ Повідомлено<h1>Welcome to our documentation</h1><input placeholder="Enter your email address" />// ✓ Усе добреconst { title } = useIntlayer("home");<h1>{title}</h1>

    Файли оголошення вмісту (*.content.ts, …) пропускаються.

    Щоб виправити весь файл одночасно, виконайте npx intlayer extract, і компілятор автоматично перенесе рядки до словника.

    Параметри

    static-dictionary-key

    Вимагає, щоб ключ словника був рядковим літералом.

    Компілятор може попередньо завантажити словник лише тоді, коли може прочитати ключ безпосередньо в місці виклику. У разі використання обчислюваного ключа оптимізація мовчки пропускається, і замість цього в бандл включаються всі словники.

    typescript
    // ✗ ПовідомленоuseIntlayer(dictionaryKey);useIntlayer(`home-${suffix}`);getTranslations({ namespace: page });// ✗ Змінна все одно не є літераломconst key = "home";useIntlayer(key);// ✓ Усе добреuseIntlayer("home");getTranslations({ namespace: "home" });

    Це стосується useIntlayer, getIntlayer та всіх адаптерів сумісності (useTranslation, useTranslations, formatMessage, <FormattedMessage id>, <Trans i18nKey>, …).

    no-dynamic-field-access

    Вимагає, щоб поле, яке зчитується зі словника, було статично відомим.

    Компілятор видаляє поля, використання яких він не виявив. Динамічний доступ для нього невидимий, тому читання може повернути undefined під час виконання.

    typescript
    // ✗ Повідомленоconst content = useIntlayer("home");content[fieldName];const t = useTranslations("home");t(messageKey);// ✓ Усе добреcontent.title;content["title"];content.items[0];t("hero.title");

    enforce-adapter-import

    Віддає перевагу адаптеру сумісності @intlayer/* перед оригінальним пакетом. Оригінальний пакет переходить в Intlayer лише за наявності налаштованого псевдоніма бандлера; адаптер працює завжди. Підтримує автовиправлення через --fix.

    typescript
    // ✗ Повідомленоimport { useTranslation } from "react-i18next";import { getTranslations } from "next-intl/server";// ✓ Усе добреimport { useTranslation } from "@intlayer/react-i18next";import { getTranslations } from "@intlayer/next-intl/server";

    no-unused-content

    Типово вимкнено. Повідомляє про вміст, який ніде в проєкті не зчитується, а також про ключі словників, оголошені в кількох місцях.

    src/home.content.ts
    export default {  key: "home", // ✗ Повідомляється, якщо жоден виклик у проєкті не запитує "home"  content: {    title: t({ uk: "Заголовок", en: "Title" }),    // ✗ Повідомляється, якщо ніщо не зчитує `hero`    hero: {      subtitle: t({ uk: "Підзаголовок", en: "Subtitle" }),    },  },};

    На відміну від інших правил, це правило не може вирішити лише за поточним файлом — поле є невикористаним лише відносно всього проєкту. Під час першого оголошення вмісту під час лінтингу воно завантажує конфігурацію Intlayer, сканує вихідні файли за шляхами з конфігурації (build.traversePattern, compiler.transformPattern) і запускає той самий аналізатор використання, який живить @intlayer/lsp та закреслення «невикористаного» в розширенні VS Code. Результат кешується на cacheTtl мілісекунд, тому сканування відбувається один раз за запуск, а не для кожного файлу.

    Параметри

    Зменште cacheTtl, якщо ви запускаєте лінтинг із довгоживучого сервера редактора і хочете швидше бачити зміни; встановіть baseDir, коли один запуск лінтингу охоплює кілька проєктів Intlayer у монорепозиторії.

    Схильне до мінімізації помилкових спрацьовувань. Хибне спрацьовування тут призведе до видалення потрібного перекладу, тому нічого не повідомляється, якщо словник використовується способом, який аналіз не може відстежити: об'єкт вмісту передано повністю, прив'язана функція перекладача (const t = useTranslations("home")), оголошення отримано через прямий імпорт (useDictionary(myDictionary)), виклик nest() з іншого словника або список полів, який став невичерпним через оператор spread. Однофайлові компоненти (.vue, .svelte, .astro) вважаються такими, що використовують кожне поле згаданих словників, оскільки їхні блоки скриптів тут не парсяться.

    reportDuplicateKeys зчитує необ'єднані словники, які збірка записує у .intlayer/, тому воно залишається неактивним, доки проєкт не буде зібрано принаймні один раз. Два оголошення з однаковим ключем об'єднуються, що є коректним шаблоном — звіт формується тому, що поле, визначене з обох боків, непомітно зберігає лише одне з двох значень.

    Аналізатор завантажується з @intlayer/lsp, який постачається як ESM. Тому правилу потрібна версія Node, здатна виконувати require() для ES-модулів — Node 20.19+ або 22.12+. На старіших версіях воно нічого не повідомляє, щоб не зупиняти процес лінтингу.

    Фреймворки

    Кожне правило працює в усіх інтеграціях Intlayer, включно з шаблонами Vue, Svelte та Angular. Потрібно лише вказати ESLint, який парсер зчитує кожен тип файлів.

    Фреймворк Файли Парсер
    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
    Шаблони Angular .component.html @angular-eslint/template-parser
    Astro .astro astro-eslint-parser

    Встановлюйте лише ті парсери, які потрібні вашому проєкту.

    Відоме обмеження. У шаблонах Vue та Angular вираз на кшталт {{ content[key] }} не перевіряється правилом no-dynamic-field-access. Динамічні звернення всередині блоку script виявляються у звичайному режимі.