diff --git a/packages/message-compiler/src/tokenizer.ts b/packages/message-compiler/src/tokenizer.ts index 6fd233577..594b70519 100644 --- a/packages/message-compiler/src/tokenizer.ts +++ b/packages/message-compiler/src/tokenizer.ts @@ -420,6 +420,16 @@ export function createTokenizer( return takeChar(scnr, closure) } + // NOTE: It is assumed that this function is used in conjunction with `takeIdentifierChar` for named. + // TODO: we need to refactor this function ... + function takeNamedIdentifierChar(scnr: Scanner): string | undefined | null { + const closure = (ch: string) => { + const cc = ch.charCodeAt(0) + return cc === 45 // - + } + return takeChar(scnr, closure) + } + function takeDigit(scnr: Scanner): string | undefined | null { const closure = (ch: string) => { const cc = ch.charCodeAt(0) @@ -503,7 +513,8 @@ export function createTokenizer( let ch: string | undefined | null = '' let name = '' - while ((ch = takeIdentifierChar(scnr))) { + // eslint-disable-next-line no-cond-assign + while ((ch = takeIdentifierChar(scnr) || takeNamedIdentifierChar(scnr))) { name += ch } diff --git a/packages/message-compiler/test/tokenizer/named.test.ts b/packages/message-compiler/test/tokenizer/named.test.ts index 6bf9f7b57..6dba32a48 100644 --- a/packages/message-compiler/test/tokenizer/named.test.ts +++ b/packages/message-compiler/test/tokenizer/named.test.ts @@ -310,6 +310,57 @@ test('new line', () => { }) }) +test('include hyphen', () => { + const tokenizer = createTokenizer('My Nickname is {nick-name}.') + expect(tokenizer.nextToken()).toEqual({ + type: TokenTypes.Text, + value: 'My Nickname is ', + loc: { + start: { line: 1, column: 1, offset: 0 }, + end: { line: 1, column: 16, offset: 15 } + } + }) + expect(tokenizer.nextToken()).toEqual({ + type: TokenTypes.BraceLeft, + value: '{', + loc: { + start: { line: 1, column: 16, offset: 15 }, + end: { line: 1, column: 17, offset: 16 } + } + }) + expect(tokenizer.nextToken()).toEqual({ + type: TokenTypes.Named, + value: 'nick-name', + loc: { + start: { line: 1, column: 17, offset: 16 }, + end: { line: 1, column: 26, offset: 25 } + } + }) + expect(tokenizer.nextToken()).toEqual({ + type: TokenTypes.BraceRight, + value: '}', + loc: { + start: { line: 1, column: 26, offset: 25 }, + end: { line: 1, column: 27, offset: 26 } + } + }) + expect(tokenizer.nextToken()).toEqual({ + type: TokenTypes.Text, + value: '.', + loc: { + start: { line: 1, column: 27, offset: 26 }, + end: { line: 1, column: 28, offset: 27 } + } + }) + expect(tokenizer.nextToken()).toEqual({ + type: TokenTypes.EOF, + loc: { + start: { line: 1, column: 28, offset: 27 }, + end: { line: 1, column: 28, offset: 27 } + } + }) +}) + describe('modulo cases', () => { test('basic named: hi %{name} !', () => { const tokenizer = createTokenizer('hi %{name} !') diff --git a/packages/vue-i18n-core/test/issues.test.ts b/packages/vue-i18n-core/test/issues.test.ts index 550e288cb..b9edbf81c 100644 --- a/packages/vue-i18n-core/test/issues.test.ts +++ b/packages/vue-i18n-core/test/issues.test.ts @@ -1358,3 +1358,21 @@ test('issue #1738', async () => { expect(wrapper.find('#te1')?.textContent).toEqual(`true - expected true`) expect(wrapper.find('#te2')?.textContent).toEqual(`true - expected true`) }) + +test('#1796', async () => { + const i18n = createI18n({ + locale: 'en', + messages: { + en: { + hello: 'hello world', + 'message-with-placeholder-using-hyphens': + 'My message with {placeholder-hyphens}.' + } + } + }) + expect( + i18n.global.t('message-with-placeholder-using-hyphens', { + 'placeholder-hyphens': i18n.global.t('hello') + }) + ).toEqual('My message with hello world.') +}) diff --git a/spec/syntax.ebnf b/spec/syntax.ebnf index c285f0f39..326aaf635 100644 --- a/spec/syntax.ebnf +++ b/spec/syntax.ebnf @@ -1,5 +1,5 @@ (* - * Inltify message syntax v0.2 + * Inltify message syntax v0.3 * (vue-i18n compatible) *) @@ -14,7 +14,7 @@ Message ::= (Text? (Placeholder | Linked)? Text?)+; Text ::= TextChar+; Placeholder ::= Named | List | StringLiteral; Modulo ::= "%"; -Named ::= Modulo? "{" Space? (Identifier) Space? "}"; +Named ::= Modulo? "{" Space? (NamedIdentifier) Space? "}"; List ::= "{" Space? (NumberLiteral) Space? "}"; Linked ::= "@" (LinkedModifier)? LinkedDelimiter LinkedRefer; LinkedRefer ::= LinkedKey | Placeholder; @@ -41,6 +41,7 @@ Digits ::= [0-9]+; (* identifier *) Identifier ::= [a-zA-Z_] [a-zA-Z0-9_$]*; +NamedIdentifier ::= [a-zA-Z_] [a-zA-Z0-9_\-$]*; (* whitespaces *) SpaceInline ::= #x0020; (* "\u0020" *)