Международная локализация (i18n) веб-приложений требует аккуратного подхода к управлению текстовым содержимым и форматированием данных, особенно когда приложение построено с использованием веб-компонентов и Lit. Lit обеспечивает декларативный подход к рендерингу, что создаёт уникальные возможности и ограничения при внедрении i18n.
import { LitElement, html } from 'lit';
import { property } from 'lit/decorators.js';
class GreetingComponent extends LitElement {
@property({ type: String }) greeting = 'Hello';
render() {
return html`<p>${this.greeting}, user!</p>`;
}
}
customElements.define('greeting-component', GreetingComponent);
Смена языка выполняется изменением свойства greeting.
Этот способ удобен для небольших компонентов с ограниченным набором
текстов.
// en.json
{
"greeting": "Hello",
"farewell": "Goodbye"
}
// ru.json
{
"greeting": "Привет",
"farewell": "До свидания"
}
Компонент может динамически загружать нужный файл:
import { LitElement, html } from 'lit';
class I18nComponent extends LitElement {
static properties = {
locale: {},
messages: {}
};
constructor() {
super();
this.locale = 'en';
this.messages = {};
this.loadMessages(this.locale);
}
async loadMessages(locale) {
const messages = await import(`./locales/${locale}.json`);
this.messages = messages.default;
}
render() {
return html`<p>${this.messages.greeting}</p>`;
}
}
customElements.define('i18n-component', I18nComponent);
Преимущество этого метода — централизованное хранение и возможность асинхронной подгрузки локализации.
Lit строит рендеринг на реактивных свойствах. Это позволяет легко
переключать язык без перезагрузки страницы. Для этого достаточно
обновить свойство locale и повторно загрузить строки
перевода:
this.locale = 'ru';
await this.loadMessages(this.locale);
this.requestUpdate();
Важный момент: requestUpdate() гарантирует, что
изменения свойства messages будут учтены в следующем
рендере.
Для корректного отображения числовых данных и дат лучше использовать
стандартный API Intl. Пример форматирования даты:
const date = new Date();
const formattedDate = new Intl.DateTimeFormat('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date);
Комбинируя это с реактивными свойствами Lit, можно строить динамические компоненты с локализованным выводом чисел, дат и валют:
html`<p>Сумма: ${new Intl.NumberFormat(this.locale, { style: 'currency', currency: 'RUB' }).format(12345.67)}</p>`
Часто необходимо использовать шаблонные строки с переменными, например: «Привет, {username}!». Для этого применяются функции-шаблоны:
function translate(messages, key, params = {}) {
let message = messages[key] || key;
Object.keys(params).forEach(k => {
message = message.replace(`{${k}}`, params[k]);
});
return message;
}
Использование в компоненте:
html`<p>${translate(this.messages, 'greetingUser', { username: 'Иван' })}</p>`
Такой подход позволяет централизованно управлять плейсхолдерами и избегать дублирования логики в компонентах.
В крупных приложениях полезно хранить текущую локаль в глобальном
состоянии, например через ReactiveController или глобальный
объект store. Компоненты Lit подписываются на изменения
локали и автоматически обновляют отображение:
class LocaleController {
listeners = [];
locale = 'en';
setLocale(newLocale) {
this.locale = newLocale;
this.listeners.forEach(fn => fn(this.locale));
}
subscribe(fn) {
this.listeners.push(fn);
fn(this.locale);
}
}
Компонент:
this.localeController.subscribe(locale => {
this.locale = locale;
this.loadMessages(locale);
});
Это обеспечивает единообразную локализацию на всех уровнях приложения.
Для крупных проектов критически важно подгружать только необходимые
переводы. Разделение JSON-файлов по компонентам и языкам, использование
динамического import() и кэширования сообщений уменьшает
нагрузку на клиент и ускоряет рендеринг.
html-шаблон позволяет легко вставлять
локализованные строки без сложной логики.Intl.Эти принципы позволяют создавать гибкие и масштабируемые веб-компоненты, полностью готовые к международной локализации, без потери преимуществ реактивного рендеринга Lit.