Vue-i18n: How do I change the language after configuration?

Created on 18 Dec 2014  Â·  66Comments  Â·  Source: kazupon/vue-i18n

Most helpful comment

If you want to do it inside a component:

selectLanguage(lang) {
   this.$root.$i18n.locale = lang;
   window.localStorage.language = lang;
},

If you want to use in vuex, so you can easily access/watch in other components:

// i18n.js
import Vue from 'vue';
import VueI18n from 'vue-i18n';

Vue.use(VueI18n);

export default new VueI18n({
  locale: window.localStorage.language || navigator.language || 'en',
});

// store.js
import i18n from '../i18n';

mutations: {
  [SET_LANGUAGE](state, payload) {
    state.language = payload.data;
    i18n.locale = payload.data;
    window.localStorage.language = payload.data;
  },
},
actions: {
  setLanguage: ({ commit }, data) => {
    commit(SET_LANGUAGE, { data });
  },
},

All 66 comments

sorry, this plugin cannot change the language after the configuration.

@thelinuxlich
Hi
I released vue-i18n new version.
You can change language the below code.

<p>{{ $t('message.hello', 'en') }}</p>

Please try it !!

I tested it and it is useful but I think that it would be better to somehow set lang parameter as observable and change globally based on it

I add Vue.config.lang configuration.

This doesn't solve the issue unless Vue.config.lang is reactive

Sorry,
I didn't understand your request.

I think that it would be better to somehow set lang parameter as observable and change globally based on it

You need reactive lang params, don't you ?

yeah, I believe it's a common case for the user switching the language during interaction

OK.
I'll try out. :)

Any news on this one?

I'm interested in easy switching languages by user too.

+1
i tried making a language switcher widget, but it don't seem to update the translations after Vue.config.lang has been changed

+1

+1

+1
@martinlindhe Did you get it to work?

@kazupon This package https://github.com/Haixing-Hu/vue-i18n has support for this through jQuery AJAX calls(from what I can tell). Perhaps you can use some of it's logic to solve this issue. Just a thought!

@sagaio: nope, i'm currently working around it by doing a page reload when language was changed.

location.reload();

works, but ain't pretty!

+1

+1

+1

Was thinking of this issue again today and came up with this solution.
It involves using a new tag and I'm not sure i gonna do this on my current vue project, but maybe for the next one.

First, we have someplace in our ui to change language.

to change language you do something like this

select(code) {
    Vue.config.lang = code;
    this.$broadcast('language');
}

to put translations in our templates, we now do <t t="user.new"></t>

and finally a T.vue component:

<template>
    <span>
        {{ translated }}
    </span>
</template>

<script>
export default {
    props: {
        t: '',
    },
    data() {
        return {
            translated: '',
        };
    },
    ready() {
        this.translated = this.$t(this.t);
        this.$on('language', function() {
            this.translated = this.$t(this.t);
        });
    },
};
</script>

Now your language changes are reactive.

+1

@kazupon I would like to add this feature myself, but i can't afford it right now...but i would like to know do you plan to support this or should i switch to what @sagaio said?

@seemsindie
I'll plan to support in vue-i18n v3. but the release of vue-i18n v3 is undecided.

@kazupon Ok thanks, all i needed to hear :)

+1

A global runtime this.$setLang() method preferred

+1 count me in if you need an hand for the next release

Also, for those using vuex I'm using the following workaround at the moment:

modules/i18n/mutation-types.js

export const SET_LANGUAGE = 'SET_LANGUAGE'

modules/i18n/index.js

import {
 SET_LANGUAGE
} from './mutation-types'

const state = {
 lang: 'en',
 locales: [ 'en', 'fi' ]
}

const mutations = {
 [SET_LANGUAGE] (state, lang) {
  if (state.locales.indexOf(lang) !== -1) {
   state.lang = lang
  }
 }
}

export default {
 state,
 mutations
}

store.js

import i18n from './modules/i18n'
// ... import other stuff

Vue.use(Vuex)

const store = new Vuex.Store({
 modules: {
  i18n
 }
})

export default store

main.js

import Vue from 'vue'
import store from './store'

Vue.filter('translate', function (value, arg) {
 return this.$t(value, this.$store.state.i18n.lang, arg)
})

and then just use it like this

<span>{{ 'message' | translate }}</span>

where

en.json

{
 "message": "English message"
}

fi.json

{
 "message": "Finnish message"
}

If you change the language like this:

vm.$store.dispatch('SET_LANGUAGE', 'fi')

everything will work like a charm.

NOTE: In my case I opt-out for create a filter, and it might not work perfectly, but gives an idea on how to get this to work.

