Управление переводами

Preact, как легковесная альтернатива React, не включает встроенной поддержки интернационализации, однако архитектура фреймворка позволяет гибко интегрировать решения для управления переводами. Основной подход заключается в создании контекста для хранения текущего языка и словаря переводов, а также в использовании хуков для динамического получения локализованных строк в компонентах.

Создание контекста переводов

Для централизованного управления языковыми ресурсами удобно использовать Preact.createContext. Контекст позволяет любому компоненту получать доступ к текущему языку и функции переключения без необходимости пробрасывать пропсы на каждом уровне.

import { createContext } from 'preact';
import { useState } from 'preact/hooks';

const TranslationContext = createContext({
  language: 'en',
  translations: {},
  setLanguage: () => {}
});

export const TranslationProvider = ({ children, defaultLanguage = 'en', dictionaries }) => {
  const [language, setLanguage] = useState(defaultLanguage);

  const value = {
    language,
    translations: dictionaries[language] || {},
    setLanguage
  };

  return (
    <TranslationContext.Provider value={value}>
      {children}
    </TranslationContext.Provider>
  );
};

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

  • dictionaries — объект, где ключи — языковые коды ('en', 'ru'), а значения — словари.
  • setLanguage позволяет динамически менять текущий язык приложения.

Хук для получения перевода

Для удобства можно создать кастомный хук useTranslation, который будет возвращать функцию для поиска перевода по ключу:

import { useContext } from 'preact/hooks';

export const useTranslation = () => {
  const { translations } = useContext(TranslationContext);

  const t = (key, fallback = '') => translations[key] || fallback;

  return { t };
};

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

  • Функция t принимает ключ перевода и опциональный fallback.
  • Позволяет компонентации оставаться чистой и минимальной, не зависящей от контекста напрямую.

Динамическая смена языка

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

import { useContext } from 'preact/hooks';
import { TranslationContext } from './TranslationProvider';

const LanguageSwitcher = () => {
  const { setLanguage } = useContext(TranslationContext);

  return (
    <div>
      <button onCl ick={() => setLanguage('en')}>English</button>
      <button onCl ick={() => setLanguage('ru')}>Русский</button>
    </div>
  );
};

Эффект:

  • Изменение языка приводит к перерендеру всех компонентов, использующих useTranslation.
  • Возможна интеграция с локальным хранилищем или cookie для сохранения выбранного языка между сессиями.

Организация словарей

Словари переводов удобно хранить в виде отдельных модулей:

export const en = {
  welcome: "Welcome",
  login: "Login",
  logout: "Logout"
};

export const ru = {
  welcome: "Добро пожаловать",
  login: "Войти",
  logout: "Выйти"
};

Рекомендации по структуре:

  • Ключи должны быть семантически понятными и не зависеть от конкретного текста.
  • Возможна вложенная структура для группировки по модулям (auth.login, menu.home), с соответствующей обработкой в функции t.

Интеграция с компонентами

Использование перевода в компонентах выглядит лаконично:

import { useTranslation } from './useTranslation';

const Header = () => {
  const { t } = useTranslation();

  return (
    <header>
      <h1>{t('welcome')}</h1>
      <nav>
        <a href="/login">{t('login')}</a>
        <a href="/logout">{t('logout')}</a>
      </nav>
    </header>
  );
};

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

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

Ленивая загрузка переводов

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

const loadTranslations = async (lang) => {
  const module = await import(`./locales/${lang}.js`);
  return module.default;
};
  • Позволяет загружать только необходимые языковые ресурсы.
  • В сочетании с Suspense можно показывать индикатор загрузки при смене языка.

Поддержка множества форматов

Базовый подход с объектами подходит для простых приложений, но для сложных случаев можно использовать библиотеку i18next или formatjs. В Preact они интегрируются аналогично, используя контекст и хуки, сохраняя преимущества реактивного обновления при смене языка.

  • i18next поддерживает интерполяцию переменных и вложенные ключи.
  • formatjs обеспечивает поддержку форматирования дат, чисел и валют в зависимости от локали.

Ключевые практики

  • Все строки интерфейса выносить в словари, избегая “хардкода” текста в компонентах.
  • Использовать семантические ключи для легкой навигации по словарям.
  • Централизованно управлять текущим языком через контекст.
  • Реализовать fallback на дефолтный язык для отсутствующих переводов.
  • Рассмотреть ленивая загрузка и CDN для масштабных проектов с множеством языков.

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