kazupon / vue-i18n

:globe_with_meridians: Internationalization plugin for Vue.js
https://kazupon.github.io/vue-i18n/
MIT License
7.27k stars 860 forks source link

Using vue-i18n with json files instead #474

Open bonchevski opened 5 years ago

bonchevski commented 5 years ago

Hello, so I am about to start using the plugin ,however I failed finding examples or pointers on how to utilize .json Files. The gist of it is this: I have 7 .json files - all in different languages(translations of one another) which i need to switch between in my app (basically in Vuex , within the store mutations ,actions etc) The folder structure is standart and I have created a -- translations[folder] --example.json[files] I can't figure out how to point to these files to get my translations from them , a basic example would probably get me going , thank you in advance! Sorry for the inconvenience I may cause. Yours truly,

kimuraz commented 5 years ago

Something like this: https://github.com/kimuraz/vue-i18n-json ?

bonchevski commented 5 years ago

@kimuraz Not exactly... I think , cause I have 7 json files that need to be switched between using vuex mutations and cases for each language. I attempted a couple of times to fetch(),axios.get() the folder containing those files , although I might be completely wrong in my approach. The basic Idea is to switch between the jsons for each translation using state and functions within the store that call the change of state and commit it (action,mutations) etc. That's why I was thinking that calling it somehow with fetch,axios etc and storing the resp.data of each to the state would be optimal for those needs...

dekadentno commented 5 years ago

If I understood correctly, you want something like this: Two json files for two different languages:

// en.json
{
    "hello": "Hello"
}
// de.json
{
    "hello": "Guten tag"
}

In the same folder, you can add an index.js file:

// index.js
import en from './en.json'
import de from './de.json'

export const defaultLocale = 'en'

export const languages = {
  en: en,
  de: de,
}

After that, in your main.js file you should do this:

// main.js
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)

import { languages } from './i18n/index.js'
import { defaultLocale } from './i18n/index.js'
const messages = Object.assign(languages)

Vue.config.productionTip = false

var i18n = new VueI18n({
  locale: defaultLocale,
  fallbackLocale: 'de',
  messages
})

...
// don't forget to pass i18n to the Vue instance
new Vue({
  router,
  i18n,
  render: (h) => h(App),
}).$mount('#app')
bonchevski commented 5 years ago

If I understood correctly, you want something like this: Two json files for two different languages:

// en.json
{
    "hello": "Hello"
}
// de.json
{
    "hello": "Guten tag"
}

In the same folder, you can add an index.js file:

// index.js
import en from './en.json'
import hr from './de.json'

export const defaultLocale = 'en'

export const languages = {
  en: en,
  de: de,
}

After that, in your main.js file you should do this:

// main.js
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)

import { languages } from './i18n/index.js'
import { defaultLocale } from './i18n/index.js'
const messages = Object.assign(languages)

Vue.config.productionTip = false

var i18n = new VueI18n({
  locale: defaultLocale,
  fallbackLocale: 'de',
  messages
})

...
// don't forget to pass i18n to the Vue instance
new Vue({
  router,
  i18n,
  render: (h) => h(App),
}).$mount('#app')

Actally the json file looks like this { library : { "hello": "Hello from library" }, documents: { "hello": "Hello from documents" } i can't $t(documents.hello)

dekadentno commented 5 years ago

@bonchevski I think that's not a valid json file. Try it like this:

{
   "library":{
      "hello":"Hello from library"
   },
   "documents":{
      "hello":"Hello from documents"
   }
}
MaTToX3 commented 5 years ago

If I understood correctly, you want something like this: Two json files for two different languages:

// en.json
{
    "hello": "Hello"
}
// de.json
{
    "hello": "Guten tag"
}

In the same folder, you can add an index.js file:

// index.js
import en from './en.json'
import hr from './de.json'

export const defaultLocale = 'en'

export const languages = {
  en: en,
  de: de,
}

After that, in your main.js file you should do this:

// main.js
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)

import { languages } from './i18n/index.js'
import { defaultLocale } from './i18n/index.js'
const messages = Object.assign(languages)

Vue.config.productionTip = false

var i18n = new VueI18n({
  locale: defaultLocale,
  fallbackLocale: 'de',
  messages
})

...
// don't forget to pass i18n to the Vue instance
new Vue({
  router,
  i18n,
  render: (h) => h(App),
}).$mount('#app')

Exactly what I was looking for - works great paired together with .json files that are part of Laravel application!

ghost commented 5 years ago

From my perspective better create default locale folder near assets and in those folder store localization files. I mean it's better for developer and who will work with those files.

For big projects rather difficult to manage big files with localization and for that store files in pages folder. For now i don't know complexity of this but in vue files


