Динамическая локализация

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


Основы реактивного состояния

В Atomico реактивность строится вокруг функции useState. Она позволяет компоненту автоматически обновляться при изменении состояния. Для динамической локализации важно хранить текущий язык и словарь фраз в реактивном состоянии:

import { component, useState } from "atomico";

const i18nData = {
    en: { greeting: "Hello", farewell: "Goodbye" },
    ru: { greeting: "Привет", farewell: "До свидания" },
};

export const MyComponent = component(() => {
    const [lang, setLang] = useState("en");

    const t = key => i18nData[lang][key] || key;

    return (
        <host>
            <p>{t("greeting")}</p>
            <button oncl ick={() => setLang(lang === "en" ? "ru" : "en")}>
                Switch Language
            </button>
        </host>
    );
});

Ключевые моменты:

  • useState создаёт реактивное состояние lang.
  • Функция t(key) работает как простой переводчик.
  • Любое изменение lang автоматически перерисует компонент.

Создание централизованного контекста локализации

Для масштабных приложений удобно вынести локализацию в отдельный контекст. Atomico поддерживает createContext и useContext, что позволяет передавать данные языковых настроек глубоко по дереву компонентов без проп-дриллинга.

import { createContext, useContext } from "atomico";

export const I18nContext = createContext({
    lang: "en",
    setLang: () => {},
    t: key => key
});

// Провайдер
export const I18nProvider = component(({ children }) => {
    const [lang, setLang] = useState("en");

    const t = key => i18nData[lang][key] || key;

    return (
        <I18nContext.Provider value={{ lang, setLang, t }}>
            {children}
        </I18nContext.Provider>
    );
});

// Использование в компоненте
export const Greeting = component(() => {
    const { t, setLang } = useContext(I18nContext);

    return (
        <host>
            <p>{t("greeting")}</p>
            <button oncl ick={() => setLang("ru")}>RU</button>
            <button oncl ick={() => setLang("en")}>EN</button>
        </host>
    );
});

Преимущества такого подхода:

  • Централизованное управление языком.
  • Легкая интеграция с любым компонентом через useContext.
  • Минимизация дублирования кода.

Реактивное обновление словаря

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

import { useEffect } from "atomico";

export const MyComponent = component(() => {
    const [lang, setLang] = useState("en");
    const [dict, setDict] = useState({});

    useEffect(async () => {
        const data = await fetch(`/locales/${lang}.json`).then(r => r.json());
        setDict(data);
    }, [lang]);

    const t = key => dict[key] || key;

    return (
        <host>
            <p>{t("greeting")}</p>
            <button oncl ick={() => setLang("ru")}>RU</button>
            <button oncl ick={() => setLang("en")}>EN</button>
        </host>
    );
});

Особенности реализации:

  • useEffect с зависимостью [lang] позволяет подгружать словарь только при смене языка.
  • setDict обновляет реактивное состояние, инициируя перерисовку.
  • Позволяет хранить переводы вне основной сборки, экономя память.

Ленивая локализация и оптимизация

Для крупного проекта стоит разделять словари по модульным файлам и загружать их лениво. Это снижает нагрузку на сеть и ускоряет первоначальный рендер. Atomico хорошо сочетается с динамическими импортами:

useEffect(() => {
    import(`./locales/${lang}.js`).then(module => setDict(module.default));
}, [lang]);

Преимущества:

  • Минимизируется основной бандл.
  • Поддержка любых языков без перегрузки страницы.
  • Возможность расширения приложения без изменения существующих компонентов.

Интернационализированные шаблоны

Иногда нужно не просто вставить текст, а форматировать строки с переменными. Для этого можно использовать функцию-шаблонизатор:

const format = (template, vars) =>
    template.replace(/\{(\w+)\}/g, (_, key) => vars[key] || "");

const t = key => format(dict[key] || key, { name: "Alex" });

<p>{t("welcome_user")}</p> // "Добро пожаловать, Alex"

Особенности:

  • Поддержка параметров в фразах.
  • Полная реактивность при смене языка или данных.

Совместимость с кастомными элементами

Atomico строит компоненты как Web Components. Это значит, что динамическая локализация работает не только внутри React-подобных компонентов, но и в любых HTML-страницах. Можно использовать атрибуты для передачи языка:

<MyComponent lang="ru"></MyComponent>

Внутри компонента можно реагировать на изменение атрибута с помощью useProp:

const [lang] = useProp("lang");

Это позволяет интегрировать локализацию с существующими системами, не меняя архитектуру приложения.


Итоговые принципы динамической локализации в Atomico

  1. Реактивность состояния — основа смены языка на лету.
  2. Контекст для глобального управления — упрощает работу с деревом компонентов.
  3. Асинхронная подгрузка словарей — уменьшает размер бандла.
  4. Ленивая загрузка и динамические импорты — повышают производительность.
  5. Поддержка шаблонов и параметров — делает тексты гибкими.
  6. Совместимость с Web Components — интеграция с любыми проектами.

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