Skip to content

Commit 35eea9d

Browse files
authored
fix: cannot resolve empty string on plural (#1985)
1 parent 4cb1a7d commit 35eea9d

File tree

10 files changed

+177
-1
lines changed

10 files changed

+177
-1
lines changed

packages/core-base/src/format.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function formatMessageParts<Message = string>(
5656
node: MessageNode
5757
): MessageFunctionReturn<Message> {
5858
const _static = node.s || node.static
59-
if (_static) {
59+
if (_static != null) {
6060
return ctx.type === 'text'
6161
? (_static as MessageFunctionReturn<Message>)
6262
: ctx.normalize([_static] as MessageType<Message>[])

packages/core-base/test/format.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,16 @@ describe('features', () => {
130130
})
131131
})
132132
})
133+
134+
describe('edge cases', () => {
135+
test('empty string in interpolation', () => {
136+
const { ast } = compile(`{''} | {n} test | {n} tests`, {
137+
jit: true
138+
})
139+
const msg = format(ast)
140+
const ctx = context({
141+
pluralIndex: 0
142+
})
143+
expect(msg(ctx)).toBe('')
144+
})
145+
})

packages/message-compiler/test/__snapshots__/compiler.test.ts.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,15 @@ exports[`edge cases > {_field} with the same value already exists. > code 1`] =
381381
}"
382382
`;
383383
384+
exports[`edge cases > empty literal string in interpolation > code 1`] = `
385+
"function __msg__ (ctx) {
386+
const { normalize: _normalize } = ctx
387+
return _normalize([
388+
""
389+
])
390+
}"
391+
`;
392+
384393
exports[`edge cases > hi %s ! > code 1`] = `
385394
"function __msg__ (ctx) {
386395
const { normalize: _normalize } = ctx

packages/message-compiler/test/__snapshots__/optimizer.test.ts.snap

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`empty literal string in interpolation 1`] = `
4+
{
5+
"body": {
6+
"items": [
7+
{
8+
"type": 9,
9+
},
10+
],
11+
"static": "",
12+
"type": 2,
13+
},
14+
"type": 0,
15+
}
16+
`;
17+
318
exports[`full text items: foo{'@'}domain.com 1`] = `
419
{
520
"body": {

packages/message-compiler/test/compiler.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,9 @@ describe('edge cases', () => {
151151
const { code } = compile(`%{nickname} %{action} issue %{code}`)
152152
expect(code).toMatchSnapshot('code')
153153
})
154+
155+
test('empty literal string in interpolation', () => {
156+
const { code } = compile(`{''}`)
157+
expect(code).toMatchSnapshot('code')
158+
})
154159
})

packages/message-compiler/test/optimizer.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,12 @@ test(`incldue dynamic node in pluarl: no apples | {0} apple | {n} apples`, () =>
6262
.filter(Boolean)
6363
expect(messages).toEqual(['no apples'])
6464
})
65+
66+
test('empty literal string in interpolation', () => {
67+
const parser = createParser({ location: false })
68+
const msg = `{''}`
69+
const ast = optimize(parser.parse(msg))
70+
71+
expect(ast).toMatchSnapshot()
72+
expect((ast.body as MessageNode).static).toBe('')
73+
})

packages/message-compiler/test/parser/__snapshots__/literal.test.ts.snap

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,64 @@ exports[`emoji > hi, {'😺'} ! 1`] = `
188188
}
189189
`;
190190

191+
exports[`empty string 1`] = `
192+
{
193+
"body": {
194+
"end": 4,
195+
"items": [
196+
{
197+
"end": 4,
198+
"loc": {
199+
"end": {
200+
"column": 5,
201+
"line": 1,
202+
"offset": 4,
203+
},
204+
"start": {
205+
"column": 1,
206+
"line": 1,
207+
"offset": 0,
208+
},
209+
},
210+
"start": 0,
211+
"type": 9,
212+
"value": "",
213+
},
214+
],
215+
"loc": {
216+
"end": {
217+
"column": 5,
218+
"line": 1,
219+
"offset": 4,
220+
},
221+
"start": {
222+
"column": 1,
223+
"line": 1,
224+
"offset": 0,
225+
},
226+
},
227+
"start": 0,
228+
"type": 2,
229+
},
230+
"end": 4,
231+
"loc": {
232+
"end": {
233+
"column": 5,
234+
"line": 1,
235+
"offset": 4,
236+
},
237+
"source": "{''}",
238+
"start": {
239+
"column": 1,
240+
"line": 1,
241+
"offset": 0,
242+
},
243+
},
244+
"start": 0,
245+
"type": 0,
246+
}
247+
`;
248+
191249
exports[`errors > include new line: hi { 'foo\\n' } 1`] = `
192250
{
193251
"body": {

packages/message-compiler/test/parser/literal.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,25 @@ describe('unicode', () => {
152152
})
153153
})
154154

155+
test('empty string', () => {
156+
const text = `{''}`
157+
const parser = createParser({ onError: spy })
158+
const ast = parser.parse(text)
159+
160+
expect(ast).toMatchSnapshot()
161+
expect(spy).not.toHaveBeenCalled()
162+
expect(ast.type).toEqual(NodeTypes.Resource)
163+
expect(ast.body.type).toEqual(NodeTypes.Message)
164+
const message = ast.body as MessageNode
165+
expect(message.items).toHaveLength(1)
166+
expect(message.items).toMatchObject([
167+
{
168+
type: NodeTypes.Literal,
169+
value: ''
170+
}
171+
])
172+
})
173+
155174
describe('intlify message syntax special characters', () => {
156175
const items = ['{', '}', '@', '|', '%']
157176
for (const ch of items) {

packages/message-compiler/test/tokenizer/literal.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,41 @@ describe('escapes', () => {
604604
}
605605
})
606606
})
607+
608+
test('empty string', () => {
609+
const tokenizer = createTokenizer(`{''}`)
610+
expect(tokenizer.nextToken()).toEqual({
611+
type: TokenTypes.BraceLeft,
612+
value: '{',
613+
loc: {
614+
start: { line: 1, column: 1, offset: 0 },
615+
end: { line: 1, column: 2, offset: 1 }
616+
}
617+
})
618+
expect(tokenizer.nextToken()).toEqual({
619+
type: TokenTypes.Literal,
620+
value: '',
621+
loc: {
622+
start: { line: 1, column: 2, offset: 1 },
623+
end: { line: 1, column: 4, offset: 3 }
624+
}
625+
})
626+
expect(tokenizer.nextToken()).toEqual({
627+
type: TokenTypes.BraceRight,
628+
value: '}',
629+
loc: {
630+
start: { line: 1, column: 4, offset: 3 },
631+
end: { line: 1, column: 5, offset: 4 }
632+
}
633+
})
634+
expect(tokenizer.nextToken()).toEqual({
635+
type: TokenTypes.EOF,
636+
loc: {
637+
start: { line: 1, column: 5, offset: 4 },
638+
end: { line: 1, column: 5, offset: 4 }
639+
}
640+
})
641+
})
607642
})
608643

609644
describe('errors', () => {

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,3 +1405,16 @@ test('#1912', async () => {
14051405

14061406
expect(el?.innerHTML).include(`No apples found`)
14071407
})
1408+
1409+
test('#1972', async () => {
1410+
const i18n = createI18n({
1411+
legacy: false,
1412+
locale: 'en',
1413+
messages: {
1414+
en: {
1415+
test: "{''} | {n} test | {n} tests"
1416+
}
1417+
}
1418+
})
1419+
expect(i18n.global.t('test', 0)).toEqual('')
1420+
})

0 commit comments

Comments
 (0)