VueI18n dynamic labels from api call - vue.js

I want to retrieve the labels from api, they are in 2 languages, and switch them when user clicks to change language. Right now one language is loaded as default, but it is not reactive. How to make it react to user changing the language? I was thinking about somehow calling the function before everything else and storing labels in localStorage
The code: (lang.js file)
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import { HTTP } from './../config/http-request.js'
Vue.use(VueI18n)
const locale = 'da'
var langName;
const i18n = new VueI18n({
locale, // language identifier
messages: {}
})
function getLang(lang){
console.log(lang)
if(lang == "da"){
langName = "Danish"
}else{
langName = "English"
}
HTTP.get("1.0/language/label?language_name="+ langName + "&compact=True")
.then((response) => {
var msg = response.data.compact
i18n.setLocaleMessage(lang, msg)
i18n.locale = lang
return lang
})
.catch(function(error) {
console.log(error)
})
}
// This method is called when the manual switch languages, to be loaded when the page is initialized with a default language
// Vue.prototype.$loadLanguageAsync = loadLanguageAsync
getLang(locale)
export default i18n

Related

Vue3 vue-i18n Lazy loading

I would like to implement lazy loading for individual languages in my APP, however I don't really understand the example.
Example: https://vue-i18n.intlify.dev/guide/advanced/lazy.html
i18n.js
import { nextTick } from 'vue'
import { createI18n } from 'vue-i18n'
export const SUPPORT_LOCALES = ['de', 'en']
export function setupI18n(options = { locale: 'de' }) {
const i18n = createI18n(options)
setI18nLanguage(i18n, options.locale)
return i18n
}
export function setI18nLanguage(i18n, locale) {
if (i18n.mode === 'legacy') {
i18n.global.locale = locale
} else {
i18n.global.locale.value = locale
}
document.querySelector('html').setAttribute('lang', locale)
}
export async function loadLocaleMessages(i18n, locale) {
// load locale messages with dynamic import
const messages = await import(
/* webpackChunkName: "locale-[request]" */ `./locales /${locale}.json`
)
// set locale and locale message
i18n.global.setLocaleMessage(locale, messages.default)
return nextTick()
}
My folder structure looks quite similar.
I don't use the composition API at this point.
Instead of loading the languages via vue-router I would like to define a default language which can be changed in the user settings.
Where and how do I have to load the function "loadLocaleMessages()" now?
Currently I load the configuration in my main.js like this so that I have "$t" available in the template:
import { setupI18n } from "#/plugins/i18n";
...
app.use(setupI18n());
...
The i18n.js looks like in the example only that my path for the languages is different.
Also I would like to know how I have to include everything so that I have e.g. "$t" also available in other (not components)?
E.g. in the routes.js or in the store (vuex)
EDIT:
middlewares.js - beforeEachHook
import { i18n } from "#/plugins/i18n"
const { t } = i18n.global
/**
* Translate/set page title
*/
export function setPageTitleMiddleware (to, from, next) {
const pageTitle = to.matched.find(item => item.meta.title)
if (to.meta && to.meta.title)
window.document.title = process.env.VUE_APP_DOMAIN_TITLE + ' | ' + t(to.meta.title)
else if
(pageTitle && pageTitle.meta) window.document.title = process.env.VUE_APP_DOMAIN_TITLE + ' | ' + t(pageTitle.meta.title)
next()
}
To be able to get i18n translation from anywhere in your application (vue3 in my case) :
import i18n from "#/i18n";
i18n.global.t("your_key")

How to make react native app save the language when changed from the app with i18next?

I'm using i18next in react native to use multi languages in the app :
the user can change the language from the app by clicking on a button
in this button I make an action to set the language in AsyncStorage ,
in i18next init file I want to use the value of the AsyncStorage, but its not changing it because AsyncStorage it need async and await so it take long time to change the value ,
this is the code :
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import English from '../Translation/Languages/English.json';
import Spanish from '../Translation/Languages/Spanish.json';
import Arabic from '../Translation/Languages/Arabic.json';
import AsyncStorage from '#react-native-async-storage/async-storage';
let language = null;
const changeLanguage = async () => {
try {
const Lang = await AsyncStorage.getItem('Language');
if (Lang !== null) {
language = Lang;
}
}
catch (error) {
console.log("Error ", error)
}
};
changeLanguage();
i18n
.use(initReactI18next)
.init({
lng: language,
fallbackLng: 'en',
resources: {
en: English,
es: Spanish,
ar: Arabic
}
});
export default i18n;
Checkout this medium-post for a working sample...
Github Repo
https://github.com/hend-elsahli/ReactNativeLocalization
use languageDetector
i18n
.use(languageDetector)
.init(...)
const languageDetector = {
init: Function.prototype,
type: 'languageDetector',
async: true, // flags below detection to be async
detect: async callback => {
const selectedLanguage = await AsyncStorage.getItem('Language');
/** ... */
callback(selectedLanguage);
},
cacheUserLanguage: () => {},
};

i18n-js in react native: language not loaded on startup

