How to register translate function in app - vue.js

I got this translate function, in order to use it I have to import it in every component which is quite annoying.
Looks like
import { useI18n } from "vue-i18n";
export default {
setup() {
const { t } = useI18n();
return { t }
}
}
main.js looks like
import installI18n from "./lang/index";
installI18n(app);
lang/index
const i18n = createI18n({
locale: getLocale(),
legacy: false,
messages,
});
// export default i18n
export default (app) => {
app.use(i18n);
};
Can I register this somehow in my app, so I dont have to put it in setup every time?

You can use globalInjection parameter to enable global injection even in non-legacy (Composition API) mode
const i18n = createI18n({
locale: getLocale(),
legacy: false,
globalInjection: true,
messages,
});
Then you must use functions prefixed with $ in your templates - see Implicit with injected properties and functions

To use app-wide i18n,
locate translate json files in locales folder - locales/en.json
{
"key": "value",
}
create i18n.js file
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) // Path to the locales folder
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({
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages()
})
And then use it as param while creating Vue app - in app.js
...
import i18n from './i18n'
...
new Vue({
router,
store,
i18n,
render: h => h(App)
}).$mount('#admin-app')
And then use $t() in Vue files. - index.vue
<h3>
{{ $t('key') }}
</h3>

Related

How to dynamically change language for vue-google-maps

I use #fawmi/vue-google-maps for show google map in my Vue3 app. Also I use vue-i18n for i18n.
My main.js:
const { createApp } = require("vue");
import VueGoogleMaps from '#fawmi/vue-google-maps';
import * as VueI18n from 'vue-i18n';
import messages from './assets/messages';
import App from './App.vue';
const i18n = VueI18n.createI18n({
locale: 'ar',
fallbackLocale: 'en',
messages,
});
createApp(App)
.use(i18n)
.use(VueGoogleMaps, {
load: {
key: 'myprivatekey',
language: 'ar',
}
})
.mount("#app");
If I want to set language for map by default, it is no problem.
In other files I can set locale dynamically like this
this.$i18n.locale = 'es'
But how I can make it for map component?
Code like below is not working for me (obliviously, because $i18n is not defined here)
.use(i18n)
.use(VueGoogleMaps, {
load: {
key: 'myprivatekey',
language: this.$i18n.locale,
}
})

Load i18n for all components Vue3

When using vue-i18n, I need to add this to every single component:
setup() {
const { t, locale } = useI18n();
return { t, locale };
}
Is there any way to preload the t function to all my components?
You can load and switch languages as below.
in i18n.js
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
}
const locale = localStorage.getItem('language') || 'tr'
export default new VueI18n({
locale: locale,
fallbackLocale: locale,
messages: loadLocaleMessages()
})
in main.js
import i18n from './i18n'
new Vue({
el: "#app",
router,
store,
i18n,
render: h => h(App),
})
createI18n() creates a Vue plugin that makes vue-i18n available globally for all components:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { createI18n } from 'vue-i18n'
const i18n = createI18n(/*...*/)
createApp(App).use(i18n).mount('#app')
t is then available in the template as $t, and locale is $i18n.locale.
demo

How to create a Quasar Framework boot file for vueI18n#next with the Vue Composition API?

We;re trying to install the new vueI18n#next package with Quasar Framework for use with the Vue 2 and the Vue Composition API. The Vue I18n docs mention this:
import { createApp } from 'vue'
import { createI18n, useI18n } from 'vue-i18n'
// call with I18n option
const i18n = createI18n({
legacy: false,
locale: 'ja',
messages: { en: { ... } }
})
const App = {
setup() {
// ...
const { t } = useI18n({ ... })
return { ... , t }
}
}
const app = createApp(App)
app.use(i18n)
app.mount('#app')
When we're trying to translate that to a Quasar Framework boot file we get an error on the app.setup part:
import { boot } from 'quasar/wrappers'
import messages from 'src/i18n'
import Vue from 'vue'
import { createI18n, useI18n, VueI18n } from 'vue-i18n'
declare module 'vue/types/vue' {
interface Vue {
i18n: VueI18n
}
}
const i18n = createI18n({
legacy: false,
locale: 'en-us',
fallbackLocale: 'en-us',
messages,
})
Vue.use(i18n)
export default boot(({ app }) => {
// Set i18n instance on app
app.setup = () => {
const { t } = useI18n()
return { t }
}
})
Error:
What is the correct way to install this?
Quasar is still on Vue 2, not Vue 3. So we're now using the vue-i18n-composable package instead with this boot file:
import { boot } from 'quasar/wrappers'
import { createI18n } from 'vue-i18n-composable'
import messages from 'src/i18n'
import VueI18n from 'vue-i18n'
declare module 'vue/types/vue' {
interface Vue {
i18n: VueI18n
}
}
const i18n = createI18n({
locale: 'en-us',
fallbackLocale: 'en-us',
messages,
})
export default boot(({ app }) => {
app.i18n = i18n
})

vue show page after i18n translations loaded from backend

