Регистрация пользовательских локалей

Регистрация локали — это процесс добавления функции форматирования в глобальный реестр библиотеки. После регистрации локаль доступна по строковому ключу во всём приложении.


Функция register

register(locale: string, localeFunc: LocaleFunc): void
  • locale — произвольная строка-идентификатор
  • localeFunc — функция формата (number, index) => [string, string]

Базовый пример регистрации

import { register, format } from 'timeago.js';

register('my_locale', (number, index) => {
  const strings = [
    ['только что', 'скоро'],
    ['%s с. назад', 'через %s с.'],
    ['%s с. назад', 'через %s с.'],
    ['1 мин. назад', 'через 1 мин.'],
    ['%s мин. назад', 'через %s мин.'],
    ['1 ч. назад', 'через 1 ч.'],
    ['%s ч. назад', 'через %s ч.'],
    ['1 д. назад', 'через 1 д.'],
    ['%s д. назад', 'через %s д.'],
    ['1 нед. назад', 'через 1 нед.'],
    ['%s нед. назад', 'через %s нед.'],
    ['1 мес. назад', 'через 1 мес.'],
    ['%s мес. назад', 'через %s мес.'],
    ['1 г. назад', 'через 1 г.'],
    ['%s л. назад', 'через %s л.'],
  ];
  return strings[index];
});

format(Date.now() - 1000 * 60 * 10, 'my_locale');
// → "10 мин. назад"

Регистрация функции, определённой отдельно

function ruShort(number, index) {
  const strings = [/* ... */];
  return strings[index];
}

register('ru_short', ruShort);

Регистрация через экспорт модуля

// locales/ru_business.js

export default function(number, index) {
  const strings = [
    ['только что',    'скоро'],
    ['недавно',       'скоро'],
    ['несколько минут назад', 'через несколько минут'],
    // ...
  ];
  return strings[index];
}
import { register } from 'timeago.js';
import ruBusiness from './locales/ru_business.js';

register('ru_business', ruBusiness);

Переопределение встроенной локали

Встроенную en_US можно переопределить:

register('en_US', (number, index) => {
  const strings = [
    ['just now', 'right now'],
    ['%s sec ago', 'in %s sec'],
    // ...
  ];
  return strings[index];
});

После этого вызовы без явной локали будут использовать новые строки.


Регистрация с динамическим содержимым

Функция локали может генерировать строки на основе словаря:

const dictionary = {
  second: { one: 'секунду', few: 'секунды', many: 'секунд' },
  minute: { one: 'минуту', few: 'минуты', many: 'минут' },
  hour:   { one: 'час',    few: 'часа',   many: 'часов'  },
  day:    { one: 'день',   few: 'дня',    many: 'дней'   },
  week:   { one: 'неделю', few: 'недели', many: 'недель' },
  month:  { one: 'месяц',  few: 'месяца', many: 'месяцев'},
  year:   { one: 'год',    few: 'года',   many: 'лет'    },
};

const units = ['', 'second', 'second', 'minute', 'minute', 'hour', 'hour',
               'day', 'day', 'week', 'week', 'month', 'month', 'year', 'year'];

register('ru_dict', (number, index) => {
  if (index === 0) return ['только что', 'прямо сейчас'];

  const unit  = units[index];
  const forms = dictionary[unit];
  const form  = pluralRu(number, forms.one, forms.few, forms.many);

  return [
    `${number} ${form} назад`,
    `через ${number} ${form}`,
  ];
});

Регистрация нескольких вариантов одного языка

register('ru',         ruFull);      // полный вариант
register('ru_short',   ruShort);     // сокращённый
register('ru_formal',  ruFormal);    // официальный стиль
register('ru_casual',  ruCasual);    // разговорный стиль

Все они существуют в реестре одновременно и выбираются по ключу.


Идемпотентность регистрации

Повторная регистрация с тем же ключом перезаписывает предыдущую функцию:

register('ru', firstVersion);
register('ru', secondVersion); // firstVersion недоступна

Это позволяет обновлять локали без перезагрузки страницы.


Проверка перед использованием

Прямого способа проверить, зарегистрирована ли локаль, нет. Можно использовать try/catch:

function isLocaleRegistered(locale) {
  try {
    format(Date.now(), locale);
    return true;
  } catch {
    return false;
  }
}

Или хранить собственный реестр:

const registeredLocales = new Set();

function safeRegister(key, fn) {
  register(key, fn);
  registeredLocales.add(key);
}

function hasLocale(key) {
  return registeredLocales.has(key);
}

Порядок регистрации

Регистрация должна произойти до первого вызова format с этой локалью. Если локаль не зарегистрирована, поведение зависит от версии библиотеки — обычно возвращается строка из en_US или выбрасывается ошибка.


TypeScript-типизация при регистрации

import { register } from 'timeago.js';

type LocaleFunc = (number: number, index: number) => [string, string];

const myLocale: LocaleFunc = (number, index) => {
  // ...
  return ['', ''];
};

register('custom', myLocale);