Проблемы с локализацией

Проблемы локализации проявляются как неверные словоформы, отсутствующие переводы, неправильное склонение и непредсказуемое поведение при работе с нестандартными языками.


Проблема 1: Локаль не зарегистрирована

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'); // Теперь на украинском

Проблема 2: Неверный индекс в LocaleFunc

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] ?? ['давно', 'скоро']; // Защита от выхода за пределы
});

Проблема 3: Некорректное склонение в русском

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, 'минуту', 'минуты', 'минут')}`,
      ];
    // ...
  }
});

Проблема 4: %s не заменяется числом

// Пользователь видит "%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, 'минуту', 'минуты', 'минут')} назад`, '...'];
});

Проблема 5: Конфликт имён локалей

// Локаль '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);
}

Проблема 6: Локаль загружена из неверного файла

// Импорт из cjs вместо esm — разные форматы
import ru from 'timeago.js/esm/lang/ru';     // Правильно для ESM
const ru = require('timeago.js/lib/lang/ru'); // Для CommonJS

Проблема 7: RTL локаль без поддержки в интерфейсе

Арабский и иврит — правосторонние языки. Текст форматируется правильно, но 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));

Проблема 8: Локаль не совпадает с языком страницы

// Язык страницы — английский, 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';
}

Проблема 9: Диалектные вариации

// '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');