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

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


Управление состоянием локали

Для динамической смены языка необходимо централизованно хранить текущее значение локали. В SkateJS рекомендуется использовать реактивные свойства компонента. Пример декларации:

import { Component, h, prop } from 'skatejs';

class I18nComponent extends Component {
  static props = {
    locale: prop.string({
      attribute: true,
      default: 'en',
      reflect: true
    })
  };

  render() {
    return (
      <div>{this.translate('greeting')}</div>
    );
  }

  translate(key) {
    return I18nComponent.messages[this.locale][key] || key;
  }
}

I18nComponent.messages = {
  en: {
    greeting: 'Hello!'
  },
  ru: {
    greeting: 'Привет!'
  },
  es: {
    greeting: '¡Hola!'
  }
};

customElements.define('i18n-component', I18nComponent);

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

  • prop.string() создаёт реактивное свойство locale, которое автоматически обновляет компонент при изменении.
  • Использование словаря messages позволяет хранить переводы в структуре { locale: { key: value } }.
  • Метод translate обращается к текущей локали и возвращает соответствующий текст, или ключ, если перевод отсутствует.

Реактивная смена языка

SkateJS автоматически перерисовывает компонент при изменении реактивного свойства. Динамическая смена локали осуществляется простым присвоением нового значения:

const component = document.querySelector('i18n-component');

// Смена языка на русский
component.locale = 'ru';

// Смена языка на испанский
component.locale = 'es';

При этом render() вызывается автоматически, и отображаемый текст обновляется без необходимости ручного вмешательства.


Поддержка вложенных компонентов

Если приложение содержит вложенные компоненты, которые также должны реагировать на смену языка, рекомендуется использовать общий объект состояния локали или события CustomEvent.

Вариант с глобальным состоянием:

export const LocaleState = {
  current: 'en',
  listeners: new Set(),
  set(locale) {
    this.current = locale;
    this.listeners.forEach(cb => cb(locale));
  },
  subscribe(cb) {
    this.listeners.add(cb);
    return () => this.listeners.delete(cb);
  }
};

Компонент подписывается на изменения:

class NestedComponent extends Component {
  connectedCallback() {
    super.connectedCallback();
    this.unsubscribe = LocaleState.subscribe(locale => {
      this.locale = locale;
    });
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    this.unsubscribe();
  }

  static props = {
    locale: prop.string()
  };

  render() {
    return <span>{I18nComponent.messages[this.locale].greeting}</span>;
  }
}

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

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

async function loadMessages(locale) {
  const messagesModule = await import(`./locales/${locale}.js`);
  I18nComponent.messages[locale] = messagesModule.default;
}

После загрузки словаря можно обновить локаль:

loadMessages('fr').then(() => {
  component.locale = 'fr';
});

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

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

Обработка форматов и дат

Для полного i18n-решения необходимо учитывать не только текстовые строки, но и форматы чисел, дат и валют. SkateJS не ограничивает разработчика в использовании стандартного API Jav * aScript:

function formatDate(date, locale) {
  return new Intl.DateTimeFormat(locale, { dateStyle: 'long' }).format(date);
}

function formatNumber(value, locale) {
  return new Intl.NumberFormat(locale).format(value);
}

Пример использования в компоненте:

render() {
  const today = new Date();
  return (
    <div>
      <p>{this.translate('greeting')}</p>
      <p>{formatDate(today, this.locale)}</p>
      <p>{formatNumber(1234567.89, this.locale)}</p>
    </div>
  );
}

Интеграция с JSX и слотами

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

render() {
  return (
    <section>
      <h1>{this.translate('title')}</h1>
      <slot name="description"></slot>
    </section>
  );
}

Слоты могут быть динамически обновлены при смене локали, если передавать в них компоненты с реактивным свойством locale.


Итоги по реализации

  • Реактивные свойства (prop) обеспечивают автоматическое обновление компонента при смене языка.
  • Централизованное состояние или события помогают синхронизировать локаль между вложенными компонентами.
  • Динамическая загрузка словарей позволяет оптимизировать производительность.
  • Стандартные API Intl покрывают локализацию чисел, валют и дат.
  • JSX и слоты дают гибкость при интеграции локализованного контента в структуру DOM.

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