4 альтернативы Moment.js для интернационализации дат

  • 12 октября, 11:27
  • 8798
  • 0

Moment.js - одна из наиболее часто используемых библиотек JavaScript для форматирования и обработки дат, которые мы можем использовать для этой цели. Однако в некоторых случаях некоторые вещи в этой библиотеке (например, ее размер или структура) могут заставить вас задуматься, есть ли какие-то альтернативы.

4 альтернативы Moment.js для интернационализации дат

В этой статье я собираюсь рассмотреть четыре альтернативы moment.js в отношении интернационализации даты:

  1. API интернационализации JavaScript
  2. Luxon
  3. date -FNS
  4. day.js

Давайте начнем с API интернационализации JavaScript.

API интернационализации JavaScript

Intl является глобальным объектом, который действует как пространство имен API интернационализации ECMAScript. Что касается дат, этот объект предоставляет следующие конструкторы:

  • Intl.DateTimeFormat, который обеспечивает форматирование даты и времени
  • Intl.RelativeTimeFormat, который предоставляет простые для чтения фразы для дат и отметок времени

Эти конструкторы принимают два необязательных аргумента: locale и объект с параметрами для настройки вывода. Например:

let rtf = new Intl.RelativeTimeFormat('en-GB', { style: 'long' });
let dtf = new Intl.DateTimeFormat('de');

Аргумент locale - это строка, представляющая языковой тег BCP 47, который состоит из следующих частей:

  • Код языка ( ISO 639-1 / 639-2 ). Например, el(современный греческий)
  • Код сценария ( ISO 15924 ). Например, Grek(греческий)
  • Код страны ( ISO 3166 ). Например, GR(Греция)
  • Вариант. Например, polyton(политонический греческий)
  • Расширения (от Unicode). Например, u-nu-native(родные цифры)

Вот пример со всеми частями вместе:

let rtf = new Intl.RelativeTimeFormat('el-Grek-GR-polyton-u-nu-native');

Требуется только первая часть (код языка), и вы можете передать массив строк для определения резервных языков:

// Requests Dutch as the primary language and if it is not available, it requests french
let dtf = new Intl.DateTimeFormat(['nl', 'fr'])

Если языковой стандарт не указан, используется языковой стандарт среды выполнения.

Что касается второго аргумента, объекта параметров, он варьируется между конструкторами.

Intl.DateTimeFormat принимает опции, такие как стиль даты ( full, long, medium, и short), нужно использовать либо 12-часовой или 24-часовой формат времени или представление частей дня, как год, месяц, день недели и т.д.

На странице документации Intl.DateTimeFormat вы можете узнать больше обо всех параметрах, которые можно использовать для настройки этого объекта.

О Intl.RelativeTimeFormat, объект параметров имеет только следующие свойства:

  • localeMatcher, алгоритм соответствия locale для использования. Возможные значения: lookup(от более конкретного к менее конкретному, если en-us недоступно, en выбрано) и best fit (значение по умолчанию, если en-us недоступно, en-uk может быть выбрано что-то подобное )
  • numeric, чтобы отформатировать выходное сообщение. Возможные значения: always (например, 2 hours ago) или auto, что не всегда допускает числовые значения в выходных данных (например, yesterday)
  • style, чтобы отформатировать длину выходного сообщения. Возможные значения long, short и narrow

Если у вас есть объект типа Intl.DateTimeFormat или Intl.RelativeTimeFormat, вы можете использовать методы format() или formatToParts() (который возвращает массив с частями выходных данных) для форматирования даты.

В случае Intl.DateTimeFormat, методы принимают Date объект для форматирования:

const date = new Date(Date.UTC(2014, 8, 19, 14, 5, 0));
const options = {
   dateStyle: 'short',
   timeStyle: 'full',
   hour12: true,
   day: 'numeric',
   month: 'long',
   year: '2-digit',
   minute: '2-digit',
   second: '2-digit',
};

// Sample output: 19 septembre 14 à 05:00
console.log(new Intl.DateTimeFormat("fr", options).format(date));

