Simon
Simon

Reputation: 59

Use component based translations in child components in vue-i18n

I'm using vue-i18n to translate messages in my vue app. I have some global translations that are added in new VueI18n(...) as well as some component based translations in a component named c-parent. The component contains child components named c-child. Now, I would like to use the component based translations of c-parent also in c-child.

I made a small example in this fiddle: https://jsfiddle.net/d80o7mpL/

The problem is in the last line of the output: The message in c-child is not translated using the component based translations of c-parent.

Since global translations are "inherited" by all components, I would expect the same for component based translations (in their respective component subtree). Is there a way to achieve this in vue-i18n?

Upvotes: 0

Views: 2636

Answers (3)

jordisan
jordisan

Reputation: 131

What I'm doing is using i18n.mergeLocaleMessage in router.ts to merge a particular .i18n.json translation file (by setting a meta.i18n property) for each route:

const router = new Router({
[...]
    {
      path: '/settings',
      name: 'settings',
      component: () => import('./views/Settings.vue'),
      meta: {
        i18n: require('./views/Settings.i18n.json'),
      },
    },
[...]
});

router.beforeEach((to, from, next) => {
    // load view-scoped translations?
    if (!!to.meta.i18n) {
      Object.keys(to.meta.i18n).forEach((lang) => i18n.mergeLocaleMessage(lang, to.meta.i18n[lang]));
    }
    next();
});

With Settings.i18n.json being like:

{
    "en": 
    {
        "Key": "Key"    
    },
    "es": 
    {
        "Key": "Clave"    
    }
}

That way, all child components will use the same translation file.

In case you can't use vue-router, maybe you can do it in the parent component's mounted() hook (haven't tried that)

Upvotes: 2

Emmanuel
Emmanuel

Reputation: 63

I had the same situation with i18n.

Let's say we have a "card" object prop which it includes the needed language ( was my case) that we'll use in a CardModal.vue component which will be the parent. So what i did was get the needed locale json file ( based on the prop language) and adding those messages within the card prop.

So in the parent component we'll have:

<template>
  <div id="card-modal">
    <h1> {{ card.locales.title }} </h1>
    <ChildComponent card="card" />
  </div>
</template>

<script>
export default {
  name: 'CardModal',
  
  props: {

   card: {
    type: Object,
    required: true,
    }

  }

  data() {
    return {
      locale: this.card.language, //'en' or 'es'
      i18n: {
        en: require('@/locales/en'),
        es: require('@/locales/es'),
      },
    }
  },

  created() {
   this.card.locales = this.i18n[this.locale].card_modal
  }

}
</script>

Notice that we are not relying in the plugin function anymore ( $t() ) and we are only changing the locale in the current component. I did it in this way cause i didn't want to use the "i18n" tag in each child component and wanted to keep all the locales messages in one single json file per language. I was already using the card prop in all child components so that's why i added the locales to that object.

If you need a way to change the locale using a select tag in the component, we can use a watcher for the locale data property like the docs shows

Upvotes: 0

Varit J Patel
Varit J Patel

Reputation: 3520

Well, you need to pass the text to child component using props.

Global translations are "inherited" by all components. But you're using local translation in child.

const globalMessages = {
  en: { global: { title: 'Vue i18n: usage of component based translations' } }
}

const componentLocalMessages = {
	en: { local: {
    title: "I\'m a translated title",
    text: "I\'m a translated text"
  }}
}

Vue.component('c-parent', {
	i18n: {
  	messages: componentLocalMessages
  },
	template: `
    <div>
      <div>c-parent component based translation: {{ $t('local.title') }}</div>
      <c-child :text="$t('local.title')"></c-child>
    </div>
  `
})

Vue.component('c-child', {
  props: ['text'],
	template: `
		<div>c-child translation: {{ text }}</div>
	`
})

Vue.component('app', {
	template: '<c-parent />'
})

const i18n = new VueI18n({
	locale: 'en',
  messages: globalMessages
})

new Vue({
  i18n,
  el: "#app",
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
}

h5 {
  margin: 1em 0 .5em 0;
}
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-i18n"></script>

<div id="app">
  <h2>{{ $t('global.title') }}</h2>
  We define two Vue components: <code>&lt;c-child/&gt;</code> contained in <code>&lt;c-parent/&gt;</code>.
  <code>&lt;c-parent/&gt;</code> defines some component based translations. We would like to use the
  parent's translations in the child but it does not work.
  
  <h5>Example:</h5>
  <app />
</div>

Upvotes: 1

Related Questions