From f69950acaff8d93abff0d2c80d5623627652bf18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Simi=C4=87?= Date: Thu, 1 Oct 2020 10:50:29 +0200 Subject: [PATCH 1/4] Update Serbian date locale to valid format --- src/locale/sr-cyrl.js | 8 ++++---- src/locale/sr.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/locale/sr-cyrl.js b/src/locale/sr-cyrl.js index 019544dac..38d2aad66 100644 --- a/src/locale/sr-cyrl.js +++ b/src/locale/sr-cyrl.js @@ -28,10 +28,10 @@ const locale = { formats: { LT: 'H:mm', LTS: 'H:mm:ss', - L: 'DD.MM.YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' + L: 'D. M. YYYY.', + LL: 'D. MMMM YYYY.', + LLL: 'D. MMMM YYYY. H:mm', + LLLL: 'dddd, D. MMMM YYYY. H:mm' } } diff --git a/src/locale/sr.js b/src/locale/sr.js index ca0cb47fb..ddd2d2769 100644 --- a/src/locale/sr.js +++ b/src/locale/sr.js @@ -28,10 +28,10 @@ const locale = { formats: { LT: 'H:mm', LTS: 'H:mm:ss', - L: 'DD.MM.YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' + L: 'D. M. YYYY.', + LL: 'D. MMMM YYYY.', + LLL: 'D. MMMM YYYY. H:mm', + LLLL: 'dddd, D. MMMM YYYY. H:mm' } } From 8ff37a402109243558f8ac5b530ed0d34b0872bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Simi=C4=87?= Date: Fri, 2 Oct 2020 15:40:54 +0200 Subject: [PATCH 2/4] Update foramts and fix grammar and logic This is the same update of formats that has been done about a month ago on Moment.js. Regarding the grammar, there was no logic for different grammar cases for the nouns. This is added by adding very precise logic for determining grammar cases by the `number`, `isFuture` and `withoutPreffix` parameters. The same update has been submitted to Moment.js. --- src/locale/sr-cyrl.js | 58 +++++++++++++++++++++++++++++++++++-------- src/locale/sr.js | 58 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/locale/sr-cyrl.js b/src/locale/sr-cyrl.js index 38d2aad66..1513a6183 100644 --- a/src/locale/sr-cyrl.js +++ b/src/locale/sr-cyrl.js @@ -1,6 +1,42 @@ // Serbian Cyrillic [sr-cyrl] import dayjs from 'dayjs' +const translator = { + words: { + m: ['један минут', 'једног минута'], + mm: ['%d минут', '%d минуте', '%d минута'], + h: ['један сат', 'једног сата'], + hh: ['%d сат', '%d сата', '%d сати'], + d: ['један дан', 'једног дана'], + dd: ['%d дан', '%d дана', '%d дана'], + M: ['један месец', 'једног месеца'], + MM: ['%d месец', '%d месеца', '%d месеци'], + y: ['једну годину', 'једне године'], + yy: ['%d годину', '%d године', '%d година'] + }, + correctGrammarCase(number, wordKey) { + if (number % 10 >= 1 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20)) { + return number % 10 === 1 ? wordKey[0] : wordKey[1] + } + return wordKey[2] + }, + relativeTimeFormatter(number, withoutSuffix, key, isFuture) { + const wordKey = translator.words[key] + + if (key.length === 1) { + // Nominativ + if (key === 'y' && withoutSuffix) return 'једна година' + return isFuture || withoutSuffix ? wordKey[0] : wordKey[1] + } + + const word = translator.correctGrammarCase(number, wordKey) + // Nominativ + if (key === 'yy' && withoutSuffix && word === '%d годину') return `${number} година` + + return word.replace('%d', number) + } +} + const locale = { name: 'sr-cyrl', weekdays: 'Недеља_Понедељак_Уторак_Среда_Четвртак_Петак_Субота'.split('_'), @@ -12,17 +48,17 @@ const locale = { relativeTime: { future: 'за %s', past: 'пре %s', - s: 'секунда', - m: 'минут', - mm: '%d минута', - h: 'сат', - hh: '%d сати', - d: 'дан', - dd: '%d дана', - M: 'месец', - MM: '%d месеци', - y: 'година', - yy: '%d године' + s: 'неколико секунди', + m: translator.relativeTimeFormatter, + mm: translator.relativeTimeFormatter, + h: translator.relativeTimeFormatter, + hh: translator.relativeTimeFormatter, + d: translator.relativeTimeFormatter, + dd: translator.relativeTimeFormatter, + M: translator.relativeTimeFormatter, + MM: translator.relativeTimeFormatter, + y: translator.relativeTimeFormatter, + yy: translator.relativeTimeFormatter }, ordinal: n => `${n}.`, formats: { diff --git a/src/locale/sr.js b/src/locale/sr.js index ddd2d2769..7a3891e3e 100644 --- a/src/locale/sr.js +++ b/src/locale/sr.js @@ -1,6 +1,42 @@ // Serbian [sr] import dayjs from 'dayjs' +const translator = { + words: { + m: ['jedan minut', 'jednog minuta'], + mm: ['%d minut', '%d minute', '%d minuta'], + h: ['jedan sat', 'jednog sata'], + hh: ['%d sat', '%d sata', '%d sati'], + d: ['jedan dan', 'jednog dana'], + dd: ['%d dan', '%d dana', '%d dana'], + M: ['jedan mesec', 'jednog meseca'], + MM: ['%d mesec', '%d meseca', '%d meseci'], + y: ['jednu godinu', 'jedne godine'], + yy: ['%d godinu', '%d godine', '%d godina'] + }, + correctGrammarCase(number, wordKey) { + if (number % 10 >= 1 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20)) { + return number % 10 === 1 ? wordKey[0] : wordKey[1] + } + return wordKey[2] + }, + relativeTimeFormatter(number, withoutSuffix, key, isFuture) { + const wordKey = translator.words[key] + + if (key.length === 1) { + // Nominativ + if (key === 'y' && withoutSuffix) return 'jedna godina' + return isFuture || withoutSuffix ? wordKey[0] : wordKey[1] + } + + const word = translator.correctGrammarCase(number, wordKey) + // Nominativ + if (key === 'yy' && withoutSuffix && word === '%d godinu') return `${number} godina` + + return word.replace('%d', number) + } +} + const locale = { name: 'sr', weekdays: 'Nedelja_Ponedeljak_Utorak_Sreda_Četvrtak_Petak_Subota'.split('_'), @@ -12,17 +48,17 @@ const locale = { relativeTime: { future: 'za %s', past: 'pre %s', - s: 'sekunda', - m: 'minut', - mm: '%d minuta', - h: 'sat', - hh: '%d sati', - d: 'dan', - dd: '%d dana', - M: 'mesec', - MM: '%d meseci', - y: 'godina', - yy: '%d godine' + s: 'nekoliko sekundi', + m: translator.relativeTimeFormatter, + mm: translator.relativeTimeFormatter, + h: translator.relativeTimeFormatter, + hh: translator.relativeTimeFormatter, + d: translator.relativeTimeFormatter, + dd: translator.relativeTimeFormatter, + M: translator.relativeTimeFormatter, + MM: translator.relativeTimeFormatter, + y: translator.relativeTimeFormatter, + yy: translator.relativeTimeFormatter }, ordinal: n => `${n}.`, formats: { From c9c068add785d68c10165995d7c55d6d78f9a4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Simi=C4=87?= Date: Fri, 2 Oct 2020 15:46:02 +0200 Subject: [PATCH 3/4] Add tests This adds tests for relative times in Serbian and with suffix time. --- test/locale/sr-cyrl.test.js | 54 ++++++++++++++++++++++++++++++++++++ test/locale/sr.test.js | 55 +++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 test/locale/sr-cyrl.test.js create mode 100644 test/locale/sr.test.js diff --git a/test/locale/sr-cyrl.test.js b/test/locale/sr-cyrl.test.js new file mode 100644 index 000000000..c1dc08477 --- /dev/null +++ b/test/locale/sr-cyrl.test.js @@ -0,0 +1,54 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/sr-cyrl' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Serbian cyrillic locale relative time in past and future', () => { + const cases = [ + [1, 's', 'за неколико секунди', 'неколико секунди'], + [-1, 's', 'пре неколико секунди', 'неколико секунди'], + [4, 's', 'за неколико секунди', 'неколико секунди'], + [1, 'm', 'за један минут', 'један минут'], + [-1, 'm', 'пре једног минута', 'један минут'], + [4, 'm', 'за 4 минуте', '4 минуте'], + [5, 'm', 'за 5 минута', '5 минута'], + [21, 'm', 'за 21 минут', '21 минут'], + [1, 'h', 'за један сат', 'један сат'], + [-1, 'h', 'пре једног сата', 'један сат'], + [4, 'h', 'за 4 сата', '4 сата'], + [5, 'h', 'за 5 сати', '5 сати'], + [21, 'h', 'за 21 сат', '21 сат'], + [1, 'd', 'за један дан', 'један дан'], + [-1, 'd', 'пре једног дана', 'један дан'], + [4, 'd', 'за 4 дана', '4 дана'], + [5, 'd', 'за 5 дана', '5 дана'], + [21, 'd', 'за 21 дан', '21 дан'], + [1, 'M', 'за један месец', 'један месец'], + [-1, 'M', 'пре једног месеца', 'један месец'], + [4, 'M', 'за 4 месеца', '4 месеца'], + [5, 'M', 'за 5 месеци', '5 месеци'], + [10, 'M', 'за 10 месеци', '10 месеци'], + [1, 'y', 'за једну годину', 'једна година'], + [-1, 'y', 'пре једне године', 'једна година'], + [4, 'y', 'за 4 године', '4 године'], + [5, 'y', 'за 5 година', '5 година'], + [21, 'y', 'за 21 годину', '21 година'] + ] + + cases.forEach((c) => { + expect(dayjs().add(c[0], c[1]).locale('sr-cyrl').fromNow()).toBe(c[2]) + expect(dayjs().add(c[0], c[1]).locale('sr-cyrl').fromNow(true)).toBe(c[3]) + // TODO: compare to momentjs once logic and grammar are fixed there + }) +}) + diff --git a/test/locale/sr.test.js b/test/locale/sr.test.js new file mode 100644 index 000000000..d32832945 --- /dev/null +++ b/test/locale/sr.test.js @@ -0,0 +1,55 @@ +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/sr' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('Serbian locale relative time in past and future', () => { + const cases = [ + [1, 's', 'za nekoliko sekundi', 'nekoliko sekundi'], + [-1, 's', 'pre nekoliko sekundi', 'nekoliko sekundi'], + [4, 's', 'za nekoliko sekundi', 'nekoliko sekundi'], + [1, 'm', 'za jedan minut', 'jedan minut'], + [-1, 'm', 'pre jednog minuta', 'jedan minut'], + [4, 'm', 'za 4 minute', '4 minute'], + [5, 'm', 'za 5 minuta', '5 minuta'], + [21, 'm', 'za 21 minut', '21 minut'], + [1, 'h', 'za jedan sat', 'jedan sat'], + [-1, 'h', 'pre jednog sata', 'jedan sat'], + [4, 'h', 'za 4 sata', '4 sata'], + [5, 'h', 'za 5 sati', '5 sati'], + [21, 'h', 'za 21 sat', '21 sat'], + [1, 'd', 'za jedan dan', 'jedan dan'], + [-1, 'd', 'pre jednog dana', 'jedan dan'], + [4, 'd', 'za 4 dana', '4 dana'], + [5, 'd', 'za 5 dana', '5 dana'], + [21, 'd', 'za 21 dan', '21 dan'], + [1, 'M', 'za jedan mesec', 'jedan mesec'], + [-1, 'M', 'pre jednog meseca', 'jedan mesec'], + [4, 'M', 'za 4 meseca', '4 meseca'], + [5, 'M', 'za 5 meseci', '5 meseci'], + [10, 'M', 'za 10 meseci', '10 meseci'], + [1, 'y', 'za jednu godinu', 'jedna godina'], + [-1, 'y', 'pre jedne godine', 'jedna godina'], + [4, 'y', 'za 4 godine', '4 godine'], + [5, 'y', 'za 5 godina', '5 godina'], + [21, 'y', 'za 21 godinu', '21 godina'] + ] + + cases.forEach((c) => { + // With suffix + expect(dayjs().add(c[0], c[1]).locale('sr').fromNow()).toBe(c[2]) + // Without suffix + expect(dayjs().add(c[0], c[1]).locale('sr').fromNow(true)).toBe(c[3]) + // TODO: compare to momentjs once logic and grammar are fixed there + }) +}) From 3efee294129a411506be092438a1c6585f0ee729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Simi=C4=87?= Date: Sun, 11 Oct 2020 15:38:22 +0200 Subject: [PATCH 4/4] Add minor grammatical fix from Moment.js --- src/locale/sr-cyrl.js | 2 +- src/locale/sr.js | 2 +- test/locale/sr-cyrl.test.js | 2 +- test/locale/sr.test.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/locale/sr-cyrl.js b/src/locale/sr-cyrl.js index 1513a6183..84b42addd 100644 --- a/src/locale/sr-cyrl.js +++ b/src/locale/sr-cyrl.js @@ -4,7 +4,7 @@ import dayjs from 'dayjs' const translator = { words: { m: ['један минут', 'једног минута'], - mm: ['%d минут', '%d минуте', '%d минута'], + mm: ['%d минут', '%d минута', '%d минута'], h: ['један сат', 'једног сата'], hh: ['%d сат', '%d сата', '%d сати'], d: ['један дан', 'једног дана'], diff --git a/src/locale/sr.js b/src/locale/sr.js index 7a3891e3e..8399d8e48 100644 --- a/src/locale/sr.js +++ b/src/locale/sr.js @@ -4,7 +4,7 @@ import dayjs from 'dayjs' const translator = { words: { m: ['jedan minut', 'jednog minuta'], - mm: ['%d minut', '%d minute', '%d minuta'], + mm: ['%d minut', '%d minuta', '%d minuta'], h: ['jedan sat', 'jednog sata'], hh: ['%d sat', '%d sata', '%d sati'], d: ['jedan dan', 'jednog dana'], diff --git a/test/locale/sr-cyrl.test.js b/test/locale/sr-cyrl.test.js index c1dc08477..a72ed33ae 100644 --- a/test/locale/sr-cyrl.test.js +++ b/test/locale/sr-cyrl.test.js @@ -20,7 +20,7 @@ it('Serbian cyrillic locale relative time in past and future', () => { [4, 's', 'за неколико секунди', 'неколико секунди'], [1, 'm', 'за један минут', 'један минут'], [-1, 'm', 'пре једног минута', 'један минут'], - [4, 'm', 'за 4 минуте', '4 минуте'], + [4, 'm', 'за 4 минута', '4 минута'], [5, 'm', 'за 5 минута', '5 минута'], [21, 'm', 'за 21 минут', '21 минут'], [1, 'h', 'за један сат', 'један сат'], diff --git a/test/locale/sr.test.js b/test/locale/sr.test.js index d32832945..c2ccb9f25 100644 --- a/test/locale/sr.test.js +++ b/test/locale/sr.test.js @@ -20,7 +20,7 @@ it('Serbian locale relative time in past and future', () => { [4, 's', 'za nekoliko sekundi', 'nekoliko sekundi'], [1, 'm', 'za jedan minut', 'jedan minut'], [-1, 'm', 'pre jednog minuta', 'jedan minut'], - [4, 'm', 'za 4 minute', '4 minute'], + [4, 'm', 'za 4 minuta', '4 minuta'], [5, 'm', 'za 5 minuta', '5 minuta'], [21, 'm', 'za 21 minut', '21 minut'], [1, 'h', 'za jedan sat', 'jedan sat'],