// Sample output: 19. September 14, 05:00
console.log(new Intl.DateTimeFormat("de-AT", options).format(date));
/* Sample output: [{"type":"day","value":"19"},{"type":"literal","value":" "},
{"type":"month","value":"settembre"},{"type":"literal","value":" "}{"type":"year","value":"14"},{"type":"literal","value":", "{"type":"minute","value":"05"},{"type":"literal","value":":"},{"type":"second","value":"00"}] */ console.log(new Intl.DateTimeFormat("it", options).formatToParts(date));

Обратите внимание, что если вы указываете только несколько компонентов даты и времени в объекте параметров, они будут присутствовать в выходных данных:

const date = new Date(Date.UTC(2014, 08, 19, 14, 5, 0));
const options = {
   year: '2-digit',
};
// Output: 14
console.log(new Intl.DateTimeFormat("en", options).format(date));

В случае Intl.RelativeTimeFormat, format() принимает числовое значение для использования в сообщении и второй аргумент, чтобы указать единицу измерения этого значения (например, year или second в форме единственного или множественного числа):

const options = {
   localeMatcher: 'best fit',
   numeric: 'auto',
   style: 'short',
};
// Output: last mo.
console.log(new Intl.RelativeTimeFormat("en-CA", options).format(-1, 'month'));
// Output: la semana pasada
console.log(new Intl.RelativeTimeFormat("es-ES", options).format(-1, 'week'));
/* Output: [{"type":"integer","value":"60","unit":"minute"},{"type":"literal","value":" 分鐘前"}] */
console.log(new Intl.RelativeTimeFormat("zh-TW", options).formatToParts(-60, 'minutes'));

Кроме того , обратите внимание на разницу между использованием always и auto значения для numeric объекта:

// Output: in 0 days
console.log(new Intl.RelativeTimeFormat("en", {numeric: 'always'}).format(0, 'day'));
// Output: today
console.log(new Intl.RelativeTimeFormat("en", {numeric: 'auto'}).format(0, 'day'));

Большая часть функциональности Intl.DateTimeFormat  хорошо поддерживается в современных браузерах, однако Intl.RelativeTimeFormat полностью поддерживается только в Chrome 71 и Firefox 70 (на момент написания этой статьи поддержка Safari или Edge не поддерживалась).

Вы можете использовать полифил , но вам придется создать объект по-другому:

const myLocale = /* Import JSON file for the choosen locale */;
const localeTag = /* Tag for the above locale */;
const options = { /* Options object */ };
RelativeTimeFormat.addLocale(myLocale);
new RelativeTimeFormat(localeTag, options).format(3, 'day');

Итак, как вы можете видеть, Intl.RelativeTimeFormat похоже на moment.duration(). humanize():

moment.duration(-1, 'weeks').humanize(true); // a week ago

Если вы привыкли к вычислению относительного времени способом moment.js делает:

moment('20140919', 'YYYYMMDD').fromNow(); // 5 years ago
moment().add(5, 'days').calendar(); // Tuesday at 1:15 PM

Вам нужно будет вручную рассчитать разницу между двумя датами .

Luxon

Luxon - это библиотека, созданная одним из ведущих разработчиков, поэтому она заимствует много идей, предлагая улучшения в некоторых областях.

В целях интернационализации вы можете думать о Luxon как обертке для Intl.DateTimeFormat и Intl.RelativeTimeFormat.

Например, один из способов форматирования дат в соответствии с locale - сначала установить locale, а затем использовать метод toFormat(fmt:string, opts: Object) вместе с токенами даты и времени из этой таблицы :

// Sample output: 2019 сентябрь
console.log(DateTime.local().setLocale('ru').toFormat('yyyy MMMM'));

Вы также можете передать locale в объекте параметров, который метод может принять в качестве аргумента:

// Output: 2019 сентябрь
console.log(DateTime.local(2018, 9, 1).toFormat('yyyy MMMM', { locale: "ru" }));

Или, если вы используете такие методы, как fromObject , fromISO , fromHTTP , fromFormat или fromRFC2822 , вы можете установить locale во время создания:

const italianDate = DateTime.fromISO("2014-09-19", { locale: "it" });
// Output: 2014 settembre 19
console.log(italianDate.toFormat("yyyy MMMM dd"));

