From 043106c1cfebb8fbae4e03faf5b23df5458bf1c9 Mon Sep 17 00:00:00 2001 From: Mikhail Bashkirov Date: Mon, 13 May 2019 10:29:57 +0200 Subject: [PATCH] feat(localize): add reusable generators for month and weekday names --- packages/localize/index.js | 2 + packages/localize/src/date/getMonthNames.js | 25 +++++++ packages/localize/src/date/getWeekdayNames.js | 53 +++++++++++++++ .../localize/test/date/getMonthNames.test.js | 21 ++++++ .../test/date/getWeekdayNames.test.js | 65 +++++++++++++++++++ 5 files changed, 166 insertions(+) create mode 100644 packages/localize/src/date/getMonthNames.js create mode 100644 packages/localize/src/date/getWeekdayNames.js create mode 100644 packages/localize/test/date/getMonthNames.test.js create mode 100644 packages/localize/test/date/getWeekdayNames.test.js diff --git a/packages/localize/index.js b/packages/localize/index.js index efb5d6cb3..1efbe8f3b 100644 --- a/packages/localize/index.js +++ b/packages/localize/index.js @@ -1,5 +1,7 @@ export { formatDate } from './src/date/formatDate.js'; export { getDateFormatBasedOnLocale } from './src/date/getDateFormatBasedOnLocale.js'; +export { getMonthNames } from './src/date/getMonthNames.js'; +export { getWeekdayNames } from './src/date/getWeekdayNames.js'; export { parseDate } from './src/date/parseDate.js'; export { formatNumber, diff --git a/packages/localize/src/date/getMonthNames.js b/packages/localize/src/date/getMonthNames.js new file mode 100644 index 000000000..132b7c8fa --- /dev/null +++ b/packages/localize/src/date/getMonthNames.js @@ -0,0 +1,25 @@ +import { normalizeDate } from './normalizeDate.js'; + +const monthsLocaleCache = {}; + +export function getMonthNames({ locale }) { + let months = monthsLocaleCache[locale]; + + if (months) { + return months; + } + + months = []; + + const formatter = new Intl.DateTimeFormat(locale, { month: 'long' }); + for (let i = 0; i < 12; i += 1) { + const date = new Date(2019, i, 1); + const formattedDate = formatter.format(date); + const normalizedDate = normalizeDate(formattedDate); + months.push(normalizedDate); + } + + monthsLocaleCache[locale] = months; + + return months; +} diff --git a/packages/localize/src/date/getWeekdayNames.js b/packages/localize/src/date/getWeekdayNames.js new file mode 100644 index 000000000..aec20bb12 --- /dev/null +++ b/packages/localize/src/date/getWeekdayNames.js @@ -0,0 +1,53 @@ +import { normalizeDate } from './normalizeDate.js'; + +const weekdayNamesCache = {}; + +/** + * @desc Return cached weekday names for locale for all styles ('long', 'short', 'narrow') + * @param {string} locale locale + * @returns {Object} like { long: ['Sunday', 'Monday'...], short: ['Sun', ...], narrow: ['S', ...] } + */ +function getCachedWeekdayNames(locale) { + let weekdays = weekdayNamesCache[locale]; + + if (weekdays) { + return weekdays; + } + + weekdayNamesCache[locale] = { long: [], short: [], narrow: [] }; + + ['long', 'short', 'narrow'].forEach(style => { + weekdays = weekdayNamesCache[locale][style]; + const formatter = new Intl.DateTimeFormat(locale, { weekday: style }); + + const date = new Date('2019/04/07'); // start from Sunday + for (let i = 0; i < 7; i += 1) { + const weekday = formatter.format(date); + const normalizedWeekday = normalizeDate(weekday); + weekdays.push(normalizedWeekday); + date.setDate(date.getDate() + 1); + } + }); + + return weekdayNamesCache[locale]; +} + +// TODO: consider using a database with information for the `firstDayOfWeek`? +// https://github.com/unicode-cldr/cldr-core/blob/35.0.0/supplemental/weekData.json#L60 +// https://github.com/tc39/ecma402/issues/6#issuecomment-114079502 + +/** + * @desc Returns weekday names for locale + * @param {string} options.locale locale + * @param {string} [options.style=long] long, short or narrow + * @param {number} [options.firstDayOfWeek=0] 0 (Sunday), 1 (Monday), etc... + * @returns {Array} like: ['Sunday', 'Monday', 'Tuesday', ...etc]. + */ +export function getWeekdayNames({ locale, style = 'long', firstDayOfWeek = 0 } = {}) { + const weekdays = getCachedWeekdayNames(locale)[style]; + const orderedWeekdays = []; + for (let i = firstDayOfWeek; i < firstDayOfWeek + 7; i += 1) { + orderedWeekdays.push(weekdays[i % 7]); + } + return orderedWeekdays; +} diff --git a/packages/localize/test/date/getMonthNames.test.js b/packages/localize/test/date/getMonthNames.test.js new file mode 100644 index 000000000..cb2925b78 --- /dev/null +++ b/packages/localize/test/date/getMonthNames.test.js @@ -0,0 +1,21 @@ +import { expect } from '@open-wc/testing'; + +import { getMonthNames } from '../../src/date/getMonthNames.js'; + +function s(strings) { + return strings[0].split(' '); +} + +describe('getMonthNames', () => { + it('generates month names for a given locale', () => { + expect(getMonthNames({ locale: 'en-GB' })).to.deep.equal( + s`January February March April May June July August September October November December`, + ); + expect(getMonthNames({ locale: 'nl-NL' })).to.deep.equal( + s`januari februari maart april mei juni juli augustus september oktober november december`, + ); + expect(getMonthNames({ locale: 'zh-CH' })).to.deep.equal( + s`一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月`, + ); + }); +}); diff --git a/packages/localize/test/date/getWeekdayNames.test.js b/packages/localize/test/date/getWeekdayNames.test.js new file mode 100644 index 000000000..ded662192 --- /dev/null +++ b/packages/localize/test/date/getWeekdayNames.test.js @@ -0,0 +1,65 @@ +import { expect } from '@open-wc/testing'; + +import { getWeekdayNames } from '../../src/date/getWeekdayNames.js'; + +function s(strings) { + return strings[0].split(' '); +} + +describe('getWeekdayNames', () => { + it('generates weekday names for a given locale with defaults (from Sunday, long style)', () => { + expect(getWeekdayNames({ locale: 'en-GB' })).to.deep.equal( + s`Sunday Monday Tuesday Wednesday Thursday Friday Saturday`, + ); + expect(getWeekdayNames({ locale: 'nl-NL' })).to.deep.equal( + s`zondag maandag dinsdag woensdag donderdag vrijdag zaterdag`, + ); + expect(getWeekdayNames({ locale: 'zh-CH' })).to.deep.equal( + s`星期日 星期一 星期二 星期三 星期四 星期五 星期六`, + ); + }); + + it('allows to specify a day when a week starts', () => { + expect(getWeekdayNames({ locale: 'en-GB', firstDayOfWeek: 7 })).to.deep.equal( + s`Sunday Monday Tuesday Wednesday Thursday Friday Saturday`, + ); + expect(getWeekdayNames({ locale: 'en-GB', firstDayOfWeek: 1 })).to.deep.equal( + s`Monday Tuesday Wednesday Thursday Friday Saturday Sunday`, + ); + expect(getWeekdayNames({ locale: 'en-GB', firstDayOfWeek: 2 })).to.deep.equal( + s`Tuesday Wednesday Thursday Friday Saturday Sunday Monday`, + ); + expect(getWeekdayNames({ locale: 'en-GB', firstDayOfWeek: 3 })).to.deep.equal( + s`Wednesday Thursday Friday Saturday Sunday Monday Tuesday`, + ); + expect(getWeekdayNames({ locale: 'en-GB', firstDayOfWeek: 4 })).to.deep.equal( + s`Thursday Friday Saturday Sunday Monday Tuesday Wednesday`, + ); + expect(getWeekdayNames({ locale: 'en-GB', firstDayOfWeek: 5 })).to.deep.equal( + s`Friday Saturday Sunday Monday Tuesday Wednesday Thursday`, + ); + expect(getWeekdayNames({ locale: 'en-GB', firstDayOfWeek: 6 })).to.deep.equal( + s`Saturday Sunday Monday Tuesday Wednesday Thursday Friday`, + ); + }); + + it('supports "short" style', () => { + expect(getWeekdayNames({ locale: 'en-GB', style: 'short' })).to.deep.equal( + s`Sun Mon Tue Wed Thu Fri Sat`, + ); + expect(getWeekdayNames({ locale: 'nl-NL', style: 'short' })).to.deep.equal( + s`zo ma di wo do vr za`, + ); + expect(getWeekdayNames({ locale: 'zh-CH', style: 'short' })).to.deep.equal( + s`周日 周一 周二 周三 周四 周五 周六`, + ); + }); + + it('supports "narrow" style', () => { + expect(getWeekdayNames({ locale: 'en-GB', style: 'narrow' })).to.deep.equal(s`S M T W T F S`); + expect(getWeekdayNames({ locale: 'nl-NL', style: 'narrow' })).to.deep.equal(s`Z M D W D V Z`); + expect(getWeekdayNames({ locale: 'zh-CH', style: 'narrow' })).to.deep.equal( + s`日 一 二 三 四 五 六`, + ); + }); +});