In my application translations can be edited by admin, so I need to preload them from my backend and initialize in the vue apps I have. So I found that vue i18n package has the ability to lazy load messages (link) . But on their example, they already initialize vue with default (en) messages, but in my case, I do not have default messages preloaded. So I want to load messages from the backend at a moment of creating a new instance of the VueI18n.
Here is the i18n.js file:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import {syncGetData} from "#/apiUtil";
import NProgress from 'nprogress'
import './sass/nprogress.scss'
Vue.use(VueI18n)
let loadedLanguages = []
export const i18n = new VueI18n({
locale: navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE,
fallbackLocale: navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE,
messages: loadLanguage(navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE)
})
function setI18nLanguage (lang) {
i18n.locale = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguage(lang) {
// If the language was already loaded
if (loadedLanguages.includes(lang)) {
return Promise.resolve(setI18nLanguage(lang))
}
NProgress.start();
return syncGetData('/api/translations/' + lang)
.then(function(data) {
setI18nLanguage(lang)
loadedLanguages.push(lang)
return data;
})
.catch(function (error) {
console.error(error)
}).finally(function () {
NProgress.done();
});
}
also here is the main.js (simplified):
import Vue from 'vue'
import App from './App.vue'
import {i18n} from './i18n'
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
The loading of the translations working, but Vue render the template before they loaded, and instead of actual translations I see translation keys. I tried to use v-cloak but it did not help.
So what I want to achieve is, while translations loading users should see only the loading bar (I'm using NProgress, you can see the usage in the i18n.js) and render the app only after translations loaded (maybe not render but initialize). What I'm getting instead: both loading bar and rendered app without actual translations
Thanks in advance!
Updates after Michal LevĂ˝ asnwer
Now, i18n.js looks like this:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import { syncGetData} from "#/apiUtil";
import NProgress from 'nprogress'
import './sass/nprogress.scss'
Vue.use(VueI18n)
let loadedLanguages = [];
export function initI18n() {
const lang = navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE
return syncGetData('/api/translations/' + lang)
.then(function(data) {
console.log(data)
setI18nLanguage(lang)
loadedLanguages.push(lang)
return new VueI18n({
locale: lang,
fallbackLocale: lang,
messages: data
})
})
.catch(function (error) {
console.error(error)
});
}
function setI18nLanguage (lang) {
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguage(lang) {
// If the language was already loaded
if (loadedLanguages.includes(lang)) {
return Promise.resolve(setI18nLanguage(lang))
}
NProgress.start();
return syncGetData('/api/translations/' + lang)
.then(function(data) {
setI18nLanguage(lang)
loadedLanguages.push(lang)
return data;
})
.catch(function (error) {
console.error(error)
}).finally(function () {
NProgress.done();
});
}
and main.js is:
import Vue from 'vue'
import App from './App.vue'
import {initI18n} from './i18n'
import NProgress from "nprogress";
import './sass/nprogress.scss'
NProgress.start();
initI18n().then(function(i18n) {
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
}).finally(function () {
NProgress.done();
});
I can confirm that now messages are loaded before the Vue initalized, b-z in my App.vue I have a console.log in mounted method:
mounted() {
console.log(this.$i18n.messages)
}
and the output of console log is Object { main_title: "Calculation", tab_you: "You", tab_friend: "Your Friend",...} , before it was empty object.
But still <h2 class="title-form">{{ $t('main_title') }}</h2> renders main_title instead of Calculation
Replace VueI18n innitialization with something like this:
export function initI18n() {
const lang = navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE
return loadLanguage(lang).then(function(data) {
return new VueI18n({
locale: lang,
fallbackLocale: lang,
messages: data
})
})
}
main.js
import Vue from 'vue'
import App from './App.vue'
import { initI18n } from './i18n'
initI18n().then(function(i18n) {
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
})
Edit after Q update:
It seems the format of your localization is wrong. VueI18 is able to use multiple languages at once so all your translation keys should be contained in "language object"
Instead of:
{
main_title: "Calculation",
tab_you: "You",
tab_friend: "Your Friend",
...
}
your API should return
{
"en": {
main_title: "Calculation",
tab_you: "You",
tab_friend: "Your Friend",
...
}
}
...where "en" is language identifier
Check your Browser console, I believe VueI18n should warn about missing keys...

Get current locale in a child component

I don't manage to get the locale parameter from vue-i18n in my child component.
I've installed vue-i18n in cli ui. The translation with $t("message") is working but I have error when i try to access to i18n.locale
my enter point (main.js)
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import i18n from './i18n'
new Vue({
router,
i18n,
render: h => h(App)
}).$mount('#app')
my child component
<template>
<div>{{ $t("message") }}</div>
</template>
<script>
import {HTTP} from '#/http-common'
export default
{
name : 'c1',
methods:{
selectMap()
{
console.log(i18n.locale);//=> doesn't work
}
}
</script>
i18n.js
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({
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages()
})
Try this.$i18n.locale, or just $i18n.locale if inside the <template>.
For Composition API this is my solution:
<script setup>
import { useI18n } from "vue-i18n";
const i18nLocale = useI18n();
console.log(i18nLocale.locale.value); // "en"
</script>