Knockout.js — это легковесный JavaScript-фреймворк, реализующий паттерн Model-View-ViewModel (MVVM). Его основная цель — обеспечение автоматической синхронизации между моделью данных и пользовательским интерфейсом. Это достигается с помощью обсерваблов, вычисляемых значений и привязок данных.
Обсерваблы (observable) представляют собой объекты, отслеживающие свои изменения. Присваивание нового значения автоматически уведомляет все связанные элементы интерфейса:
var firstName = ko.observable('Иван');
firstName('Пётр'); // обновляет значение и уведомляет подписчиков
Вычисляемые значения (computed) позволяют создавать динамические свойства, зависящие от других обсерваблов. Они автоматически пересчитываются при изменении зависимостей:
var fullName = ko.computed(function() {
return firstName() + ' Иванов';
});
Привязки данных (data-bind) обеспечивают автоматическую синхронизацию между HTML и ViewModel:
<input data-bind="value: firstName" />
<p data-bind="text: fullName"></p>
Knockout.js не имеет встроенной системы управления переводами, но его реактивная модель идеально подходит для интеграции с внешними библиотеками локализации, такими как i18next, Globalize.js или собственные решения на основе JSON.
Файлы с переводами обычно хранятся в формате JSON:
{
"ru": {
"greeting": "Привет, {name}!",
"button_save": "Сохранить"
},
"en": {
"greeting": "Hello, {name}!",
"button_save": "Save"
}
}
Создаётся обсервабл для текущего языка, а также функция для получения перевода по ключу:
var currentLang = ko.observable('ru');
var translations = {
ru: { greeting: "Привет, {name}!", button_save: "Сохранить" },
en: { greeting: "Hello, {name}!", button_save: "Save" }
};
function t(key, params) {
var text = translations[currentLang()][key] || key;
if (params) {
Object.keys(params).forEach(function(k) {
text = text.replace('{' + k + '}', params[k]);
});
}
return text;
}
var greetingText = ko.computed(function() {
return t('greeting', { name: 'Иван' });
});
Привязка в HTML выполняется через text или
html:
<p data-bind="text: greetingText"></p>
<button data-bind="text: t('button_save')"></button>
Благодаря реактивности обсерваблов, при изменении значения
currentLang все вычисляемые тексты обновляются
автоматически:
currentLang('en'); // Переведёт интерфейс на английский
Для комплексных интерфейсов с множеством динамических компонентов удобно использовать привязку к обсерваблу с функцией перевода. Это позволяет централизованно управлять всеми текстами:
ko.bindingHandlers.i18nText = {
update: function(element, valueAccessor) {
var key = ko.unwrap(valueAccessor());
element.textContent = t(key);
}
};
Использование в разметке:
<span data-bind="i18nText: 'button_save'"></span>
Такой подход упрощает поддержку и масштабирование интерфейсов с несколькими языками.
Переводы часто содержат переменные и динамические значения. Для этого вычисляемые функции и обсерваблы позволяют применять форматирование и подстановку значений в реальном времени:
var userName = ko.observable('Мария');
var greetingDynamic = ko.computed(function() {
return t('greeting', { name: userName() });
});
В HTML это отразится автоматически:
<p data-bind="text: greetingDynamic"></p>
Любое изменение userName() мгновенно обновит текст на
странице без дополнительного кода.
Knockout.js легко сочетается с библиотеками вроде i18next, которые поддерживают асинхронную загрузку языковых файлов, fallback-языки и сложные структуры перевода:
var currentLang = ko.observable('ru');
currentLang.subscribe(function(lang) {
i18next.changeLanguage(lang, function() {
ko.tasks.runEarly(); // Обновление вычисляемых обсерваблов
});
});
var greetingText = ko.computed(function() {
return i18next.t('greeting', { name: 'Иван' });
});
Knockout.js поддерживает шаблоны через
template binding. Это позволяет создавать отдельные шаблоны
для разных языков или использовать обсерваблы внутри шаблонов:
<!-- ko template: { name: 'greetingTemplate' } --><!-- /ko -->
<script type="text/html" id="greetingTemplate">
<p data-bind="text: greetingText"></p>
</script>
Таким образом, интерфейс остаётся реактивным, независимо от сложности шаблонов и количества языков.
При больших интерфейсах с множеством переводов важно минимизировать количество вычисляемых обсерваблов и подписок. Для этого используют:
Эти практики обеспечивают быструю реакцию интерфейса и упрощают поддержку многоязычных приложений.