Многоязычность (i18n) в Mithril требует организации текстового контента и управления переводами без нарушения реактивной природы компонентов. Основной принцип заключается в том, чтобы текстовые данные не захардкоживались в компонентах, а подгружались из внешних словарей или файлов перевода.
Структура словаря переводов обычно представляет собой объект, где ключи — идентификаторы текстов, а значения — переводы для конкретного языка:
const translations = {
en: {
greeting: "Hello",
farewell: "Goodbye",
welcomeMessage: "Welcome to our application"
},
ru: {
greeting: "Привет",
farewell: "До свидания",
welcomeMessage: "Добро пожаловать в наше приложение"
}
};
Ключи должны быть консистентны и описательны, чтобы облегчить поддержку больших проектов.
Необходим механизм хранения и смены текущего языка. В Mithril это удобно реализовать через глобальное состояние или сервис, который можно импортировать в компоненты:
const i18n = {
currentLang: "en",
setLang(lang) {
this.currentLang = lang;
m.redraw(); // Перерисовка всех компонентов при смене языка
},
t(key) {
return translations[this.currentLang][key] || key;
}
};
Функция t возвращает перевод по ключу или сам ключ, если
перевода нет, что предотвращает ошибки при недостающих строках.
В Mithril компоненты описываются через функцию view.
Переводы интегрируются напрямую в шаблон с помощью вызова функции
t:
const WelcomeComponent = {
view() {
return m("div", [
m("h1", i18n.t("greeting")),
m("p", i18n.t("welcomeMessage")),
m("button", { onclick: () => i18n.setLang("ru") }, "RU"),
m("button", { onclick: () => i18n.setLang("en") }, "EN")
]);
}
};
Особенности перерисовки: Mithril автоматически
реагирует на изменение состояния, но необходимо вручную вызвать
m.redraw() при изменении языка, если смена происходит вне
событий, связанных с компонентами.
Для крупных проектов словари лучше разделять на отдельные файлы по языкам:
/i18n
en.js
ru.js
Пример en.js:
export default {
greeting: "Hello",
farewell: "Goodbye",
welcomeMessage: "Welcome to our application"
};
Загрузка осуществляется через импорт:
import en from "./i18n/en.js";
import ru from "./i18n/ru.js";
const translations = { en, ru };
Такой подход упрощает поддержку и позволяет подключать переводы динамически при необходимости.
Часто тексты содержат переменные, например имя пользователя или число элементов. Для этого используют шаблоны с подстановкой:
const en = {
greetingUser: "Hello, {name}!"
};
function t(key, params = {}) {
let str = translations[i18n.currentLang][key] || key;
for (const k in params) {
str = str.replace(`{${k}}`, params[k]);
}
return str;
}
// Использование
m("p", t("greetingUser", { name: "Alice" }));
Поддержка параметров позволяет создавать универсальные переводы, которые не зависят от конкретного контекста.
Для более сложных задач можно использовать сторонние библиотеки, например i18next. Mithril позволяет интегрировать их с минимальными усилиями:
import i18next from "i18next";
i18next.init({
lng: "en",
resources: {
en: { translation: { greeting: "Hello" } },
ru: { translation: { greeting: "Привет" } }
}
});
const WelcomeComponent = {
view() {
return m("div", i18next.t("greeting"));
}
};
Подключение i18next упрощает работу с форматированием дат, чисел и поддержкой множественного числа.
Если приложение использует вложенные компоненты, важно передавать контекст языка через сервис или props. Пример с сервисом удобен, так как компоненты обращаются к нему напрямую, и изменение языка автоматически вызывает перерисовку всех дочерних элементов.
Переводы текста — только часть задачи. Для полного мультиязычного опыта необходимо учитывать:
Intl.DateTimeFormat)Intl.NumberFormat)Пример форматирования даты:
const date = new Date();
const formatted = new Intl.DateTimeFormat(i18n.currentLang, { dateStyle: "long" }).format(date);
m("p", formatted);
Использование Intl обеспечивает корректное отображение
данных для любого языка без сторонних библиотек.
Некоторые языки имеют сложные правила множественного числа. Для базового решения можно использовать функции-валидаторы:
function pluralize(key, count) {
const base = translations[i18n.currentLang][key] || key;
return count === 1 ? base.singular : base.plural;
}
// Пример словаря
const en = {
item: { singular: "item", plural: "items" }
};
Для сложных правил лучше использовать готовые библиотеки типа i18next, которые учитывают правила конкретного языка.
При динамической смене языка важно:
m.redraw())i18n.setLang = function(lang) {
this.currentLang = lang;
localStorage.setItem("lang", lang);
m.redraw();
};
Загрузка языка из localStorage позволяет сохранять предпочтения между сессиями.
t для любых текстовых
элементовIntlТакой подход позволяет создавать масштабируемые Mithril-приложения с полной поддержкой многоязычности, минимизируя дублирование кода и обеспечивая гибкость при добавлении новых языков.