Skip to content

Commit c0eef25

Browse files
authored
fix: cannot apply custom pluralization rules (#357)
* fix: cannot apply custom pluralization rules * update
1 parent 2781357 commit c0eef25

File tree

6 files changed

+194
-15
lines changed

6 files changed

+194
-15
lines changed

packages/vue-i18n/src/composer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ export function createComposer<
11191119
: {} as LinkedModifiers<Message>
11201120

11211121
// pluralRules
1122-
const _pluralRules = options.pluralRules
1122+
const _pluralRules = options.pluralRules || (__root && __root.pluralRules)
11231123

11241124
// runtime context
11251125
// eslint-disable-next-line prefer-const

packages/vue-i18n/src/mixin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ function mergeToRoot<Messages, DateTimeFormats, NumberFormats>(
166166
root.escapeParameterHtml =
167167
options.escapeParameterHtml || root.escapeParameterHtml
168168
root.sync = options.sync || root.sync
169+
root.pluralizationRules =
170+
options.pluralizationRules || root.pluralizationRules
169171
const messages = getLocaleMessages<VueMessageType>(root.locale, {
170172
messages: options.messages,
171173
__i18n: options.__i18n

packages/vue-i18n/test/composer.test.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ jest.mock('@intlify/shared', () => ({
66
warn: jest.fn()
77
}))
88
import { isString, warn } from '@intlify/shared'
9+
import { pluralRules as _pluralRules } from './helper'
910

1011
import {
1112
createComposer,
@@ -310,16 +311,23 @@ describe('pluralRules', () => {
310311
expect(pluralRules).toEqual({})
311312
})
312313

313-
test('initialize at composer creating', () => {
314-
const _pluralRules = {
315-
en: () => {
316-
return 0
314+
test('specified', () => {
315+
const { pluralRules, t } = createComposer({
316+
locale: 'ru',
317+
pluralRules: _pluralRules,
318+
messages: {
319+
ru: {
320+
car: '0 машин | {n} машина | {n} машины | {n} машин'
321+
}
317322
}
318-
}
319-
const { pluralRules } = createComposer({
320-
pluralRules: _pluralRules
321323
})
324+
322325
expect(pluralRules).toEqual(_pluralRules)
326+
expect(t('car', 1)).toEqual('1 машина')
327+
expect(t('car', 2)).toEqual('2 машины')
328+
expect(t('car', 4)).toEqual('4 машины')
329+
expect(t('car', 12)).toEqual('12 машин')
330+
expect(t('car', 21)).toEqual('21 машина')
323331
})
324332
})
325333

packages/vue-i18n/test/helper.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ interface Wrapper {
3434
find: typeof document['querySelector']
3535
}
3636

37+
export const pluralRules = {
38+
ru: (choice: number, choicesLength: number): number => {
39+
if (choice === 0) {
40+
return 0
41+
}
42+
43+
const teen = choice > 10 && choice < 20
44+
const endsWithOne = choice % 10 === 1
45+
if (!teen && endsWithOne) {
46+
return 1
47+
}
48+
if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
49+
return 2
50+
}
51+
52+
return choicesLength < 4 ? 2 : 3
53+
}
54+
}
55+
3756
function initialProps<P>(propsOption: ComponentObjectPropsOptions<P>) {
3857
const copy = {} as ComponentPublicInstance<typeof propsOption>['$props']
3958

packages/vue-i18n/test/i18n.test.ts

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
getCurrentInstance,
99
ComponentOptions
1010
} from 'vue'
11-
import { mount } from './helper'
11+
import { mount, pluralRules as _pluralRules } from './helper'
1212
import { createI18n, useI18n } from '../src/index'
1313
import { errorMessages, I18nErrorCodes } from '../src/errors'
1414
import { Composer } from '../src/composer'
@@ -680,3 +680,145 @@ test('merge i18n custom blocks to global scope', async () => {
680680
foo: 'ふー!'
681681
})
682682
})
683+
684+
describe('custom pluralization', () => {
685+
test('legacy', async () => {
686+
const i18n = createI18n({
687+
locale: 'ru',
688+
pluralizationRules: _pluralRules,
689+
messages: {
690+
ru: {
691+
car: '0 машин | {n} машина | {n} машины | {n} машин'
692+
}
693+
}
694+
})
695+
696+
const App = defineComponent({
697+
template: `
698+
<p>{{ $tc('car', 1) }}</p>
699+
<p>{{ $tc('car', 2) }}</p>
700+
<p>{{ $tc('car', 4) }}</p>
701+
<p>{{ $tc('car', 12) }}</p>
702+
<p>{{ $tc('car', 21) }}</p>
703+
`
704+
})
705+
const { find } = await mount(App, i18n)
706+
await nextTick()
707+
expect(find('p:nth-child(1)')!.innerHTML).toEqual('1 машина')
708+
expect(find('p:nth-child(2)')!.innerHTML).toEqual('2 машины')
709+
expect(find('p:nth-child(3)')!.innerHTML).toEqual('4 машины')
710+
expect(find('p:nth-child(4)')!.innerHTML).toEqual('12 машин')
711+
expect(find('p:nth-child(5)')!.innerHTML).toEqual('21 машина')
712+
})
713+
714+
test('legacy + custom block', async () => {
715+
const i18n = createI18n({
716+
locale: 'ru',
717+
pluralizationRules: _pluralRules
718+
})
719+
720+
const App = defineComponent({
721+
__i18n: [
722+
{
723+
locale: 'ru',
724+
resource: {
725+
car: '0 машин | {n} машина | {n} машины | {n} машин'
726+
}
727+
}
728+
],
729+
template: `
730+
<p>{{ $tc('car', 1) }}</p>
731+
<p>{{ $tc('car', 2) }}</p>
732+
<p>{{ $tc('car', 4) }}</p>
733+
<p>{{ $tc('car', 12) }}</p>
734+
<p>{{ $tc('car', 21) }}</p>
735+
`
736+
})
737+
const { find } = await mount(App, i18n)
738+
await nextTick()
739+
expect(find('p:nth-child(1)')!.innerHTML).toEqual('1 машина')
740+
expect(find('p:nth-child(2)')!.innerHTML).toEqual('2 машины')
741+
expect(find('p:nth-child(3)')!.innerHTML).toEqual('4 машины')
742+
expect(find('p:nth-child(4)')!.innerHTML).toEqual('12 машин')
743+
expect(find('p:nth-child(5)')!.innerHTML).toEqual('21 машина')
744+
})
745+
746+
test('composition', async () => {
747+
const i18n = createI18n({
748+
legacy: false,
749+
locale: 'ru',
750+
pluralRules: _pluralRules,
751+
messages: {
752+
ru: {
753+
car: '0 машин | {n} машина | {n} машины | {n} машин'
754+
}
755+
}
756+
})
757+
758+
const App = defineComponent({
759+
setup() {
760+
const { t } = useI18n()
761+
return { t }
762+
},
763+
template: `
764+
<p>{{ t('car', 1) }}</p>
765+
<p>{{ t('car', 2) }}</p>
766+
<p>{{ t('car', 4) }}</p>
767+
<p>{{ t('car', 12) }}</p>
768+
<p>{{ t('car', 21) }}</p>
769+
`
770+
})
771+
const { find } = await mount(App, i18n)
772+
await nextTick()
773+
expect(find('p:nth-child(1)')!.innerHTML).toEqual('1 машина')
774+
expect(find('p:nth-child(2)')!.innerHTML).toEqual('2 машины')
775+
expect(find('p:nth-child(3)')!.innerHTML).toEqual('4 машины')
776+
expect(find('p:nth-child(4)')!.innerHTML).toEqual('12 машин')
777+
expect(find('p:nth-child(5)')!.innerHTML).toEqual('21 машина')
778+
})
779+
780+
test('composition + custom block', async () => {
781+
const i18n = createI18n({
782+
legacy: false,
783+
locale: 'ru'
784+
})
785+
786+
const App = defineComponent({
787+
setup() {
788+
const instance = getCurrentInstance()
789+
if (instance == null) {
790+
throw new Error()
791+
}
792+
const options = instance.type as ComponentOptions
793+
options.__i18n = [
794+
{
795+
locale: 'ru',
796+
resource: {
797+
car: '0 машин | {n} машина | {n} машины | {n} машин'
798+
}
799+
}
800+
]
801+
const { t } = useI18n({
802+
inheritLocale: true,
803+
useScope: 'local',
804+
pluralRules: _pluralRules
805+
})
806+
return { t }
807+
},
808+
template: `
809+
<p>{{ t('car', 1) }}</p>
810+
<p>{{ t('car', 2) }}</p>
811+
<p>{{ t('car', 4) }}</p>
812+
<p>{{ t('car', 12) }}</p>
813+
<p>{{ t('car', 21) }}</p>
814+
`
815+
})
816+
const { find } = await mount(App, i18n)
817+
await nextTick()
818+
expect(find('p:nth-child(1)')!.innerHTML).toEqual('1 машина')
819+
expect(find('p:nth-child(2)')!.innerHTML).toEqual('2 машины')
820+
expect(find('p:nth-child(3)')!.innerHTML).toEqual('4 машины')
821+
expect(find('p:nth-child(4)')!.innerHTML).toEqual('12 машин')
822+
expect(find('p:nth-child(5)')!.innerHTML).toEqual('21 машина')
823+
})
824+
})

packages/vue-i18n/test/legacy.test.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { errorMessages, I18nErrorCodes } from '../src/errors'
1313
import { getWarnMessage, I18nWarnCodes } from '../src/warnings'
1414
import { watchEffect, nextTick } from 'vue'
1515
import { compileToFunction, registerMessageCompiler } from '@intlify/core-base'
16+
import { pluralRules as _pluralRules } from './helper'
1617

1718
beforeEach(() => {
1819
registerMessageCompiler(compileToFunction)
@@ -135,15 +136,22 @@ test('postTranslation', () => {
135136
})
136137

137138
test('pluralizationRules', () => {
138-
const _pluralRules = {
139-
en: () => {
140-
return 0
141-
}
142-
}
143139
const i18n = createVueI18n({
144-
pluralizationRules: _pluralRules
140+
locale: 'ru',
141+
pluralizationRules: _pluralRules,
142+
messages: {
143+
ru: {
144+
car: '0 машин | {n} машина | {n} машины | {n} машин'
145+
}
146+
}
145147
})
148+
146149
expect(i18n.pluralizationRules).toEqual(_pluralRules)
150+
expect(i18n.tc('car', 1)).toEqual('1 машина')
151+
expect(i18n.tc('car', 2)).toEqual('2 машины')
152+
expect(i18n.tc('car', 4)).toEqual('4 машины')
153+
expect(i18n.tc('car', 12)).toEqual('12 машин')
154+
expect(i18n.tc('car', 21)).toEqual('21 машина')
147155
})
148156

149157
test('messages', () => {

0 commit comments

Comments
 (0)