Repository: intlify/vue-i18n-next (ts)

Issue:
Linked messages doesn't support chinese character as key
### Reporting a bug?

when use this feature like `audit @:状态`, it won't work as expected. 

### Expected behavior

Linked message with chinese as key can work.

### Reproduction

https://stackblitz.com/edit/vitejs-vite-ju6nqb?file=src%2Fmain.ts

### System Info

```shell
System:
    OS: Windows 11 10.0.22621
    CPU: (8) x64 Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz
    Memory: 10.39 GB / 31.89 GB
  Binaries:
    Node: 21.7.3 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.18 - ~\env\npmDownload\packages\yarn.CMD
    npm: 9.6.2 - ~\env\npmDownload\packages\npm.CMD
    pnpm: 8.13.1 - ~\env\npmDownload\pnpm\pnpm.EXE
  Browsers:
    Edge: Chromium (123.0.2420.97)
    Internet Explorer: 11.0.22621.1
```


### Screenshot

![image](https://github.com/intlify/vue-i18n-next/assets/30925732/41f0171d-996e-40b5-b344-9a5e70e98baf)


### Additional context

Use our native language as the translation key can help read page code more easily without some IDE a11y plugin. When use chinese , in most case it worked, now I found only this feature get some problem.

_No response_

### Validations

- [X] Read the [Contributing Guidelines](https://github.com/intlify/vue-i18n-next/blob/master/.github/CONTRIBUTING.md)
- [X] Read the [Documentation](https://vue-i18n.intlify.dev/)
- [X] Check that there isn't [already an issue](https://github.com/intlify/vue-i18n-next/issues) that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a [GitHub Discussions](https://github.com/intlify/vue-i18n-next/discussions)

Pull request:
fix(message-compiler): cannot resolve none-identifier characters at linked key
resolve #1809 

Patch:
diff --git a/packages/message-compiler/src/tokenizer.ts b/packages/message-compiler/src/tokenizer.ts
index b6b6a793..1e12face 100644
--- a/packages/message-compiler/src/tokenizer.ts
+++ b/packages/message-compiler/src/tokenizer.ts
@@ -321,7 +321,7 @@ export function createTokenizer(
         return fn()
       } else {
         // other characters
-        return isIdentifierStart(ch)
+        return isTextStart(scnr, false)
       }
     }
 
@@ -680,7 +680,7 @@ export function createTokenizer(
   }
 
   function readLinkedRefer(scnr: Scanner): string {
-    const fn = (detect = false, buf: string): string => {
+    const fn = (buf: string): string => {
       const ch = scnr.currentChar()
       if (
         ch === TokenChars.BraceLeft ||
@@ -697,15 +697,15 @@ export function createTokenizer(
       } else if (ch === NEW_LINE || ch === DOT) {
         buf += ch
         scnr.next()
-        return fn(detect, buf)
+        return fn(buf)
       } else {
         buf += ch
         scnr.next()
-        return fn(true, buf)
+        return fn(buf)
       }
     }
 
-    return fn(false, '')
+    return fn('')
   }
 
   function readPlural(scnr: Scanner): string {
diff --git a/spec/syntax.ebnf b/spec/syntax.ebnf
index 326aaf63..f09313a3 100644
--- a/spec/syntax.ebnf
+++ b/spec/syntax.ebnf
@@ -1,5 +1,5 @@
 (*
- * Inltify message syntax v0.3
+ * Inltify message syntax v0.4.0
  * (vue-i18n compatible)
  *)
 
@@ -18,7 +18,7 @@ Named ::= Modulo? "{" Space? (NamedIdentifier) Space? "}";
 List ::= "{" Space? (NumberLiteral) Space? "}";
 Linked ::= "@" (LinkedModifier)? LinkedDelimiter LinkedRefer;
 LinkedRefer ::= LinkedKey | Placeholder;
-LinkedKey ::= Identifier;
+LinkedKey ::= Text;
 LinkedModifier ::= LinkedDot Identifier;
 LinkedDelimiter ::= ":";
 LinkedDot ::= ".";


Test patch:
diff --git a/packages/message-compiler/test/__snapshots__/parser.test.ts.snap b/packages/message-compiler/test/__snapshots__/parser.test.ts.snap
index f7f09c88..f134f37b 100644
--- a/packages/message-compiler/test/__snapshots__/parser.test.ts.snap
+++ b/packages/message-compiler/test/__snapshots__/parser.test.ts.snap
@@ -525,28 +525,6 @@ exports[`parse > linked key paren error with modifier: "@.lower:(foo)" 1`] = `
 }
 `;
 
-exports[`parse > linked key paren error with modifier: "@.lower:(foo)" errors 1`] = `
-[
-  {
-    "code": 13,
-    "domain": "parser",
-    "location": {
-      "end": {
-        "column": 14,
-        "line": 1,
-        "offset": 13,
-      },
-      "start": {
-        "column": 8,
-        "line": 1,
-        "offset": 7,
-      },
-    },
-    "message": "Unexpected empty linked key",
-  },
-]
-`;
-
 exports[`parse > linked key paren error: "@:(foo)" 1`] = `
 {
   "body": {
@@ -640,28 +618,6 @@ exports[`parse > linked key paren error: "@:(foo)" 1`] = `
 }
 `;
 
-exports[`parse > linked key paren error: "@:(foo)" errors 1`] = `
-[
-  {
-    "code": 13,
-    "domain": "parser",
-    "location": {
-      "end": {
-        "column": 8,
-        "line": 1,
-        "offset": 7,
-      },
-      "start": {
-        "column": 2,
-        "line": 1,
-        "offset": 1,
-      },
-    },
-    "message": "Unexpected empty linked key",
-  },
-]
-`;
-
 exports[`parse > linked key with named and modifier: "hi @._upper:{_name} !" 1`] = `
 {
   "body": {
@@ -2024,16 +1980,6 @@ exports[`parser options > location disable > linked key paren error with modifie
 }
 `;
 
-exports[`parser options > location disable > linked key paren error with modifier: "@.lower:(foo)" errors 1`] = `
-[
-  {
-    "code": 13,
-    "domain": "parser",
-    "message": "Unexpected empty linked key",
-  },
-]
-`;
-
 exports[`parser options > location disable > linked key paren error: "@:(foo)" 1`] = `
 {
   "body": {
@@ -2056,16 +2002,6 @@ exports[`parser options > location disable > linked key paren error: "@:(foo)" 1
 }
 `;
 
-exports[`parser options > location disable > linked key paren error: "@:(foo)" errors 1`] = `
-[
-  {
-    "code": 13,
-    "domain": "parser",
-    "message": "Unexpected empty linked key",
-  },
-]
-`;
-
 exports[`parser options > location disable > linked key with named and modifier: "hi @._upper:{_name} !" 1`] = `
 {
   "body": {
diff --git a/packages/message-compiler/test/__snapshots__/tokenizer.test.ts.snap b/packages/message-compiler/test/__snapshots__/tokenizer.test.ts.snap
index e0f31387..94eb379b 100644
--- a/packages/message-compiler/test/__snapshots__/tokenizer.test.ts.snap
+++ b/packages/message-compiler/test/__snapshots__/tokenizer.test.ts.snap
@@ -4370,6 +4370,90 @@ exports[`token analysis > "hi @:name !" tokens 1`] = `
 ]
 `;
 
+exports[`token analysis > "hi @:名前" tokens 1`] = `
+[
+  {
+    "loc": {
+      "end": {
+        "column": 4,
+        "line": 1,
+        "offset": 3,
+      },
+      "start": {
+        "column": 1,
+        "line": 1,
+        "offset": 0,
+      },
+    },
+    "type": 0,
+    "value": "hi ",
+  },
+  {
+    "loc": {
+      "end": {
+        "column": 5,
+        "line": 1,
+        "offset": 4,
+      },
+      "start": {
+        "column": 4,
+        "line": 1,
+        "offset": 3,
+      },
+    },
+    "type": 8,
+    "value": "@",
+  },
+  {
+    "loc": {
+      "end": {
+        "column": 6,
+        "line": 1,
+        "offset": 5,
+      },
+      "start": {
+        "column": 5,
+        "line": 1,
+        "offset": 4,
+      },
+    },
+    "type": 10,
+    "value": ":",
+  },
+  {
+    "loc": {
+      "end": {
+        "column": 8,
+        "line": 1,
+        "offset": 7,
+      },
+      "start": {
+        "column": 6,
+        "line": 1,
+        "offset": 5,
+      },
+    },
+    "type": 11,
+    "value": "名前",
+  },
+  {
+    "loc": {
+      "end": {
+        "column": 8,
+        "line": 1,
+        "offset": 7,
+      },
+      "start": {
+        "column": 8,
+        "line": 1,
+        "offset": 7,
+      },
+    },
+    "type": 14,
+  },
+]
+`;
+
 exports[`token analysis > "hi @\\n. upper\\n:  {'name'}\\n !" errors 1`] = `
 [
   {
@@ -10520,6 +10604,30 @@ exports[`tokenize options: location disable > "hi @:name !" tokens 1`] = `
 ]
 `;
 
+exports[`tokenize options: location disable > "hi @:名前" tokens 1`] = `
+[
+  {
+    "type": 0,
+    "value": "hi ",
+  },
+  {
+    "type": 8,
+    "value": "@",
+  },
+  {
+    "type": 10,
+    "value": ":",
+  },
+  {
+    "type": 11,
+    "value": "名前",
+  },
+  {
+    "type": 14,
+  },
+]
+`;
+
 exports[`tokenize options: location disable > "hi @\\n. upper\\n:  {'name'}\\n !" errors 1`] = `
 [
   {
diff --git a/packages/message-compiler/test/tokenizer.test.ts b/packages/message-compiler/test/tokenizer.test.ts
index 01db8b8b..303c3037 100644
--- a/packages/message-compiler/test/tokenizer.test.ts
+++ b/packages/message-compiler/test/tokenizer.test.ts
@@ -80,6 +80,7 @@ const CASES = [
   `hi @.upper\n{name} !`,
   `hi @.upper {name} !`,
   `hi @:\nname !`,
+  `hi @:名前`,
   `hi @: {'name'} !`,
   `hi @\n. upper\n:  {'name'}\n !`,
   ` | | |`,
diff --git a/packages/vue-i18n-core/test/issues.test.ts b/packages/vue-i18n-core/test/issues.test.ts
index c635eef9..a742fe40 100644
--- a/packages/vue-i18n-core/test/issues.test.ts
+++ b/packages/vue-i18n-core/test/issues.test.ts
@@ -1437,3 +1437,16 @@ test('#1796', async () => {
     })
   ).toEqual('My message with hello world.')
 })
+
+test('#1809', async () => {
+  const i18n = createI18n({
+    locale: 'en',
+    messages: {
+      en: {
+        hi: 'hi @:名前',
+        名前: 'kazupon'
+      }
+    }
+  })
+  expect(i18n.global.t('hi')).toEqual('hi kazupon')
+})