В i18next постпроцессоры представляют собой слой обработки уже переведённой строки перед её возвратом потребителю. Они позволяют модифицировать результат перевода, не вмешиваясь в логику выбора ключа, языка или ресурсов.
Постпроцессоры подключаются на уровне результата t() и
применяются последовательно, образуя цепочку трансформаций. Каждый из
них получает строку перевода и контекст выполнения, а затем возвращает
изменённое значение.
Ключевая особенность механизма — разделение ответственности:
При вызове t('key') i18next выполняет несколько
стадий:
На шаге постобработки формируется последовательность обработчиков, заданных либо глобально, либо в параметрах вызова.
i18next.t('welcome', {
postProcess: ['upperCase']
})
В этом случае строка проходит через постпроцессор
upperCase.
Постпроцессор регистрируется через API addPostProcessor.
Он должен реализовывать метод process.
import i18next from 'i18next';
const upperCasePostProcessor = {
type: 'postProcessor',
name: 'upperCase',
process(value, key, options, translator) {
return value.toUpperCase();
}
};
i18next
.use(upperCasePostProcessor)
.init({
resources: {
en: {
translation: {
hello: 'hello world'
}
}
}
});
Здесь process получает:
value — переведённая строкаkey — ключ переводаoptions — параметры t()translator — экземпляр переводчикаГлавная особенность механизма — возможность применять несколько постпроцессоров подряд.
i18next.t('price', {
postProcess: ['currencyFormat', 'round', 'appendSymbol']
});
В этом случае результат проходит последовательно:
currencyFormat — приводит число к локальному формату
валютыround — округляет значениеappendSymbol — добавляет символ валютыКаждый следующий постпроцессор работает уже с результатом предыдущего.
Порядок в массиве postProcess строго определяет цепочку
обработки. Перестановка элементов может радикально изменить
результат.
postProcess: ['trim', 'upperCase']
и
postProcess: ['upperCase', 'trim']
могут давать разные результаты в зависимости от реализации.
Например:
trim удаляет пробелыupperCase изменяет регистрЕсли сначала применить upperCase, а затем
trim, поведение может стать менее предсказуемым при наличии
локализационных пробелов или специальных символов.
Постпроцессоры часто используют options для адаптации
поведения.
const suffixPostProcessor = {
name: 'suffix',
type: 'postProcessor',
process(value, key, options) {
if (options.suffix) {
return `${value}${options.suffix}`;
}
return value;
}
};
Использование:
i18next.t('file', {
postProcess: ['suffix'],
suffix: '.txt'
});
Такой подход позволяет передавать динамическую логику прямо в цепочку обработки.
Постпроцессоры могут быть условными, изменяя поведение в зависимости от ключа перевода или контекста языка.
const pluralAwareProcessor = {
name: 'pluralAware',
type: 'postProcessor',
process(value, key, options) {
if (options.count === 1) {
return value;
}
return value + 's';
}
};
Хотя i18next уже имеет встроенную поддержку множественных форм, постпроцессоры применяются для нестандартных сценариев, например:
Постпроцессоры могут быть использованы как средство композиции трансформаций.
postProcess: [
'sanitize',
'markdownToHtml',
'highlightVariables'
]
Типичная цепочка в реальных приложениях:
Такой подход позволяет держать переводимые строки максимально «сырыми», а всю визуальную обработку переносить в постпроцессинг.
Хорошей практикой считается проектирование постпроцессоров как идемпотентных функций, то есть таких, которые при повторном применении не искажают результат.
Проблемные случаи:
process(value) {
if (value.endsWith('€')) return value;
return value + '€';
}
Интерполяция выполняется до постпроцессоров, поэтому постпроцессоры работают уже с готовыми значениями.
i18next.t('welcome_user', { name: 'Alex' });
Если перевод:
"welcome_user": "Hello {{name}}"
то постпроцессор получит уже строку:
Hello Alex
Это важно учитывать при проектировании цепочек, особенно если требуется анализ плейсхолдеров — их на этом этапе уже нет.
i18next допускает асинхронную обработку, но требует явного указания.
const asyncProcessor = {
name: 'asyncAppend',
type: 'postProcessor',
process(value, key, options, translator, cb) {
setTimeout(() => {
cb(null, value + '!');
}, 100);
}
};
Использование асинхронных цепочек требует осторожности:
t()Постпроцессоры часто используются для:
Форматирования данных
UI-трансформаций
Безопасности
Бизнес-логики
Постпроцессоры можно подключать глобально, чтобы они применялись ко всем переводам:
i18next.init({
postProcess: ['sanitize']
});
В этом случае каждый вызов t() проходит через указанный
слой обработки, если он не переопределён локально.
Типичные проблемы:
optionsДля диагностики часто добавляют логирующий постпроцессор:
const logger = {
name: 'logger',
type: 'postProcessor',
process(value, key, options) {
console.log('postProcess:', { value, key, options });
return value;
}
};
Он позволяет отслеживать прохождение строки через цепочку трансформаций.
Цепочки постпроцессоров в i18next фактически формируют композицию функций:
f3(f2(f1(value)))
где:
f1, f2, f3 —
постпроцессорыvalue — результат переводаТакая модель делает систему расширяемой без изменения ядра библиотеки и позволяет внедрять произвольные трансформации на уровне приложения.