Ленивая загрузка локалей

Atomico — современная библиотека для построения веб-компонентов с реактивной моделью, где особое внимание уделено производительности и модульности. Одной из ключевых задач при разработке мультиязычных приложений является эффективное управление локалями. Ленивая загрузка локалей позволяет загружать языковые пакеты по мере необходимости, снижая первоначальный размер бандла и ускоряя загрузку страницы.


Принципы ленивой загрузки

Ленивая загрузка локалей строится на следующих принципах:

  • Асинхронная загрузка: языковые данные подгружаются только когда требуется переключение на определённый язык.
  • Минимизация исходного бандла: основной скрипт приложения содержит только минимальный набор локалей (например, язык по умолчанию).
  • Кэширование: после первой загрузки локаль сохраняется в памяти для повторного использования без дополнительных запросов.

Эти принципы позволяют создавать приложения, где поддержка большого числа языков не влияет на скорость первичной загрузки.


Структура проекта с ленивыми локалями

Пример структуры папок для мультиязычного приложения:

src/
├─ locales/
│  ├─ en.js
│  ├─ ru.js
│  └─ es.js
├─ components/
│  └─ MyComponent.js
└─ index.js

Каждый файл локали экспортирует объект с переводами:

// locales/ru.js
export default {
    greeting: "Привет",
    farewell: "До свидания",
    button: {
        save: "Сохранить",
        cancel: "Отмена"
    }
};
// locales/en.js
export default {
    greeting: "Hello",
    farewell: "Goodbye",
    button: {
        save: "Save",
        cancel: "Cancel"
    }
};

Загрузка локалей по требованию

Atomico поддерживает динамический импорт модулей, что позволяет реализовать ленивую загрузку:

import { createSignal } from "atomico";

const [locale, setLocale] = createSignal({});

async function loadLocale(lang) {
    switch(lang) {
        case "ru":
            setLocale(await import("../locales/ru.js").then(mod => mod.default));
            break;
        case "en":
            setLocale(await import("../locales/en.js").then(mod => mod.default));
            break;
        default:
            setLocale(await import("../locales/en.js").then(mod => mod.default));
    }
}

В этом примере при вызове loadLocale("ru") модуль ru.js загружается асинхронно. Если язык уже был загружен, его объект может храниться в памяти, чтобы избежать повторных запросов.


Реактивное использование локалей в компонентах

Компоненты Atomico могут использовать сигнал локали для отображения текста:

import { c, useSignal } from "atomico";

function MyComponent() {
    const locale = useSignal({});

    return (
        <host>
            <h1>{locale.greeting}</h1>
            <button>{locale.button?.save}</button>
        </host>
    );
}

customElements.define("my-component", c(MyComponent));

После загрузки локали с помощью loadLocale("ru") реактивный сигнал автоматически обновит текст внутри компонента без перезагрузки страницы.


Динамическое переключение языков

Для поддержки переключения языков создаётся функция, которая меняет сигнал локали и вызывает повторный рендер:

const [currentLang, setCurrentLang] = createSignal("en");

async function switchLanguage(lang) {
    await loadLocale(lang);
    setCurrentLang(lang);
}

Использование:

<button onCl ick={() => switchLanguage("ru")}>Русский</button>
<button onCl ick={() => switchLanguage("en")}>English</button>

После нажатия кнопки компонент автоматически отображает переведённый текст.


Оптимизация и кэширование

Для уменьшения количества сетевых запросов можно хранить загруженные локали в объекте:

const localeCache = {};

async function loadLocale(lang) {
    if (localeCache[lang]) {
        return localeCache[lang];
    }
    const mod = await import(`../locales/${lang}.js`);
    localeCache[lang] = mod.default;
    return localeCache[lang];
}

Это позволяет:

  • Избежать повторных загрузок одного и того же языка.
  • Обеспечить мгновенное переключение между ранее загруженными языками.

Применение с Atomico Hooks

Atomico предоставляет удобные хуки для работы с сигналами и асинхронными данными:

import { useEffect, useSignal } from "atomico";

function useLocale(lang) {
    const locale = useSignal({});

    useEffect(async () => {
        locale.value = await loadLocale(lang);
    }, [lang]);

    return locale;
}

Компонент можно переписать с использованием этого хука:

function MyComponent({ lang }) {
    const locale = useLocale(lang);

    return (
        <host>
            <h1>{locale.greeting}</h1>
            <button>{locale.button?.save}</button>
        </host>
    );
}

Теперь любое изменение свойства lang автоматически вызывает загрузку соответствующей локали и обновление интерфейса.


Поддержка большого числа языков

Для приложений с десятками локалей рекомендуется:

  • Разделять локали на отдельные чанки, чтобы не загружать их все сразу.
  • Использовать динамический импорт с шаблонными строками, как в import(\../locales/${lang}.js).
  • Применять стратегию кэширования, чтобы повторная загрузка локали не происходила.
  • Минимизировать объём объектов локали, оставляя только необходимые ключи для текущего интерфейса.

Ленивая загрузка локалей в Atomico позволяет построить высокопроизводительные мультиязычные приложения, где текст интерфейса подгружается по требованию, сохраняя отзывчивость и минимальный размер бандла. Подход хорошо интегрируется с реактивной моделью сигналов и хуками Atomico, обеспечивая удобное и эффективное управление языками.