Интеграция i18n-библиотек

Slim.js — легковесный фреймворк для создания веб-компонентов, предоставляющий реактивное связывание данных и удобное управление состоянием компонентов. В современных приложениях важным аспектом является интернационализация (i18n) — возможность отображать интерфейс на разных языках. Интеграция i18n-библиотек в Slim.js требует понимания жизненного цикла компонентов и реактивности свойств.


Подключение i18n-библиотеки

Для работы с i18n чаще всего используют библиотеки вроде i18next, Polyglot.js или messageformat.js. Подключение осуществляется стандартным образом через npm:

npm install i18next

После установки библиотеку нужно инициализировать перед использованием в компонентах:

import i18next from 'i18next';

i18next.init({
  lng: 'en',
  resources: {
    en: {
      translation: {
        greeting: "Hello, {{name}}!"
      }
    },
    ru: {
      translation: {
        greeting: "Привет, {{name}}!"
      }
    }
  }
});

Инициализация создаёт глобальный объект, через который можно получать локализованные строки.


Использование i18n в свойствах Slim.js

Slim.js позволяет создавать реактивные свойства компонентов с помощью декораторов @prop или объявления через массив observedAttributes. Для интеграции i18n необходимо привязать отображаемые значения к локализованным строкам:

import { Slim, html } from 'slim-js';

class GreetingComponent extends Slim {
  static get observedAttributes() { return ['name', 'lang']; }

  onBeforeCreated() {
    this.name = this.name || 'User';
    this.lang = this.lang || 'en';
  }

  get greeting() {
    return i18next.getFixedT(this.lang)('greeting', { name: this.name });
  }

  render() {
    return html`<p>${this.greeting}</p>`;
  }
}

customElements.define('greeting-component', GreetingComponent);

Ключевой момент: свойство lang реагирует на изменения, что позволяет динамически обновлять язык компонента без перезагрузки страницы.


Реактивное обновление локализации

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

attributeChangedCallback(attrName, oldVal, newVal) {
  if (attrName === 'lang' && oldVal !== newVal) {
    this.update();
  }
}

Метод update() инициирует перерисовку компонента с новыми локализованными строками.


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

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

// Родительский компонент
class AppRoot extends Slim {
  static get observedAttributes() { return ['lang']; }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (attrName === 'lang') {
      this.querySelectorAll('child-component').forEach(child => {
        child.lang = newVal;
      });
    }
  }

  render() {
    return html`
      <child-component name="Alice" lang="${this.lang}"></child-component>
      <child-component name="Bob" lang="${this.lang}"></child-component>
    `;
  }
}

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


Использование шаблонов и плейсхолдеров

Библиотеки i18n поддерживают плейсхолдеры, что позволяет внедрять динамические значения в переводы. В Slim.js это удобно делать через вычисляемые свойства:

get farewell() {
  return i18next.getFixedT(this.lang)('farewell', { name: this.name, date: new Date().toLocaleDateString(this.lang) });
}

Динамическая подстановка значений работает напрямую с реактивной системой Slim.js, обеспечивая синхронизацию интерфейса с данными.


Поддержка асинхронной загрузки переводов

Для крупных приложений часто требуется ленивая загрузка языковых файлов. i18next поддерживает загрузку ресурсов через XHR или fetch. В Slim.js можно использовать асинхронную инициализацию внутри жизненного цикла компонента:

async onBeforeCreated() {
  await i18next.init({
    lng: this.lang,
    backend: {
      loadPath: '/locales/{{lng}}.json'
    }
  });
}

После загрузки переводов можно вызвать this.update() для обновления интерфейса.


Рекомендации по структуре i18n в Slim.js

  1. Централизованное хранение переводов — все ресурсы лучше хранить в одном месте и загружать динамически по необходимости.
  2. Использование реактивных свойств — язык и другие параметры локализации должны быть наблюдаемыми.
  3. Масштабируемость — для многоязычных приложений важно, чтобы изменения языка в корневом компоненте автоматически отражались на всех дочерних.
  4. Интеграция с шаблонами — плейсхолдеры и вычисляемые свойства облегчают работу с динамическим контентом.
  5. Асинхронная загрузка — критично для производительности при большом объёме языковых файлов.

Пример комплексной интеграции

class Dashboard extends Slim {
  static get observedAttributes() { return ['lang']; }

  async onBeforeCreated() {
    await i18next.init({
      lng: this.lang,
      backend: { loadPath: `/locales/{{lng}}.json` }
    });
    this.update();
  }

  get title() {
    return i18next.getFixedT(this.lang)('dashboard.title');
  }

  get welcomeMessage() {
    return i18next.getFixedT(this.lang)('dashboard.welcome', { user: this.userName });
  }

  render() {
    return html`
      <h1>${this.title}</h1>
      <p>${this.welcomeMessage}</p>
    `;
  }
}

customElements.define('dashboard-component', Dashboard);

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