[Vuejs]-Set language with i18n an change country flag based on selected language in Vuejs

0👍

This is how I do it – using a single JSON file with all localizations (each localization has a key title which represents the name of the locale in its corresponding language):

<template>
  <pop-over>
    <flat-button class="country_selector pl-1 pr-1 pt-0 pb-0 ma-0 font-0 bold">
      <div class="current_flag" :style="{'background-image': 'url(' + $root.baseURL + 'flags/' + curLocale + '.png' + ')'}"/>
      <div class="flexbox flex-column align-center ml-1">
        <icon>
          <caret-up/>
        </icon>
        <span class="upper" style="margin-top: -5px;">{{ $root.locale }}</span>
      </div>
    </flat-button>
    <my-card slot="popover" title-class="popover_header">
      <!--
      <div slot="title">{{ $t('header.language_selector') }}</div>
      -->
      <div v-for="lang in $root.languages" :key="lang" v-close-popover class="popup_menu_item flexbox align-center pl-2 pr-2 pt-1 pb-1" @click="changeLocale(lang)">
        <icon :size="14" :color="($i18n.messages[$i18n.locale] ? lang === $i18n.locale : lang === 'en') ? '' : 'transparent'">
          <icon-checkmark/>
        </icon>
        <div class="country_flag" :style="{'background-image': 'url(' + $root.baseURL + 'flags/' + lang + '.png' + ')'}"/>
        <div class="flex-auto font-1 bold">{{ $i18n.messages[lang].title }}</div>
      </div>
    </my-card>
  </pop-over>
</template>

<script>
import { mapGetters, mapMutations } from 'vuex'
import iconCheckmark from '@/assets/img/icon/check.svg'
import caretUp from '@/assets/img/icon/caret-up.svg'

export default
{
  name: 'LanguageSelector',
  components:
    {
      iconCheckmark,
      caretUp,
    },
  computed:
    {
      ...mapGetters(['curLocale']),
    },
  methods:
    {
      ...mapMutations(['setLocale']),
      changeLocale (langCode)
      {
        this.setLocale(langCode);
        if (!this.$root.isLogged) return;
        const data = new FormData();
        data.append('language', langCode);
        this.$ajax(
          {
            method: 'POST',
            url: '/api/user/setusersettings/',
            login: this.$root.login,
            data,
          }
        );
      },
    }
}
</script>

<style lang="scss">
  @import '@/assets/scss/theme/footer.scss';

  .current_flag
  {
    border-radius: 50%;
    width: 24px;
    height: 24px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }

  .country_flag
  {
    border-radius: 50%;
    width: 22px;
    height: 22px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    margin-left: 4px;
    margin-right: 4px;
  }

  .country_selector
  {
    color: $footer_color;
  }
</style>

And the store mutation is

      setLocale (state, lang)
      {
        state.locale = lang;
        document.querySelector('html').setAttribute('lang', lang);
        if (window.i18n) window.i18n.locale = lang;
      },

And Vue-i18n is instantiated like this

import VueI18n from 'vue-i18n'
import locales from "./locales.json";

Vue.use(VueI18n);

window.i18n = new VueI18n(
  {
    fallbackLocale: 'en',
    locale: 'en',
    messages: locales,
    silentTranslationWarn: true
  });

The relevant computed properties in the root Vue instance are

  computed:
    {
      baseURL ()
      {
        return process.env.BASE_URL;
      },
      locale ()
      {
        return this.$i18n.messages[this.$i18n.locale] ? this.$i18n.locale : 'en';
      },
      languages ()
      {
        return Object.keys(this.$i18n.messages);
      },
    }

0👍

I know this is a bit old and you’ve probably already found a solution, but I got here ’cause I had the same problem and found a different approach to solving it, so I’m posting this in case anyone else has the same issue.

I made a folder called locales in my src folder which looks something like:

|--- src
     |--- locales
          |--- en-GB.json
          |--- fr-FR.json
          |--- de-DE.json

My i18n.js file looks like this:

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

Vue.use(VueI18n);

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)
    }
  })
  return messages
}

export default new VueI18n({
  // These two lines are in case you want to setup environmental variables
  //locale: process.env.VUE_APP_I18N_LOCALE || 'en-GB',
  //fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en-GB',
  locale: 'en-GB',
  fallbackLocale: 'en-GB',
  messages: loadLocaleMessages()
})

Then I put it in my main.js

import Vue from 'vue'
import App from './App.vue'
import i18n from './i18n'

Vue.config.productionTip = false

new Vue({
  i18n,
  render: h => h(App)
}).$mount('#app')

Here comes my trick (don’t know if it’s considered a hack or anything, but it makes things simpler for me):

<select v-model="$i18n.locale">
  <option v-for="(lang, i) in $i18n.locale" :key="`Lang${i}`" :value="lang"
          v-on:click="$i18n.locale=lang">
      <img :src="require(`../assets/images/${lang}.png`)"></img>
  </option>
</select>

In order for the above code to work, make sure the names of the images are the same as the locale files, i.e. if your language file is called fr.js, the image should be called fr.png, and in my case, the image is called en-GB.png.

The v-on:click, will change your currently selected language. I’ve used require(), because in my case, it won’t show the images without it (because of webpack if I’m not mistaken) – but you might not needed it. Test and see.

Hopefully this helps someone. Cheers!

Leave a comment