Тем не менее, рекомендуемый способ заключается в использовании методов toLocaleString() и toLocaleParts() который возвращает локализованную строку , представляющую дату и массив отдельных частей строки, соответственно.

Эти методы эквивалентны методам format() и formatToParts() из Intl.DateTimeFormat, и на самом деле, они принимают один и тот же объект опций (наряду с некоторыми настройками, такие как DateTime.DATE_SHORT, среди прочих ):

const date = DateTime.utc(2014, 9, 1, 14, 5, 0);
const options = {
  dateStyle: "short",
  timeStyle: "full",
  hour12: true,
  day: "numeric",
  month: "long",
  year: "2-digit",
  minute: "2-digit",
  second: "2-digit"
};

// Output: 1 septembre 14 à 05:00 
console.log(date.setLocale("fr").toLocaleString(options));
// Output: 1. September 14, 05:00 
console.log(date.setLocale("de-AT").toLocaleString(options));
/* Output: [{"type":"day","value":"1"},{"type":"literal","value":" "},{"type":"month","value":"settembre"},{"type":"literal","value":" "},{"type":"year","value":"14"},{"type":"literal","value":", "},{"type":"minute","value":"05"},{"type":"literal","value":":"},{"type":"second","value":"00"}] */
console.log(
  JSON.stringify(date.setLocale("it").toLocaleParts(options), null, 3)
);
// Output: 2:05 PM
console.log(date.toLocaleString(DateTime.TIME_SIMPLE));
// Output: 01/09/2014 
console.log(date.toLocaleString({ locale: 'pt' }));

Это значит, что:

  • Luxon может быть настроен с использованием тех же строк locale BCP 47, что и Intl объект
  • Если Intl объект недоступен в целевом браузере, эта часть библиотеки не будет работать должным образом (для приложений Node.js может потребоваться выполнить некоторые дополнительные действия для настройки библиотеки )
  • Что касается интернационализации, Luxon выступает в качестве оболочки для API интернационализации JavaScript, но устанавливает языковой стандарт на уровне DateTime объекта Luxon (более подробная информация здесь )

С другой стороны, методы toRelative (по умолчанию возвращающие строковое представление заданного времени относительно текущего ) и toRelativeCalendar (по умолчанию возвращающие строковое представление заданной даты относительно сегодняшнего дня) являются теми, которые предоставляют функциональность похожую на Intl.RelativeTimeFormat:

// Sample output: in 23 hours 
console.log(DateTime.local().plus({ days: 1 }).toRelative());
// Sample output: tomorrow
console.log(DateTime.local().plus({ days: 1 }).toRelativeCalendar());
// Sample output: in 1 Tag 
console.log(DateTime.local().plus({ days: 1 }).toRelative({ locale: "de" }));
// Sample output: morgen 
console.log(DateTime.local().plus({ days: 1 }).toRelativeCalendar({ locale: "de" }));
// Sample output: il y a 1 semaine 
console.log(DateTime.local().setLocale("fr").minus({ days: 9 }).toRelative({ unit: "weeks" }));
// Sample output: la semaine dernière 
console.log(DateTime.local().setLocale("fr").minus({ days: 9 }).toRelativeCalendar({ unit: "weeks" }));

В отличие от этого Intl.RelativeTimeFormat, если ваш браузер не поддерживает этот API, приведенные выше методы не будут выдавать ошибку, единственная проблема заключается в том, что они не будут переведены на соответствующий язык.

Date -FNS

Date-fns - еще одна популярная библиотека JavaScript для обработки и форматирования даты. Версия 2, последняя на момент написания этой статьи, поставляется только в виде пакета NPM, поэтому, если вы хотите использовать его непосредственно в браузере, вам придется использовать такой пакет, как Browserify.

Эта библиотека содержит около шестидесяти различных locale (здесь вы можете увидеть их все). Чтобы использовать одну или несколько локалей, вам нужно импортировать их следующим образом:

import { es, enCA, it, ptBR } from 'date-fns/locale'

