Автор:
    Дата створення:2025-11-25Останнє оновлення:2026-06-07

    Оптимізація розміру бандлу та продуктивності i18n

    Одним із найпоширеніших викликів під час використання традиційних рішень i18n, що покладаються на JSON-файли, є керування розміром контенту. Якщо розробники не розділяють контент на простори імен (namespaces) вручну, користувачі часто завантажують переклади для кожної сторінки та потенційно кожної мови лише для того, щоб переглянути одну сторінку.

    Наприклад, програма з 10 сторінками, перекладена 10 мовами, може змусити користувача завантажити контент обсягом 100 сторінок, навіть якщо йому потрібна лише одна (поточна сторінка поточною мовою). Це призводить до марної витрати пропускної здатності (bandwidth) та сповільнення часу завантаження.

    Intlayer вирішує цю проблему за допомогою оптимізації під час збірки (build-time). Він аналізує ваш код для виявлення того, які саме словники реально використовуються в кожному компоненті, і реінжектить (перевставляє) у бандл лише необхідний контент.

    Зміст

    Проаналізуйте свій бандл

    Аналіз бандлу — це перший крок до виявлення "важких" JSON-файлів та пошуку можливостей для розділення коду (code-splitting). Відповідні інструменти генерують візуальну деревоподібну мапу (treemap) зібраного коду вашої програми, дозволяючи точно побачити, які бібліотеки займають найбільше місця.

    Vite / Rollup

    Vite під капотом використовує Rollup. Плагін rollup-plugin-visualizer створює інтерактивний HTML-файл, що відображає розмір кожного модуля у вашому графі.

    bash
    npm install -D rollup-plugin-visualizer
    vite.config.ts
    import { defineConfig } from "vite";import { visualizer } from "rollup-plugin-visualizer";export default defineConfig({ plugins: [   visualizer({     open: true, // Автоматично відкрити звіт у браузері     filename: "stats.html",     gzipSize: true,     brotliSize: true,   }), ],});

    Як це працює

    Intlayer використовує підхід на рівні компонентів (per-component approach). На відміну від глобальних JSON-файлів, ваш контент визначається поруч із вашими компонентами або всередині них. Під час збірки Intlayer:

    1. Аналізує ваш код, щоб знайти виклики useIntlayer.
    2. Збирає відповідний контент словників.
    3. Замінює виклик useIntlayer на оптимізований код, опираючись на вашу конфігурацію.

    Це гарантує, що:

    • Якщо компонент не імпортується, його контент не включатиметься до бандлу (Dead Code Elimination).
    • Якщо компонент завантажується ліниво (lazy-loaded), його контент також буде завантажено ліниво.

    Довідка щодо плагінів

    Оптимізація збірки від Intlayer розділена на кілька окремих плагінів, кожен з яких виконує лише одну задачу. Розуміння того, за що відповідає кожен із них, запобігає плутанині під час конфігурації.

    Плагіни Babel (@intlayer/babel)

    Ці плагіни використовуються безпосередньо в babel.config.js для налаштувань на базі Webpack (Next.js із Babel, CRA, кастомний Webpack тощо).

    Плагін Що він робить
    intlayerExtractBabelPlugin Сканує файли .content.ts та записує зібрані словники до папки .intlayer/
    intlayerOptimizeBabelPlugin Переписує useIntlayer('key') на useDictionary(hash) та інжектить (вставляє) відповідний import словника
    intlayerPurgeBabelPlugin Сканує всі файли з кодом та видаляє невикористані поля контенту зі скомпільованих словників .intlayer/**/*.json
    intlayerMinifyBabelPlugin Перейменовує ключі полів контенту на короткі алфавітні псевдоніми (наприклад, titlea) у JSON-файлах та у вихідному коді
    Порядок плагінів має значення. У файлі babel.config.js плагіни purge та minify мають з'являтися перед плагіном optimize. Оскільки optimize замінює useIntlayer('key') на непрозорий (opaque) виклик useDictionary(hash), він стирає інформацію про ключ словника. Але саме ця інформація потрібна на етапах purge та minify, щоб розпізнати, які поля контенту використовуються.

    Кожен плагін Babel має відповідного помічника налаштувань (options helper), який зчитує intlayer.config.ts лише раз під час завантаження конфігурації та повертає підготовані значення:

    Options helper З яким плагіном використовується
    getExtractPluginOptions() intlayerExtractBabelPlugin
    getOptimizePluginOptions() intlayerOptimizeBabelPlugin
    getPurgePluginOptions() intlayerPurgeBabelPlugin
    getMinifyPluginOptions() intlayerMinifyBabelPlugin

    Плагіни Vite (vite-intlayer)

    Користувачі Vite ніколи не конфігурують їх напряму. Вони налаштовуються автоматично після виклику withIntlayer() у файлі vite.config.ts. Прапорці build.purge та build.minify в intlayer.config.ts вмикають та вимикають відповідну поведінку без необхідності вручну реєструвати додаткові плагіни.

    Внутрішній плагін Vite Еквівалентна поведінка
    Usage analyzer Аналогічно до етапу аналізу плагіна intlayerPurgeBabelPlugin
    Dictionary prune Аналогічно до етапу запису JSON плагіна intlayerPurgeBabelPlugin
    Dictionary minify Аналогічно до етапу запису JSON плагіна intlayerMinifyBabelPlugin
    Babel transform Аналогічно до перейменування у вихідному коді плагіном intlayerMinifyBabelPlugin + intlayerOptimizeBabelPlugin

    Налаштування за платформами

    Next.js

    Next.js потребує плагіна @intlayer/swc для етапу оптимізації (перезапису імпорту), оскільки Next.js використовує SWC для збірки.

    Цей плагін не встановлюється автоматично, адже плагіни SWC для Next.js ще мають експериментальний статус. У майбутньому це може змінитися.
    bash
    npm install -D @intlayer/swc

    Після встановлення Intlayer автоматично виявить і почне використовувати цей плагін.

    Для етапів purge та minify (видалення та перейменування полів) вам треба встановити поруч @intlayer/babel і додати Babel-плагіни. Оскільки Next.js хоча й застосовує SWC для трансформацій, але продовжує зчитувати babel.config.js під час збирання налаштувань плагінів, Babel-плагіни запускатимуться як пре-пас (попередня обробка) перед SWC.

    bash
    npm install -D @intlayer/babel
    babel.config.js
    const { intlayerPurgeBabelPlugin, intlayerMinifyBabelPlugin, getPurgePluginOptions, getMinifyPluginOptions,} = require("@intlayer/babel");module.exports = { presets: ["next/babel"], plugins: [   // Purge: видалити невикористані поля контенту з .intlayer/**/*.json   [intlayerPurgeBabelPlugin, getPurgePluginOptions()],   // Minify: перейменувати ключі полів контенту в JSON-файлах та вихідному коді   [intlayerMinifyBabelPlugin, getMinifyPluginOptions()],   // Примітка: intlayerOptimizeBabelPlugin тут НЕ потрібен, тому що   // @intlayer/swc вже відповідає за переписування useIntlayer → useDictionary. ],};

    Конфігурація

    Ви можете контролювати те, як Intlayer оптимізує ваш бандл, за допомогою властивості build у файлі intlayer.config.ts.

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.UKRAINIAN],    defaultLocale: Locales.ENGLISH,  },  dictionary: {    importMode: "dynamic",  },  build: {    // Замінювати виклики useIntlayer() на прямі імпорти словників під час збірки.    // undefined = auto (увімкнено для production-збірки), true = always, false = never.    optimize: undefined,    // Перейменовувати ключі полів контенту в скомпільованих словниках на короткі алфавітні    // псевдоніми (напр. title → a). Зменшує розмір JSON; потребує увімкненого optimize.    minify: true,    // Видалити поля контенту, які жодного разу не викликалися (not accessed) у вихідному коді.    // Потребує увімкненого optimize.    purge: true,  },};export default config;
    У більшості випадків рекомендується залишати для optimize значення за замовчуванням (undefined).
    Перегляньте повний довідник конфігурації для усіх доступних опцій: Configuration

    Опції збірки (Build Options)

    Властивість Тип Значення за замовчуванням Опис
    optimize boolean / undefined undefined Активує проходження оптимізації імпортів. undefined = активно лише під час production-збірок. false також вимикає purge та minify.
    minify boolean false Перейменовує ключі полів контенту у скомпільованих JSON-файлах на короткі алфавітні псевдоніми. Водночас змінює відповідні доступи до властивостей (property accesses) у вихідному коді. Не діятиме, якщо optimize дорівнює false.
    purge boolean false Вилучає зі скомпільованих JSON-файлів усі поля контенту, до яких немає жодного статичного звернення у вихідному коді. Не діятиме, якщо optimize дорівнює false.

    Мініфікація (перейменування ключів полів)

    Властивість build.minify не мініфікує ваш фінальний JS-бандл — це завдання виконує ваш bundler (збиральник). Вона зменшує розмір скомпільованих JSON-файлів словників шляхом заміни кожного визначеного користувачем ключа поля на короткий алфавітний псевдонім (alias):

    plaintext
    // До мініфікації{ "title": "Привіт", "subtitle": "Світ" }// Після мініфікації{ "a": "Привіт", "b": "Світ" }

    Така сама заміна застосовується до всіх звернень (property accesses) у вашому вихідному коді. Таким чином, у скомпільованому коді content.title стає content.a. Поведінка під час виконання (runtime) залишається незмінною.

    intlayer.config.ts
    import type { IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  build: {    minify: true,  },};export default config;
    Мініфікація пропускається (skipped), коли optimize становить false, або коли editor.enabled становить true (візуальний редактор потребує оригінальних імен полів для їх редагування).
    Мініфікація також пропускається для словників, що завантажуються з importMode: 'fetch', оскільки їхні JSON-дані постачаються з віддаленого API з оригінальними іменами полів — перейменування ключів на боці клієнта порушить зв'язок сервер/клієнт.

    Очищення (видалення невикористаних полів)

    Функція build.purge аналізує, до яких полів контенту ви реально звертаєтеся у своєму вихідному коді, та видаляє всі інші поля зі скомпільованих JSON-файлів.

    intlayer.config.ts
    import type { IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  build: {    purge: true,  },};export default config;

    Приклад: словник має п'ять полів, проте з них використовуються лише два:

    plaintext
    // Перед очищенням{ "title": "…", "subtitle": "…", "cta": "…", "footer": "…", "badge": "…" }// Після очищення (у коді зверталися лише до title + subtitle){ "title": "…", "subtitle": "…" }
    Очищення пропускається, коли optimize становить false, або коли editor.enabled становить true.
    Очищення також консервативно пропускається, коли вихідний файл не може бути розпарсений, або коли результат виклику useIntlayer присвоюється змінній і далі передається способами, які статичний аналізатор не може відстежити (наприклад, поширюється у вигляді spread-об'єкта чи передається як prop без деструктуризації). В таких випадках весь словник зберігається повністю.

    Режим імпорту (Import Mode)

    У великих програмах із великою кількістю сторінок і локалей JSON-файли можуть становити вагому частку від загального розміру бандлу. Intlayer дозволяє керувати способами завантаження словників за допомогою параметра importMode.

    Глобальне визначення

    Режим імпорту можна визначити глобально у файлі intlayer.config.ts.

    intlayer.config.ts
    import type { IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  dictionary: {    importMode: "dynamic", // За замовчуванням 'static'  },};export default config;

    Визначення для окремого словника

    Ви можете змінити (override) режим імпорту для окремих словників у їхніх файлах .content.{{ts|tsx|js|jsx|mjs|cjs|json|jsonc|json5|md|mdx|yaml|yml}}.

    ts
    import { type Dictionary, t } from "intlayer";const appContent: Dictionary = {  key: "app",  importMode: "dynamic", // Перевизначити режим імпорту  content: {    // ...  },};export default appContent;
    Властивість Тип Значення за замовчуванням Опис
    importMode 'static', 'dynamic', 'fetch' 'static' Застаріло (Deprecated): Використовуйте замість цього dictionary.importMode. Визначає, як завантажуються словники (див. нижче).

    Параметр importMode визначає, яким чином контент зі словника додаватиметься у ваш компонент. Його можна встановити глобально у файлі intlayer.config.ts в об'єкті dictionary або перевизначити для кожного окремого словника в його файлі .content.ts.

    1. Статичний режим (Static Mode - default)

    У статичному режимі Intlayer замінює виклик useIntlayer на useDictionary та інжектить словник безпосередньо у JS-бандл.

    • Плюси: Миттєвий рендеринг (синхронний режим), нуль додаткових мережевих запитів під час гідратації.
    • Мінуси: Бандл включає переклади для всіх доступних мов для конкретного компонента.
    • Найкраще підходить для: Односторінкових програм (SPA).

    Приклад трансформованого коду:

    tsx
    // Ваш кодconst content = useIntlayer("my-key");// Ілюстрація оптимізованого коду після трансформації (Static)// Це лише для наочності, фактичний код дещо відрізнятиметься з міркувань оптимізаціїconst content = useDictionary({  key: "my-key",  content: {    nodeType: "translation",    translation: {      en: "My title",      uk: "Мій заголовок",    },  },});

    2. Динамічний режим (Dynamic Mode)

    У динамічному режимі Intlayer замінює виклик useIntlayer на useDictionaryAsync. Він використовує import() (механізм, схожий на Suspense) для лінивого завантаження виключно того JSON, який потрібен для поточної локалі.

    • Плюси: Tree shaking на рівні локалі. Користувач, який переглядає англійську версію сторінки, завантажить тільки англійський словник. Український словник ніколи не завантажиться.
    • Мінуси: Викликає додатковий мережевий запит на компонент (fetching) під час гідратації.
    • Найкраще підходить для: Великих текстових блоків, статей або програм із підтримкою великої кількості мов, де розмір бандлу має критичне значення.

    Приклад трансформованого коду:

    tsx
    // Ваш кодconst content = useIntlayer("my-key");// Ілюстрація оптимізованого коду після трансформації (Dynamic)// Це лише для наочності, фактичний код дещо відрізнятиметься з міркувань оптимізаціїconst content = useDictionaryAsync({  en: () =>    import(".intlayer/dynamic_dictionary/my-key/en.json").then(      (mod) => mod.default    ),  uk: () =>    import(".intlayer/dynamic_dictionary/my-key/uk.json").then(      (mod) => mod.default    ),});
    Якщо використовується importMode: 'dynamic' і на сторінці є 100 компонентів, що застосовують useIntlayer, браузер намагатиметься ініціювати 100 окремих запитів (fetch). Аби уникнути цього "водоспаду" (waterfall) запитів, згрупуйте контент у меншу кількість .content файлів (наприклад, по одному словнику на секцію сторінки), замість створювати окремий словник для кожного атомного компонента. Ви також можете використовувати кілька .content файлів із однаковим ключем — Intlayer самостійно об'єднає їх в єдиний словник.

    3. Режим Fetch (Fetch Mode)

    Працює подібно до динамічного режиму, але спершу намагається завантажити словники за допомогою Intlayer Live Sync API. Якщо API-виклик не вдається здійснити або контент не позначено для живого оновлення, система повертається до динамічного імпорту.

    Приклад трансформованого коду:

    tsx
    // Ваш кодconst content = useIntlayer("my-key");// Оптимізований код (Fetch)const content = useDictionaryAsync({  en: () =>    fetch("https://intlayer.my-domain.com/dictionary/my-key/en").then((res) =>      res.json()    ),  uk: () =>    fetch("https://intlayer.my-domain.com/dictionary/my-key/uk").then((res) =>      res.json()    ),});
    Більше інформації можна знайти у документації до CMS: CMS
    У режимі fetch етапи очищення (purge) та мініфікації (minify) не застосовуються, адже JSON обслуговується віддаленим API, що використовує оригінальні імена полів.

    Підсумок: Статичний чи Динамічний?

    Характеристика Статичний режим (Static Mode) Динамічний режим (Dynamic Mode)
    Розмір JS-бандлу Більший (включає всі мови для цього компонента) Найменший (лише код, без контенту)
    Початкове завантаження Миттєве (Контент вже є у бандлі) Незначна затримка (завантаження JSON)
    Мережеві запити 0 додаткових запитів 1 запит на один ключ словника
    Tree Shaking На рівні компонента На рівні компонента + на рівні локалі
    Найкраще підходить для UI-компонентів, невеликих програм Насичених текстом сторінок, безлічі мов