kazupon / vue-i18n

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

How do I change the language after configuration? #2

Closed thelinuxlich closed 8 years ago

TheBroox commented 7 years ago

@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>
roelvan commented 7 years ago

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

EmadAdly commented 7 years ago

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.

spacemudd commented 7 years ago
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.

TheBroox commented 7 years ago

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>
janein commented 7 years ago

@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.

janein commented 7 years ago

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.

EmadAdly commented 7 years ago

@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
})
longprao commented 7 years ago

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 });
  },
},
janein commented 7 years ago

@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);
    });
};
/* 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 :)

iMomen commented 7 years ago

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?

bungbung2 commented 7 years ago

@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()
    }
  })
})
AbdullaevTimur commented 6 years ago

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' })`

martinnaughton commented 6 years ago

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';
}
aligajani commented 5 years ago

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');
  }

})
mchikhaoui commented 5 years ago

this.$i18n.locale = 'your locale'

uniquejava commented 2 years ago

How to do this in vue3 composition api and vue-i18n@9, there is no this.$i18n