+1

Hello @kazupon
Do you have an example on how to change the Vue.config.lang from within a component?

thanks

+1 for some example

You can use the Component locale and $t method with lang params.

Oh, thanks, but as a matter of fact I was looking for some example to switch language dynamically in my app - should I just change global config option like Vue.config.lang = 'ja' or Is threre any special method like 'setLanguage' ?

hmm, I cannot see the your use-case ...
I think you can resolve with component that was wrapped vuex like state management (e.g. the above @ealves-pt comment.

@kazupon The use case is if a user navigates a websites in one language, then he clicks a button to switch to another language. Now the global vue.config.lang has to be the new language. How would you handle that switcher-button-component?

Exactly :)

In this use-case, I think you can handle data changing with watch in the component.

the below example of implementation:

const navigation = {
  template: `<nav>
    <select v-model="lang">
      <option selected value="en">EN</option>
      <option> value="ja">JA</option>
    </select>
  </nav>`,
  data () {
    return { lang: 'en' }
  },
  watch: {
    lang (val) {
      Vue.config.lang = val
    }
  }
}

Ok thank you.
At the moment, Vue is only imported in the main file, not in the components.
So I have to import 'Vue' inside my component for this?

@francoisromain you could use the $lang property:

new Vue({
  // ...
  methods: {
    change () {
      let current = this.$lang.lang

      if (current === 'en') {
        this.$lang.lang = 'el'
      } else {
        this.$lang.lang = 'en'
      }
    }
  }
})

https://jsfiddle.net/pespantelis/4xcuLtur/

@pespantelis sweet!

@pespantelis Oh but $lang is undefined in the module scope…

@francoisromain Ohh you are right!

However, I have another approach to achieve this in the module scope.

You could add Vue instance methods by attaching them to Vue.prototype.

Vue.prototype.$locale = {
  change (lang) {
    Vue.config.lang = lang
  },
  current () {
    return Vue.config.lang
  }
}

Vue.component('my-component', {
  template: '...',
  methods: {
    change () {
      let current = this.$locale.current()

      if (current === 'en') {
        this.$locale.change('el') 
      } else {
        this.$locale.change('en') 
      }
    }
  }
})

https://jsfiddle.net/pespantelis/4xcuLtur/1/

@pespantelis thanks a lot : )

I solved it a different way, as I use vue-router:

in the main js file:

router.beforeEach((to, from, next) => {
    Vue.config.lang = to.params.lang;
    next();
});

and the lang-select component:

<template>
    <div class="lang-select">
        <button @click="change('en')">en</button>
        <button @click="change('de')">de</button>
        <button @click="change('fr')">fr</button>
    </div>
</template>

<script>
    export default {
        methods: {
            change (lang) {
                const route = Object.assign({}, this.$route);
                route.params.lang = lang;
                this.$router.push(route);
            }
        }
    };
</script>

What do you think about this solution?

@pespantelis' altering of the prototype is by far the most elegant solution. Really feels like it should be baked in.

With Vue i18n 6.0, you need to adapt the locale property of VueI18n to change the language. I solved it like this:

Vue.use(VueI18n);

const i18n = new VueI18n({
    locale: 'en',
    fallbackLocale: 'en',
    messages: { [...] }
});

Vue.prototype.$locale = {
    change (language) {
        i18n.locale = language;
    },
    current () {
        return i18n.locale;
    }
};

new Vue({
    el: '#app',
    i18n,
    [...]
});

You can set the locale via $i18n property of Vue instance. :)
https://kazupon.github.io/vue-i18n/en/api.html#injected-properties

Ha, @kazupon, your response fixed it. Been trying for 3 hours but we only had do to this haha;

The value en can be dynamic.

this.$i18n.locale = 'en' 

If it does not work, make sure you can console log the right instance.

thanks for the information @nickkuijpers, worked great.

@nickkuijpers and @TheBroox the documentation says this property is readonly. That is a typo I guess?

@roelvan i think so because in my end it works. Have u tried it?

@roelvan it worked for me but after upgrading to i18n 6.0+ I have switched to @ssc-hrep3's solution. It still extends the prototype but the altered values are slightly different.

My i18n file:

import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

import english from './english.js'
import spanish from './spanish.js'
import french from './french.js'

var messages = {
    en : english,
    sp : spanish,
    fr : french
}

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

Vue.prototype.$locale = {
    change (lang) {
        i18n.locale = lang
    },
    current () {
        return i18n.locale
    }
}

export default i18n

And then how I incorporate it into a template:

<template>
    <div class="container-fluid">
    <router-view></router-view>
        <div class="row">
            <div class="languages col-xs-12">
                <ui-button v-if="this.$locale.current() !== 'en'" type="secondary" @click="changeLang('en')">
                    English
                </ui-button>
                <ui-button v-if="this.$locale.current() !== 'sp'" type="secondary" @click="changeLang('sp')">
                    Spanish
                </ui-button>
                <ui-button v-if="this.$locale.current() !== 'fr'" type="secondary" @click="changeLang('fr')">
                    French
                </ui-button>
            </div>
        </div>
    </div>
</template>


<script>
    export default {
        name: 'Localization',
        methods : {
            changeLang: function(newLang){
                this.$locale.change(newLang)
            }
        }
    }
</script>

@nickkuijpers yep it seems to work for me. Thanks for sharing @ssc-hrep3's elegant solution @TheBroox. I used your snippet :)

I think the Elegant Solution is
Within the same component, you can path the language value to a method that switches in the global range through this. $ I18n.locale = value

For example

</template>
    <button  @click="setLanguage('en')">en</button>
    <button  @click="setLanguage('de')">de</button>
    <button  @click="setLanguage('fr')">fr</button>
</template>


<script>
export default {

....

  methods: {
    setLanguage (val) {
      this.$i18n.locale = val
    }
  }
}
</script>

it will update language without refresh page or reload or redirect.

const app = new Vue({
    el: '#app',
    mounted() {
      Vue.config.lang = document.querySelector("html").lang
    }
});

In my case, Laravel takes care of filling up the 'lang' attribute in html so I see no reason to make this any more complicated.

If you are looking for a completely js solution that can be set on page load via query string param as well as offering buttons to the user.

<template>
        <button v-for="lang in notCurrentLangs" :key="lang.file" @click="changeLang(lang.key)">
          {{lang.name}}
        </ui-button>
</template>


<script>
    export default {
        name: 'Localization',
    data(){
      return {
        langs: [
          {key: 'en', name: 'English'},
          {key: 'sp', name: 'Spanish'},
          {key: 'fr', name: 'French'},
          {key: 'ho', name: 'Hodor', hidden: true}
        ]
      }
    },
    computed: {
      notCurrentLangs(){
        var currentLang = this.$locale.current()
        return this.langs.filter(function(lang){
          return lang.key !== currentLang && !lang.hidden
        })
      }
    },
        methods : {
            changeLang: function(newLang){
                this.$locale.change(newLang)
            },
      hasLang: function(checkKey) {
        var hasLang = false
        this.langs.forEach(function(lang){
          if(lang.key === checkKey){
            hasLang = true
          }
        })
        return hasLang
      }
        },
    created(){
      if(this.$route.query.lang && this.hasLang(this.$route.query.lang)){
        this.changeLang(this.$route.query.lang)
      }
    }
    }
</script>

@EmadAdly I tried your code as it's very similar to the one in the docs.
I can change the locale with this.$root.$i18n.locale = val, but all components update only after the next page change. until then nothing happens.
Any idea how I can force an "update"?

PS: Everything inside this components changes, but other components stay the same. Until I change the page.

Well, seems like I used the translations in a wring way.
I set some data props, but those do not get updated. So I guess the best way to use the translations is to always do it in the template.

@janein
sorry for that , val must be stored in local storage So add this line inside the setLanguage method

    setLanguage (val) {
      this.$i18n.locale = val
      window.localStorage.setItem('Language', val)
    }

and in main.js read locale from local storage like the below

const i18n = new VueI18n({
  locale: window.localStorage.getItem('userLanguage'), // get locale
  messages // set locale messages
})

If you want to do it inside a component:

selectLanguage(lang) {
   this.$root.$i18n.locale = lang;
   window.localStorage.language = lang;
},

If you want to use in vuex, so you can easily access/watch in other components:

// i18n.js
import Vue from 'vue';
import VueI18n from 'vue-i18n';

Vue.use(VueI18n);

export default new VueI18n({
  locale: window.localStorage.language || navigator.language || 'en',
});

// store.js
import i18n from '../i18n';

mutations: {
  [SET_LANGUAGE](state, payload) {
    state.language = payload.data;
    i18n.locale = payload.data;
    window.localStorage.language = payload.data;
  },
},
actions: {
  setLanguage: ({ commit }, data) => {
    commit(SET_LANGUAGE, { data });
  },
},

@EmadAdly @longprao Thanks, your approach looks nice.
I got another solution in the meantime. As I work with vuex I wanted to have a centralized solution to get/set the language.
I handle all my communication with the localStorage in a separate file, whose methods get called from my vuex-store's actions.
To get my language a again on restart I do something like this:

