Проблемы локализации проявляются как неверные словоформы, отсутствующие переводы, неправильное склонение и непредсказуемое поведение при работе с нестандартными языками.
import { format } from 'timeago.js';
format(new Date(), 'uk'); // Результат на английском — fallback на дефолтную локаль
Если локаль не зарегистрирована, timeago.js возвращает результат с дефолтным форматированием без предупреждения.
Решение:
import { register } from 'timeago.js';
import uk from 'timeago.js/esm/lang/uk';
register('uk', uk);
format(new Date(), 'uk'); // Теперь на украинском
register('custom', (n, i) => {
// Функция возвращает undefined для i >= 8
const forms = [
['только что', 'сейчас'],
// ... только 8 пар
];
return forms[i]; // undefined для i = 8..14
});
Симптом: undefined,
[object Object] или undefined years ago в
интерфейсе.
Решение:
register('custom', (n, i) => {
const forms = [...]; // 15 пар
return forms[i] ?? ['давно', 'скоро']; // Защита от выхода за пределы
});
register('ru_custom', (n, i) => {
// Неверно — не учитывает n
return ['минуту назад', 'через минуту'];
});
Для n = 5 должно быть “5 минут назад”, для
n = 2 — “2 минуты назад”.
Правильное склонение:
function pluralRu(n, one, few, many) {
const mod10 = n % 10;
const mod100 = n % 100;
if (mod100 >= 11 && mod100 <= 19) return many;
if (mod10 === 1) return one;
if (mod10 >= 2 && mod10 <= 4) return few;
return many;
}
register('ru_custom', (n, i) => {
switch (i) {
case 5: // 2-44 минуты
return [
`%s ${pluralRu(n, 'минуту', 'минуты', 'минут')} назад`,
`через %s ${pluralRu(n, 'минуту', 'минуты', 'минут')}`,
];
// ...
}
});
// Пользователь видит "%s минут назад" вместо "5 минут назад"
register('broken', (n, i) => {
return ['%s минут назад', 'через %s минут']; // Всегда возвращает одну пару
});
%s заменяется на n — количество единиц. Но
нужно правильно использовать его в шаблоне:
// Проверка — функция вызывается с конкретным n
// При n=5, i=5: format вернёт '5 минут назад'
register('correct', (n, i) => {
return [`%s ${pluralRu(n, 'минуту', 'минуты', 'минут')} назад`, '...'];
});
// Локаль 'ru' зарегистрирована дважды — второй вызов перезаписывает первый
register('ru', customLocale1);
register('ru', customLocale2); // Молчаливая перезапись
Решение — проверка перед регистрацией:
const REGISTERED = new Set();
function safeRegister(locale, fn) {
if (REGISTERED.has(locale)) {
console.warn(`Locale "${locale}" is already registered. Skipping.`);
return;
}
register(locale, fn);
REGISTERED.add(locale);
}
// Импорт из cjs вместо esm — разные форматы
import ru from 'timeago.js/esm/lang/ru'; // Правильно для ESM
const ru = require('timeago.js/lib/lang/ru'); // Для CommonJS
Арабский и иврит — правосторонние языки. Текст форматируется правильно, но CSS-макет не адаптирован:
format(date, 'ar'); // Строка корректна на арабском
// Но элемент без dir="rtl" отображается некорректно
Решение:
<time datetime="..." dir="rtl" lang="ar">2 دقيقة مضت</time>
const RTL_LOCALES = new Set(['ar', 'he', 'fa', 'ur']);
function getLocaleDir(locale) {
return RTL_LOCALES.has(locale) ? 'rtl' : 'ltr';
}
timeEl.setAttribute('dir', getLocaleDir(locale));
// Язык страницы — английский, timeago — русский
document.documentElement.lang = 'en';
format(date, 'ru'); // Конфликт
Лучше получать локаль из document.documentElement.lang
или хранилища пользователя:
function getAppLocale() {
const lang = document.documentElement.lang || navigator.language;
const map = { 'ru': 'ru', 'ru-RU': 'ru', 'en': 'en_US', 'en-US': 'en_US' };
return map[lang] ?? 'ru';
}
// 'pt' — португальский, 'pt_BR' — бразильский вариант
// Если нужен европейский — 'pt_BR' неверен
format(date, 'pt_BR'); // Корректно только для Бразилии
timeago.js поддерживает pt_BR. Если нужен
pt_PT — требуется отдельная регистрация.
// Проверить, работает ли локаль
function testLocale(localeCode) {
const testDates = [
new Date(Date.now() - 30_000), // 30 секунд
new Date(Date.now() - 5 * 60_000), // 5 минут
new Date(Date.now() - 2 * 3600_000), // 2 часа
new Date(Date.now() - 2 * 86400_000), // 2 дня
new Date(Date.now() + 3600_000), // через час
];
console.group(`Locale: ${localeCode}`);
testDates.forEach(d => {
console.log(` ${d.toISOString()} → "${format(d, localeCode)}"`);
});
console.groupEnd();
}
testLocale('ru');
testLocale('en_US');