I have a react native app (ios) with multiple languages. I'm currently facing a weird issue: when I open the app, for a split second, I don't see the translations but I see an error: 'missing translation ...'. This error normally shows up when the translation key is not found (key-value pair) in the json file. Like I said, this error is only there for a split second, after like 500ms, the translations pop up. It seems like the translation file is not loaded properly when the app starts up.
This error is new since the last few days, before that it didn't happen. It also does not happen on the ios simulator, but only when I'm testing on a device. Both the debug scheme and the release scheme have this issue.
My language file looks like this:
// en.json
// { key: value }
{
"hello": "Hello",
"world": "World",
...
}
This is my code in react-native:
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { _setLanguage } from '../redux/Actions';
import _ from 'lodash';
import i18n from 'i18n-js';
import * as RNLocalize from "react-native-localize";
const translationGetters = {
'en': () => require('./translations/en.json')
};
export default useLanguage = (props) => {
const dispatch = useDispatch();
// get the stored language in the redux store (if set)
const _language = useSelector(state => state.data.config.language);
// translate function to memoize the language file
const translate = _.memoize(
(key, config) => i18n.t(key, config),
(key, config) => (config ? key + JSON.stringify(config) : key)
);
// useEffect function loaded on start up to get the language from redux
// (if set) or from the device
// setLanguage() is called to load the correct language file
useEffect(() => {
if (_.isNull(_language)) {
const fallback = { languageTag: 'en' };
const { languageTag } = RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) || fallback;
dispatch(_setLanguage({ language: languageTag }));
setLanguage(languageTag);
} else {
setLanguage(_language);
}
}, []);
// setLanguage()
// sets the language file
const setLanguage = (language) => {
translate.cache.clear();
i18n.translations = { ['en']: translationGetters['en'](), [language]: translationGetters[language]() };
i18n.locale = language;
i18n.defaultLocale = 'en';
i18n.fallbacks = true;
i18n.missingTranslation = () => (null);
dispatch(_setLanguage({ language }));
};
return {
setLanguage,
language: _language,
translate
};
};

how to get access to vuex inside other js files like plugins in nuxt

i have a problem. i wanna get access to one of my getters inside my vee-validate.js file.
how can i do that?
in my pages and components, (in <script>...</script> part, outside export default{...}) i used this function:
component.vue
<script>
let lang;
function getLang({store}) {
lang = store.state.lang
}
export default{
...
}
but it is not working!
i'm trying to access my custom lang file (for translation purpose) that is stored in lang state in my vuex, and use it in vee-validate.js file for custom message.
i tried to import store but not working.
veevalidate.js:
import Vue from 'vue'
import { required } from 'vee-validate/dist/rules'
import { extend, ValidationObserver, ValidationProvider, setInteractionMode } from 'vee-validate'
import {store} from '../store'
let langFile = store
setInteractionMode('eager')
extend('required', {
...required,
message: ''
})
Vue.component('ValidationProvider', ValidationProvider);
Vue.component("ValidationObserver", ValidationObserver);
UPDATED: My store index.js
import langfile from '../static/lang'
export const state = () => ({
lang: null,
dir: null,
})
export const getters = {
//----------------- Language and Direction
lang(state){
return state.lang
},
dir(state){
return state.dir
},
}
export const mutations = {
SET_LANGUAGE(state, lang){
state.lang = lang
},
SET_DIRECTION(state, dir){
state.dir = dir
},
}
export const actions = {
async nuxtServerInit({dispatch, commit}) {
// ------------- Read Language File
let baseLang = process.env.SITE_LANGUAGE;
let siteLang = langfile[baseLang];
let siteDir = langfile[baseLang]['dir'];
commit('SET_LANGUAGE', siteLang);
commit('SET_DIRECTION', siteDir);
},
}

Change Axios Default Params Not Changing On Vuex Action Event

I am implementing a website which users can toggle the language selection. Selected language preference is thus sent to the API with the parameter lang
If Language is set to English
/api/test?lang=en
If Language is set to Thai
/api/test?lang=th
I have stored the language in localstorage and Vuex state management.
Since, ?lang parameter is needed to sent in every request, I decided to use lang parameter as axios default parameter
mutations.js
let mutations = {
UPDATE_LANGUAGE_PREFERENCE(state, language) {
if (language.toLowerCase() === ENGLISH_LANGUAGE_CODE) {
state.languagePreference = ENGLISH_LANGUAGE_CODE;
} else {
state.languagePreference = THAI_LANGUAGE_CODE;
}
Vue.prototype.$http = axios
let languagePreference = store.getters.languagePreference
if (!languagePreference) {
languagePreference = LANGUAGE_PREFERENCE
}
Vue.prototype.$http.interceptors.request.use((config) => {
config.params = config.params || {}
config.params['lang'] = languagePreference
return config
})
},
}
Doing it, it now sends the default intial language to every request. But it does not reflect the changes when user toggles the language
NavbarComponent.vue
<script>
...
methods: {
updateLanguagePreference() {
if (!this.isThai) {
this.$store.dispatch('updateLanguagePreference', THAI_LANGUAGE_CODE)
} else {
this.$store.dispatch('updateLanguagePreference', ENGLISH_LANGUAGE_CODE)
}
axios.get('/test', {
})
}
},
...
</script>
The following is the modified code based on your code:
main.js
import store from 'store'
const $http = axios.create()
$http.interceptors.request.use((config) => {
let params = config.params || {}
if (store.getters.languagePreference) {
config.params = {...params, lang: store.getters.languagePreference}
}
return config
})
Vue.prototype.$http = axios
Put the axios initialization operation in main.js
getters.js
let getters = {
languagePreference: state => state.languagePreference // or state.yourModuleName.languagePreference
}
mutations.js
let mutations = {
UPDATE_LANGUAGE_PREFERENCE(state, language) {
state.languagePreference = language
}
}
actions.js
let actions = {
updateLanguagePreference({commit}, language) {
commit('UPDATE_LANGUAGE_PREFERENCE', language)
}
}