/* main.ts */
import store from './store';
const app = new Vue({
  // ...
  store
});
store.dispatch('settings/loadSettings');
/* store/settings/actions.ts */
export const loadSettings = (state) => {
    return SettingsApi.fetchSettings().then((res) => {
       state.commit('loadSettings', res);
    });
};



md5-5f62c398b412c203aebb3e264370ca4e



/* api/settings-api.ts */
namespace SettingsApi {
  export const fetchSettings = () => {
        return localforage.startsWith(STORAGE_PREFIX).then(async (settings): Promise<SettingsState>  => {
            settings = stripSettingsPrefix(settings);

            // merge with default settings
            const allSettings = (Object as any).assign({}, defaultSettings, settings);
            await saveAllSettings(allSettings);

            return allSettings;
        });
    };
   export const saveAllSettings = async (settings) => {
        for (const setting of Object.keys(settings)) {
            const value = settings[setting];
            await saveSetting(setting, value);
        }
        return settings;
    };

    export const saveSetting = (key, value) => {
        if (key === 'language') {
            (window as any).app.$root.$i18n.locale = value;
        }

        return localforage.setItem(STORAGE_PREFIX + key, value)
            .then((val) => val)
            .catch((err) => {
                console.warn(`
                    oooops ${key} with the value "${value}" could not be saved to the local storage! :(
                `, err);
            });
    };
}

This works pretty neat as it handles all settings and not just the language.
And it is fast too, so you don't get a delay where you can see the language switch fron the default language to the saved one.
Maybe this helps someone who tries so achieve something similar :)

What if I want to reload the page to receive new content based on the user language and also I want to change the stylesheet file based on it?

@ealves-pt @longprao

Your ideas were helpful.

i solve the problem and i didn't use VueX

// lang.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

let i18n = new VueI18n({
  messages: {}
})

let setLocale = function (locale) {

  if (locale === undefined) {
    i18n.locale = window.localStorage.language || navigator.language || 'en'
  }
  else {
    i18n.locale = locale
  }

  try {
    if (!i18n.messages[i18n.locale]) {
      let addMessage = require('../message-' + i18n.locale + '.json')
      i18n.mergeLocaleMessage(i18n.locale, addMessage)
    }

    window.localStorage.language = i18n.locale
  }
  catch (error) {
  }
}

export default {i18n, setLocale}
// main.js

import 'babel-polyfill'

import Vue from 'vue'
import Quasar from 'quasar'
import lang from 'util/lang'

Vue.use(Quasar) // Install Quasar Framework

Quasar.start(() => {
  Vue.mixin({
    data: () => {
      return {
        lang: lang
      }
    }
  })

  new Vue({
    el: '#q-app',
    store,
    router,
    i18n: lang.i18n,
    render: h => h(require('./App')),
    created: function () {
      this.lang.setLocale()
    }
  })
})

I use another solution for setting language (in main.js):
`function checkLang () {
var navigatorLang = navigator.language || navigator.userLanguage
var langList = ['ru', 'en']
var fallbackLang = 'en'
var settingLang = langList.includes(navigatorLang) ? navigatorLang : fallbackLang
var lang = window.localStorage.getItem('userLang')
return lang.length > 0 ? lang : settingLang
}

const i18n = new VueI18n({
locale: checkLang(),
fallbackLocale: 'en',
dateTimeFormats: 'long'
})`

In my vue component file I have a method that can change. Just tested this and it was reactive to change it from en to fr and the UI updated straight away.

change: function () {
    this.$i18n.locale = 'fr';
}

I am doing this

router.beforeEach((to, from, next) => {

  // possible entry points where language should be set
  let entryPoints = ["login", "token"];

  if (entryPoints.includes(to.name)) {
    console.log("Setting locale from supported entry points")

    let languageQuery = to.query.locale || "en";
    i18n.locale = languageQuery;

    // This will allow us to use it later
    window.localStorage.setItem('locale', languageQuery);
    Cookies.set('locale', languageQuery);

  } else {
    console.log("Setting locale from localStorage or Cookies");
    i18n.locale = window.localStorage.getItem('locale') ||  Cookies.get('locale');
  }

})

this.$i18n.locale = 'your locale'

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sergot picture sergot  Â·  5Comments

zhangruinian picture zhangruinian  Â·  4Comments

Rosadojonathan picture Rosadojonathan  Â·  4Comments

karol-f picture karol-f  Â·  3Comments

shaunnetherby picture shaunnetherby  Â·  5Comments