vueI18n: {
        messages: {

``` and manage in big projects it's something like pain

├── assets
├── locales
|   ├── en.json
|   └── de.json
├── pages
|   ├── Home
|   |  ├── Home.vue
|   |  ├── locales
|   |  └──  └── en.json
└── └── about.vue

In Nuxt strict structure for components and pages so for locales also will be good.

Any ideas? 

Thanks
aditig492 commented 5 years ago

+1

ZtheLeader commented 5 years ago

From my perspective better create default locale folder near assets and in those folder store localization files. I mean it's better for developer and who will work with those files.

For big projects rather difficult to manage big files with localization and for that store files in pages folder. For now i don't know complexity of this but in vue files

vueI18n: {
        messages: {

``` and manage in big projects it's something like pain

├── assets
├── locales
|   ├── en.json
|   └── de.json
├── pages
|   ├── Home
|   |  ├── Home.vue
|   |  ├── locales
|   |  └──  └── en.json
└── └── about.vue

In Nuxt strict structure for components and pages so for locales also will be good.

Any ideas? 

Thanks

@uharbachou Hi I am trying to implement similar structure for my i18n files. But my translations from the component folders are not being fetched. Can you help me on it ? Did you make some changes in default i18n.js file ?

Sendoo commented 5 years ago

@ZtheLeader +1 And me interesting, how to realize this structure?

ZtheLeader commented 5 years ago

@Sendoo I found out the solution. You just give src to your separate i18n.json file. Like

<i18n src="source-to-your-file"></i18n>

For more info: http://kazupon.github.io/vue-i18n/guide/sfc.html#multiple-custom-blocks

stefandevo commented 5 years ago
// main.js
 import VueI18n from 'vue-i18n'
Vue.use(VueI18n)

import { languages } from './i18n/index.js'
import { defaultLocale } from './i18n/index.js'
const messages = Object.assign(languages)

The Object.assign does not work on IE. Can I replace it with something else?

dekadentno commented 5 years ago
// main.js
 import VueI18n from 'vue-i18n'
Vue.use(VueI18n)

import { languages } from './i18n/index.js'
import { defaultLocale } from './i18n/index.js'
const messages = Object.assign(languages)

The Object.assign does not work on IE. Can I replace it with something else?

https://stackoverflow.com/questions/42091600/how-to-merge-object-in-ie-11

peterver commented 5 years ago
// main.js
 import VueI18n from 'vue-i18n'
Vue.use(VueI18n)

import { languages } from './i18n/index.js'
import { defaultLocale } from './i18n/index.js'
const messages = Object.assign(languages)

The Object.assign does not work on IE. Can I replace it with something else?

https://stackoverflow.com/questions/42091600/how-to-merge-object-in-ie-11

Or add a polyfill and make your code futureproof : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill

Personally I use polyfill.io in most of my projects as it polyfills based on the browser's needs : https://polyfill.io/v3/

NathanAP commented 2 years ago

In my perspective, I prefer to make different files based on models/classes. So my structure is:

// src/locales/user.json

{
   "en": {
      "user": {
         "attributes": {
            "login": "Login",
            "password": "Password"
         }
      }
   }
   "pt": {
      "user": {
         "attributes": {
            "login": "Usuário",
            "password": "Senha"
         }
      }
   }
}
// src/plugins/i18n.js

import Vue from 'vue'
import VueI18n from 'vue-i18n'
import userMessages from '../locales/user.json'

export const defaultLocale = "en"
export const messages = Object.assign({
      ...userMessages,
      (you can add here other json, with "...blabla")
})
// src/main.js

import Vue from 'vue'
import VueI18n from 'vue-i18n'
import './plugins/i18n'
import { defaultLocale, messages } from './plugins/i18n'

const i18n = new VueI18n({
    locale: defaultLocale,
    fallbackLocale: 'pt',
    messages
})

new Vue({ i18n }).$mount("#app")

Now you can use like this:

<span>{{ $t("user.attributes.role") }}</span>
arnonrdp commented 2 years ago

The solution of @dekadentno helped me a lot!!

I have a similar issue so I decided to use this one instead creating another one. Here is: I have an Settings page with an <option v-for> iterating over the languages. Here is a piece of code:

<select v-model="$i18n.locale">
  <option
    v-for="locale in $i18n.availableLocales"
    :key="`locale-${locale}`"
    :value="locale"
  >
    {{ locale }}
  </option>
</select>

It is working very nice, however the in the Front-End user was seeing the key of the locale (en, pt, ja). To solve this I did a weird solution that is working but it seems to be so incorrect:

import { createI18n } from "vue-i18n/index";
import en from "./en.json";
import ja from "./ja.json";
import pt from "./pt-br.json";

const messages = {
  "English": en,
  "Português": pt,
  "日本語": ja,
};

// Create i18n instance with options
export const i18n = createI18n({
  locale: "English",
  fallbackLocale: ["Português", "日本語"],
  messages,
});

Although this is working, it seems to me to be very incorrect. So how could I display the locales values instead of their keys?

lau1944 commented 2 years ago

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()
})
BernhardSchlegel commented 1 year ago

Anyone with an idea how to insert @ in a JSON? Already tried {@} and almost all versions of @intlify/vite-plugin-vue-i18n.

setup: quasar............. v2.11.5 vite............... v2.9.15

arnonrdp commented 1 year ago

@BernhardSchlegel, if you using it in vite.config.ts try this:

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    VueI18nPlugin({
      include: path.resolve(__dirname, './src/i18n/**')
    })
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
      'vue-i18n': 'vue-i18n/dist/vue-i18n.runtime.esm-bundler.js'
    }
  }
})
BernhardSchlegel commented 1 year ago

Thanks @arnonrdp ! I suppose the relevant line is '@': fileURLToPath(new URL('./src', import.meta.url)),. Can you elaborate a little bit what it does, I'd love to understand is.

So my goal is to have a JSON like

{
   "hello": "w@rld"
}
arnonrdp commented 1 year ago

This line of code is defining an alias named "@" to a path based on the URL of the folder "./src". The "fileURLToPath" function converts the URL to a file path and the alias is used as a shorter and more convenient way of referring to the path. This is useful for importing files and folders into other files in the project, allowing you to avoid having to write the full path to each file or folder you want to import. -- ChatGPT

More details here: https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n