Skip to content

Commit cc87eff

Browse files
authored
fix: fix Serbian locale grammar (sr, sr-cyrl) (#1108)
1 parent be505c2 commit cc87eff

File tree

4 files changed

+211
-30
lines changed

4 files changed

+211
-30
lines changed

src/locale/sr-cyrl.js

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,42 @@
11
// Serbian Cyrillic [sr-cyrl]
22
import dayjs from 'dayjs'
33

4+
const translator = {
5+
words: {
6+
m: ['један минут', 'једног минута'],
7+
mm: ['%d минут', '%d минута', '%d минута'],
8+
h: ['један сат', 'једног сата'],
9+
hh: ['%d сат', '%d сата', '%d сати'],
10+
d: ['један дан', 'једног дана'],
11+
dd: ['%d дан', '%d дана', '%d дана'],
12+
M: ['један месец', 'једног месеца'],
13+
MM: ['%d месец', '%d месеца', '%d месеци'],
14+
y: ['једну годину', 'једне године'],
15+
yy: ['%d годину', '%d године', '%d година']
16+
},
17+
correctGrammarCase(number, wordKey) {
18+
if (number % 10 >= 1 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20)) {
19+
return number % 10 === 1 ? wordKey[0] : wordKey[1]
20+
}
21+
return wordKey[2]
22+
},
23+
relativeTimeFormatter(number, withoutSuffix, key, isFuture) {
24+
const wordKey = translator.words[key]
25+
26+
if (key.length === 1) {
27+
// Nominativ
28+
if (key === 'y' && withoutSuffix) return 'једна година'
29+
return isFuture || withoutSuffix ? wordKey[0] : wordKey[1]
30+
}
31+
32+
const word = translator.correctGrammarCase(number, wordKey)
33+
// Nominativ
34+
if (key === 'yy' && withoutSuffix && word === '%d годину') return `${number} година`
35+
36+
return word.replace('%d', number)
37+
}
38+
}
39+
440
const locale = {
541
name: 'sr-cyrl',
642
weekdays: 'Недеља_Понедељак_Уторак_Среда_Четвртак_Петак_Субота'.split('_'),
@@ -12,26 +48,26 @@ const locale = {
1248
relativeTime: {
1349
future: 'за %s',
1450
past: 'пре %s',
15-
s: 'секунда',
16-
m: 'минут',
17-
mm: '%d минута',
18-
h: 'сат',
19-
hh: '%d сати',
20-
d: 'дан',
21-
dd: '%d дана',
22-
M: 'месец',
23-
MM: '%d месеци',
24-
y: 'година',
25-
yy: '%d године'
51+
s: 'неколико секунди',
52+
m: translator.relativeTimeFormatter,
53+
mm: translator.relativeTimeFormatter,
54+
h: translator.relativeTimeFormatter,
55+
hh: translator.relativeTimeFormatter,
56+
d: translator.relativeTimeFormatter,
57+
dd: translator.relativeTimeFormatter,
58+
M: translator.relativeTimeFormatter,
59+
MM: translator.relativeTimeFormatter,
60+
y: translator.relativeTimeFormatter,
61+
yy: translator.relativeTimeFormatter
2662
},
2763
ordinal: n => `${n}.`,
2864
formats: {
2965
LT: 'H:mm',
3066
LTS: 'H:mm:ss',
31-
L: 'DD.MM.YYYY',
32-
LL: 'D. MMMM YYYY',
33-
LLL: 'D. MMMM YYYY H:mm',
34-
LLLL: 'dddd, D. MMMM YYYY H:mm'
67+
L: 'D. M. YYYY.',
68+
LL: 'D. MMMM YYYY.',
69+
LLL: 'D. MMMM YYYY. H:mm',
70+
LLLL: 'dddd, D. MMMM YYYY. H:mm'
3571
}
3672
}
3773

src/locale/sr.js

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,42 @@
11
// Serbian [sr]
22
import dayjs from 'dayjs'
33

4+
const translator = {
5+
words: {
6+
m: ['jedan minut', 'jednog minuta'],
7+
mm: ['%d minut', '%d minuta', '%d minuta'],
8+
h: ['jedan sat', 'jednog sata'],
9+
hh: ['%d sat', '%d sata', '%d sati'],
10+
d: ['jedan dan', 'jednog dana'],
11+
dd: ['%d dan', '%d dana', '%d dana'],
12+
M: ['jedan mesec', 'jednog meseca'],
13+
MM: ['%d mesec', '%d meseca', '%d meseci'],
14+
y: ['jednu godinu', 'jedne godine'],
15+
yy: ['%d godinu', '%d godine', '%d godina']
16+
},
17+
correctGrammarCase(number, wordKey) {
18+
if (number % 10 >= 1 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20)) {
19+
return number % 10 === 1 ? wordKey[0] : wordKey[1]
20+
}
21+
return wordKey[2]
22+
},
23+
relativeTimeFormatter(number, withoutSuffix, key, isFuture) {
24+
const wordKey = translator.words[key]
25+
26+
if (key.length === 1) {
27+
// Nominativ
28+
if (key === 'y' && withoutSuffix) return 'jedna godina'
29+
return isFuture || withoutSuffix ? wordKey[0] : wordKey[1]
30+
}
31+
32+
const word = translator.correctGrammarCase(number, wordKey)
33+
// Nominativ
34+
if (key === 'yy' && withoutSuffix && word === '%d godinu') return `${number} godina`
35+
36+
return word.replace('%d', number)
37+
}
38+
}
39+
440
const locale = {
541
name: 'sr',
642
weekdays: 'Nedelja_Ponedeljak_Utorak_Sreda_Četvrtak_Petak_Subota'.split('_'),
@@ -12,26 +48,26 @@ const locale = {
1248
relativeTime: {
1349
future: 'za %s',
1450
past: 'pre %s',
15-
s: 'sekunda',
16-
m: 'minut',
17-
mm: '%d minuta',
18-
h: 'sat',
19-
hh: '%d sati',
20-
d: 'dan',
21-
dd: '%d dana',
22-
M: 'mesec',
23-
MM: '%d meseci',
24-
y: 'godina',
25-
yy: '%d godine'
51+
s: 'nekoliko sekundi',
52+
m: translator.relativeTimeFormatter,
53+
mm: translator.relativeTimeFormatter,
54+
h: translator.relativeTimeFormatter,
55+
hh: translator.relativeTimeFormatter,
56+
d: translator.relativeTimeFormatter,
57+
dd: translator.relativeTimeFormatter,
58+
M: translator.relativeTimeFormatter,
59+
MM: translator.relativeTimeFormatter,
60+
y: translator.relativeTimeFormatter,
61+
yy: translator.relativeTimeFormatter
2662
},
2763
ordinal: n => `${n}.`,
2864
formats: {
2965
LT: 'H:mm',
3066
LTS: 'H:mm:ss',
31-
L: 'DD.MM.YYYY',
32-
LL: 'D. MMMM YYYY',
33-
LLL: 'D. MMMM YYYY H:mm',
34-
LLLL: 'dddd, D. MMMM YYYY H:mm'
67+
L: 'D. M. YYYY.',
68+
LL: 'D. MMMM YYYY.',
69+
LLL: 'D. MMMM YYYY. H:mm',
70+
LLLL: 'dddd, D. MMMM YYYY. H:mm'
3571
}
3672
}
3773

test/locale/sr-cyrl.test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import MockDate from 'mockdate'
2+
import dayjs from '../../src'
3+
import relativeTime from '../../src/plugin/relativeTime'
4+
import '../../src/locale/sr-cyrl'
5+
6+
dayjs.extend(relativeTime)
7+
8+
beforeEach(() => {
9+
MockDate.set(new Date())
10+
})
11+
12+
afterEach(() => {
13+
MockDate.reset()
14+
})
15+
16+
it('Serbian cyrillic locale relative time in past and future', () => {
17+
const cases = [
18+
[1, 's', 'за неколико секунди', 'неколико секунди'],
19+
[-1, 's', 'пре неколико секунди', 'неколико секунди'],
20+
[4, 's', 'за неколико секунди', 'неколико секунди'],
21+
[1, 'm', 'за један минут', 'један минут'],
22+
[-1, 'm', 'пре једног минута', 'један минут'],
23+
[4, 'm', 'за 4 минута', '4 минута'],
24+
[5, 'm', 'за 5 минута', '5 минута'],
25+
[21, 'm', 'за 21 минут', '21 минут'],
26+
[1, 'h', 'за један сат', 'један сат'],
27+
[-1, 'h', 'пре једног сата', 'један сат'],
28+
[4, 'h', 'за 4 сата', '4 сата'],
29+
[5, 'h', 'за 5 сати', '5 сати'],
30+
[21, 'h', 'за 21 сат', '21 сат'],
31+
[1, 'd', 'за један дан', 'један дан'],
32+
[-1, 'd', 'пре једног дана', 'један дан'],
33+
[4, 'd', 'за 4 дана', '4 дана'],
34+
[5, 'd', 'за 5 дана', '5 дана'],
35+
[21, 'd', 'за 21 дан', '21 дан'],
36+
[1, 'M', 'за један месец', 'један месец'],
37+
[-1, 'M', 'пре једног месеца', 'један месец'],
38+
[4, 'M', 'за 4 месеца', '4 месеца'],
39+
[5, 'M', 'за 5 месеци', '5 месеци'],
40+
[10, 'M', 'за 10 месеци', '10 месеци'],
41+
[1, 'y', 'за једну годину', 'једна година'],
42+
[-1, 'y', 'пре једне године', 'једна година'],
43+
[4, 'y', 'за 4 године', '4 године'],
44+
[5, 'y', 'за 5 година', '5 година'],
45+
[21, 'y', 'за 21 годину', '21 година']
46+
]
47+
48+
cases.forEach((c) => {
49+
expect(dayjs().add(c[0], c[1]).locale('sr-cyrl').fromNow()).toBe(c[2])
50+
expect(dayjs().add(c[0], c[1]).locale('sr-cyrl').fromNow(true)).toBe(c[3])
51+
// TODO: compare to momentjs once logic and grammar are fixed there
52+
})
53+
})
54+

test/locale/sr.test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import MockDate from 'mockdate'
2+
import dayjs from '../../src'
3+
import relativeTime from '../../src/plugin/relativeTime'
4+
import '../../src/locale/sr'
5+
6+
dayjs.extend(relativeTime)
7+
8+
beforeEach(() => {
9+
MockDate.set(new Date())
10+
})
11+
12+
afterEach(() => {
13+
MockDate.reset()
14+
})
15+
16+
it('Serbian locale relative time in past and future', () => {
17+
const cases = [
18+
[1, 's', 'za nekoliko sekundi', 'nekoliko sekundi'],
19+
[-1, 's', 'pre nekoliko sekundi', 'nekoliko sekundi'],
20+
[4, 's', 'za nekoliko sekundi', 'nekoliko sekundi'],
21+
[1, 'm', 'za jedan minut', 'jedan minut'],
22+
[-1, 'm', 'pre jednog minuta', 'jedan minut'],
23+
[4, 'm', 'za 4 minuta', '4 minuta'],
24+
[5, 'm', 'za 5 minuta', '5 minuta'],
25+
[21, 'm', 'za 21 minut', '21 minut'],
26+
[1, 'h', 'za jedan sat', 'jedan sat'],
27+
[-1, 'h', 'pre jednog sata', 'jedan sat'],
28+
[4, 'h', 'za 4 sata', '4 sata'],
29+
[5, 'h', 'za 5 sati', '5 sati'],
30+
[21, 'h', 'za 21 sat', '21 sat'],
31+
[1, 'd', 'za jedan dan', 'jedan dan'],
32+
[-1, 'd', 'pre jednog dana', 'jedan dan'],
33+
[4, 'd', 'za 4 dana', '4 dana'],
34+
[5, 'd', 'za 5 dana', '5 dana'],
35+
[21, 'd', 'za 21 dan', '21 dan'],
36+
[1, 'M', 'za jedan mesec', 'jedan mesec'],
37+
[-1, 'M', 'pre jednog meseca', 'jedan mesec'],
38+
[4, 'M', 'za 4 meseca', '4 meseca'],
39+
[5, 'M', 'za 5 meseci', '5 meseci'],
40+
[10, 'M', 'za 10 meseci', '10 meseci'],
41+
[1, 'y', 'za jednu godinu', 'jedna godina'],
42+
[-1, 'y', 'pre jedne godine', 'jedna godina'],
43+
[4, 'y', 'za 4 godine', '4 godine'],
44+
[5, 'y', 'za 5 godina', '5 godina'],
45+
[21, 'y', 'za 21 godinu', '21 godina']
46+
]
47+
48+
cases.forEach((c) => {
49+
// With suffix
50+
expect(dayjs().add(c[0], c[1]).locale('sr').fromNow()).toBe(c[2])
51+
// Without suffix
52+
expect(dayjs().add(c[0], c[1]).locale('sr').fromNow(true)).toBe(c[3])
53+
// TODO: compare to momentjs once logic and grammar are fixed there
54+
})
55+
})

0 commit comments

Comments
 (0)