Функции, принимающие locale в качестве аргумента, следующие:

  • формат, который возвращает отформатированную дату, принимая в качестве параметров строку, представляющую шаблон для форматирования даты (на основе символов полей даты технического стандарта Unicode # 35 ), и объект с такими опциями, как языковой стандарт и индекс первого дня недели
  • formatDistance, который возвращает расстояние между указанными датами в словах, принимая в качестве параметров даты для сравнения и объект с такими параметрами, как locale или включение секунд
  • formatDistanceToNow - это то же самое, что formatDistance но только одна дата (которая будет сравниваться с текущей )
  • formatDistanceStrict такая же, как, formatDistance но без использования помощников, как almost, over или less than. У объекта параметров есть свойства для принудительной установки единицы времени и указания способа округления частичных единиц
  • formatRelative, представляющий дату в словах относительно данной базовой даты. Он также может принимать объект параметров в качестве аргумента для установки locale и индекса первого дня недели.

Вот некоторые примеры:

import {
   format,
   formatDistance,
   formatDistanceToNow,
   formatDistanceStrict,
   formatRelative,
   addDays
 } from "date-fns";
 import { es, enCA, ro, it, ptBR } from "date-fns/locale";


 // Output: septiembre / 19
 console.log(format(new Date(), "MMMM '/' yy", { locale: es }));
 // Output: in less than 10 seconds
 console.log(
   formatDistance(
     new Date(2019, 8, 1, 0, 0, 15),
     new Date(2019, 8, 1, 0, 0, 10),
     { locale: enCA, includeSeconds: true, addSuffix: true }
   )
 );
 // Output: less than 10 seconds ago
 console.log(
   formatDistance(
     new Date(2019, 8, 1, 0, 0, 10),
     new Date(2019, 8, 1, 0, 0, 15),
     { locale: enCA, includeSeconds: true, addSuffix: true }
   )
 );
 // Output: circa 15 ore (assuming now is 9/20/2019 15:00)
 console.log(formatDistanceToNow(new Date(2019, 8, 20), { locale: ro }));
 // Output: 0 minuti
 console.log(
   formatDistanceStrict(
     new Date(2019, 8, 1, 0, 0, 15),
     new Date(2019, 8, 1, 0, 0, 10),
     { locale: it, unit: "minute" }
   )
 );
 // Output: un minuto
 console.log(
   formatDistanceStrict(
     new Date(2019, 8, 1, 0, 0, 10),
     new Date(2019, 8, 1, 0, 0, 15),
     { locale: it, unit: "minute", roundingMethod: "ceil" }
   )
 );
 // Output: amanhã às 14:48
 console.log(formatRelative(addDays(new Date(), 1), new Date(), { locale: ptBR }));

formatRelative обычно используется с помощниками для добавления или вычитания различных единиц времени, таких как addWeeks , subMonths , addQuarters и другие.

Также учтите, что если расстояние между датами превышает шесть дней, formatRelative будет возвращена дата, указанная в качестве первого аргумента:

// If today is September 20, 2019 the output will be 27/09/2019
console.log(formatRelative(addDays(new Date(), 7), new Date(), { locale: ptBR }));

Day.js

Day.js - это легковесная библиотека с API, похожим на moment.js.

По умолчанию Day.js поставляется с английским языком в Соединенных Штатах. Чтобы использовать другие locale, вам нужно импортировать их следующим образом:

import 'dayjs/locale/pt';
import localeDe from 'dayjs/locale/de'; // With a custom alias for the locale object

dayjs.locale('pt') // use Portuguese locale globally
// To use the locale just in certain places
console.log(
  dayjs()
    .locale(localeDe)
    .format()
);
console.log( dayjs('2018-4-28', { locale: 'pt' }) );

Здесь вы можете найти список всех поддерживаемых locale.

В приведенном выше примере метод format ()возвращает строку с отформатированной датой. Для форматирования даты определенным образом может потребоваться строка с токенами:

// Sample output: September 2019, Samstag
console.log(
  dayjs()
    .locale(localeDe)
    .format('MMMM YYYY, dddd')
);

Вот список всех доступных форматов.

Тем не менее, большая часть расширенной функциональности Day.js основана на плагинах, которые вы можете загружать в зависимости от ваших потребностей. Например, плагин UTC добавляет методы для получения даты в формате UTC и по местному времени:

import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";

dayjs.extend(utc);

console.log(dayjs.utc().format()); // Sample output: 2019-09-21T11:31:55Z

Что касается интернационализации, мы можем использовать плагины AdvancedFormat , LocalizedFormat , RelativeTime и Calendar .

Плагины AdvancedFormat и LocalizedFormat добавляют больше опций форматирования к format() методу:

// ...

// Plugins
import advancedFormat from "dayjs/plugin/advancedFormat";
import localizedFormat from "dayjs/plugin/localizedFormat";

// Load plugins
dayjs.extend(advancedFormat);
dayjs.extend(localizedFormat);

// Advanced format options
// If today is 2019/09/21 at 12:00 PM, the output will be 3 21º 12 12 1569087454 1569087454869
console.log(
  dayjs()
    .locale("pt")
    .format("Q Do k kk X x")
);
// Localized format options
// If today is 2019/09/21 at 12:00 PM, the output will be Sábado, 21 de Setembro de 2019 às 12:00 
console.log(
  dayjs()
    .locale("pt")
    .format("LLLL")
);

RelativeTime плагин добавляет методы для форматирования даты в относительные строки времени:

  • .fromNow(withoutSuffix?: boolean) возвращает строку, представляющую относительное время с этого момента
  • .from(compared: Dayjs, withoutSuffix?: boolean) возвращает строку, представляющую относительное время из X
  • .toNow(withoutSuffix?: boolean) возвращает строку, представляющую относительное время до настоящего времени
  • .to(compared: Dayjs, withoutSuffix?: boolean) возвращает строку, представляющую относительное время для X

Вот некоторые примеры:

// ...
import relativeTime from "dayjs/plugin/relativeTime";

// Load plugin
dayjs.extend(relativeTime);


// Assuming now is 2019-09-21 at 12:00 PM
// Output: in einem Jahr 
console.log(
  dayjs()
    .locale(localeDe)
    .from(dayjs("2018-09-21"))
);
// Output: einem Jahr 
console.log(
  dayjs()
    .locale(localeDe)
    .from(dayjs("2018-09-21"), true)
);
// Output: vor einem Jahr 
console.log(
  dayjs("2018-09-21")
    .locale(localeDe)
    .fromNow()
);
// Output: vor 2 Jahren 
console.log(
  dayjs("2018-09-21")
    .locale(localeDe)
    .to(dayjs("2016-09-21"))
);
// Output: vor 11 Jahren 
console.log(
  dayjs("2030-09-21")
    .locale(localeDe)
    .toNow()
);

Плагин Calendar добавляет .calendar метод для отображения календарного времени (на расстоянии до семи дней). 

// ...
import calendar from "dayjs/plugin/calendar";

// Load plugin
dayjs.extend(calendar);

// Assuming now is 2019-09-21 at 12:00 PM
// Output: Yesterday at 12:00 PM
console.log(
  dayjs()
    .locale('pt')
    .calendar(dayjs("2019-09-22"))
);

Однако он позволяет вручную настраивать выходные метки для того же дня, следующего дня, последних выходных и следующей недели, а также всего остального, используя строковые литералы (заключенные в квадратные скобки) и токены формата даты и времени:

// Assuming now is 2019-09-21 at 12:00 PM
// The output is Hoje às 12:00
console.log(
  dayjs().calendar(dayjs("2019-09-21"), {
    sameDay: "[Hoje às] h:m",
    nextDay: "[Amanhã]",
    nextWeek: "dddd",
    lastDay: "[Ontem]",
    lastWeek: "[Último] dddd",
    sameElse: "DD/MM/YYYY"
  })
);

Moment.js - это надежная библиотека для обработки дат, однако для некоторых проектов она может оказаться излишней. В этой статье я сравнил то, как четыре популярные библиотеки обрабатывают форматирование даты в контексте интернационализации.

Если вам нужен API более высокого уровня (например, относительное время) и другие функции, такие как часовые пояса или вспомогательные методы для добавления или вычитания единиц времени, вы можете  рассмотреть одну из других библиотек, представленых в этой статье.


0 комментариев
Сортировка:
Добавить комментарий

IT Новости

Смотреть все