Search code examples
vue.jsinternationalization

@intlify/unplugin-vue-i18n imports locales json files in weird structure


I would like to internationalize a simple vue application and I utilized vue-i18n and unplugin-vue-i18n. I expect the following snippet in vue 3 api composition renders the word John:

<template>
   ...
   <h2>{{ $tm('name') }}</h2>
   ...
<template>

but it returns weird json string as follows:

{ "type": 0, "start": 0, "end": 3, "loc": { "start": { "line": 1, "column": 1, "offset": 0 }, "end": { "line": 1, "column": 4, "offset": 3 }, "source": "John" }, "body": { "type": 2, "start": 0, "end": 3, "loc": { "start": { "line": 1, "column": 1, "offset": 0 }, "end": { "line": 1, "column": 4, "offset": 3 } }, "items": [ { "type": 3, "start": 0, "end": 3, "loc": { "start": { "line": 1, "column": 1, "offset": 0 }, "end": { "line": 1, "column": 4, "offset": 3 } } } ], "static": "John" } }

Here are the config files:

package.json:

...
  "dependencies": {
    "@headlessui/vue": "^1.7.23",
    "@intlify/unplugin-vue-i18n": "^5.2.0",
    "pinia": "^2.1.7",
    "vue": "^3.5.9",
    "vue-i18n": "^9.14.1",
    "vue-router": "^4.3.3"
  },
...

vite.config.ts:

import { fileURLToPath, URL } from 'node:url'
import { resolve, dirname } from "node:path";
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";

export default defineConfig({
  server: {
    port: 5174
  },
  plugins: [
    vue(),
    VueI18nPlugin({
      // runtimeOnly: false,
      include: resolve(dirname(fileURLToPath(import.meta.url)), './src/locales/**'),
      // strictMessage: false
    })
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

main.ts:

import './assets/main.css';

import { createApp } from 'vue';
import { createI18n } from 'vue-i18n';
import { createPinia } from 'pinia';

import App from './App.vue';
import router from './router';
import messages from '@intlify/unplugin-vue-i18n/messages';

const i18n = createI18n({
  legacy: false,
  globalInjection: true,
  locale: "en",
  fallbackLocale: "en",
  availableLocales: ["en", "fi"],
   messages
});

const app = createApp(App)

app.use(i18n)
app.use(createPinia())
app.use(router)

app.mount('#app')

and en.json file:

{
  "name": "John"
}

Would someone please help me to fix it.

Update:
I've created project using the following commands (for reproducing the problom)

npm init vue@latest test
npm install vue-i18n@latest @intlify/unplugin-vue-i18n@latest

Then I set the above configuration. That's it. Also I did according to this and this articles.


Solution

  • As explained in the documentation, tm returns a message which needs to be resolved to a string with rt. This can be optional when a message is a string, but this isn't so when they are compiled at build time with @intlify/unplugin-vue-i18n

    The correct usage is:

    $rt($tm('name'))
    

    Despite that tm correctly returns compiled message object that is listed in the question, it's not recognized by rt, this causes this error:

    Uncaught SyntaxError: Not support non-string message
        at createCompileError (message-compiler.esm-browser.js:116:19)
        at createCoreError (core-base.mjs:485:12)
        at compileToFunction (core-base.mjs:1008:15)
        at compileMessageFormat (core-base.mjs:1324:17)
        at translate (core-base.mjs:1168:11)
        at vue-i18n.mjs:559:48
        at wrapWithDeps (vue-i18n.mjs:504:19)
        at t (vue-i18n.mjs:559:16)
        at Proxy.rt (vue-i18n.mjs:567:16)
        at Proxy._sfc_render (HelloWorld.vue:10:17)
    

    The problem here is version mismatch. Despite that this doesn't seem to be mentioned in the documentation, and major versions of @intlify/unplugin-vue-i18n and vue-i18n aren't synchronized, the timeline of new version releases is similar. It should be either @intlify/unplugin-vue-i18n@5 and vue-i18n@10, or @intlify/unplugin-vue-i18n@4 and vue-i18n@9, then it becomes workable. Since both major versions of the packages have just been released, this could be a bug.