6

This is my code, that works without problems:

import { createI18n } from 'vue-i18n'
import messages from './components/json/foo/foo_messages.json'

const app = createApp(App)
installI18n(app)

const i18n = createI18n({
  locale: 'ru',
  messages
})

app
  .use(i18n)
  .use(vuetify)
  .mount('#app')

Now I need to load messages also from ./components/json/bar/bar_messages.json. I tried to do this way:

import { createI18n } from 'vue-i18n'
import foo_msg from './components/json/foo/foo_messages.json'
import bar_msg from './components/json/bar/bar_messages.json'

const app = createApp(App)
installI18n(app)

const i18n = createI18n({
  locale: 'ru',
  messages: {foo_msg, bar_msg}
})

app
  .use(i18n)
  .use(vuetify)
  .mount('#app')

But it didn't work. Could anyone say how to do it?

EDIT: This is my foo json file

{
  "ru": {
    "header": {
      "hello": "Привет"
    }
  },
  "en": {
    "header": {
      "hello": "Hello"
    }
  }
}

and this is bar json file

{
  "ru": {
    "footer": {
      "bye": "Пока"
    }
  },
  "en": {
    "footer": {
      "bye": "Goodbye"
    }
  }
}
Pavel_K
  • 10,748
  • 13
  • 73
  • 186
  • how do the json files look like? – Boussadjra Brahim Jan 03 '22 at 15:05
  • @BoussadjraBrahim Please, see the edit. – Pavel_K Jan 03 '22 at 15:09
  • Objects can be merged like this: [How can I merge properties of two JavaScript objects dynamically?](https://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically) – Peter Krebs Jan 03 '22 at 15:32
  • @PeterKrebs This will not work as all top level properties (language identifiers) will overwrite other top-level properties from different sources... – Michal Levý Jan 03 '22 at 15:38
  • @MichalLevý Look at the answers further down. For example a [recursive object merge](https://stackoverflow.com/a/383245/10441671). Code can always be adapted to ones needs, like keeping all properties and so on. But it is a good inspiration either way for an answer. – Peter Krebs Jan 03 '22 at 15:52
  • @PeterKrebs Ok I see, I wasn't reading all the solutions. Anyway, importing all translations into your main (and merging) is not a good solution for his use case – Michal Levý Jan 03 '22 at 16:01
  • @MichalLevý Yes I agree. It's just that marking it as duplicate lets mee choose the question, but not a specific answer I don't think. In either case, nice idea with the `i18n` tags - have to try that myself. – Peter Krebs Jan 03 '22 at 16:05

2 Answers2

4

What you are trying to do is not very scalable. Given the format of the i18n JSON messages, you need to merge the input files to something like this:

{
  "ru": {
    "header": {
      "hello": "Привет"
    },
    "footer": {
      "bye": "Пока"
    }
  },
  "en": {
    "header": {
      "hello": "Hello"
    },
    "footer": {
      "bye": "Goodbye"
    }
  }
}

...this is definitely possible with JS but you must still import the JSON file for each component in your main.js which is tedious and error prone

Did you consider using vue-i18n custom blocks in your components? You can even keep the translations in external JSON files and use a custom block like <i18n src="./myLang.json"></i18n>

this is much better approach but if you stil want to use yours, here is a simple code how to merge all translation files (objects imported from JSON) into a single object usable by vue-i18n:

// import foo_msg from './components/json/foo/foo_messages.json'
const foo_msg = {
  "ru": {
    "header": {
      "hello": "Привет"
    }
  },
  "en": {
    "header": {
      "hello": "Hello"
    }
  }
}

// import bar_msg from './components/json/bar/bar_messages.json'
const bar_msg = {
  "ru": {
    "footer": {
      "bye": "Пока"
    }
  },
  "en": {
    "footer": {
      "bye": "Goodbye"
    }
  }
}

const sources = [foo_msg, bar_msg]
const messages = sources.reduce((acc, source) => {
  for(key in source) {
    acc[key] = { ...(acc[key] || {}), ...source[key] }
  }
  return acc
},{})

console.log(messages)
Michal Levý
  • 33,064
  • 4
  • 68
  • 86
0

The accepted solution is already a good solution, but if you assist to use .json files to translate text. Here is my solution.

Use vue-cli to add i18n dependency, it would generate all the requirement files that we need.

vue add vue-i18n

It would generate the locales folder inside src, which it stores all the translation json files. Then it would generate couple env variable on .env file and a i18n.js file

here is the i18n.js it generates

import { createI18n } from 'vue-i18n'

/**
 * Load locale messages
 *
 * The loaded `JSON` locale messages is pre-compiled by `@intlify/vue-i18n-loader`, which is integrated into `vue-cli-plugin-i18n`.
 * See: https://github.com/intlify/vue-i18n-loader#rocket-i18n-resource-pre-compilation
 */
function loadLocaleMessages() {
  const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
  const messages = {}
  locales.keys().forEach(key => {
    const matched = key.match(/([A-Za-z0-9-_]+)\./i)
    if (matched && matched.length > 1) {
      const locale = matched[1]
      messages[locale] = locales(key).default
    }
  })
  return messages
}

export default createI18n({
  locale: process.env.VUE_APP_I18N_LOCALE || 'en',
  fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
  messages: loadLocaleMessages()
})

In our main.js, i had seen that vue has already add the component for me

import i18n from './i18n'

const app = createApp(App).use(i18n)

*Edit I am using vite for building vue project, the loadLocaleMessages does not work in my case. I made some modification. It needs to manually import all the json files, but i did not find any alternative solution.

I also change the env variable with 'VITE' prefix, and process.env to import.meta.env.

// import all the json files
import en from './locales/en.json'
import zh from './locales/zh.json'

/**
 * Load locale messages
 *
 * The loaded `JSON` locale messages is pre-compiled by `@intlify/vue-i18n-loader`, which is integrated into `vue-cli-plugin-i18n`.
 * See: https://github.com/intlify/vue-i18n-loader#rocket-i18n-resource-pre-compilation
 */
function loadLocaleMessages() {
  const locales = [{ en: en }, { zh: zh }]
  const messages = {}
  locales.forEach(lang => {
    const key = Object.keys(lang)
    messages[key] = lang[key] 
  })
  return messages
}
export default createI18n({
  locale: import.meta.env.VITE_APP_I18N_LOCALE || 'en',
  fallbackLocale: import.meta.env.VITE_APP_I18N_FALLBACK_LOCALE || 'en',
  messages: loadLocaleMessages()
})

Jimmy lau
  • 173
  • 1
  • 8