Vue 2.7, Vue 3, Nuxt 3, Vite 5
npm i vue-eyein-translation
For the locale to be reactive to changes you must provide a Ref-Like Object as a localeState.
src/main.js
import { createApp, ref } from 'vue'
import loadVueEyeinTranslation from "vue-eyein-translation/vue3.js";
import eyeinTranslationConfig from "../eyein-translation.config.js";
const app = createApp({})
eyeinTranslationConfig.localeState = ref(null);
// asynchronously load plugin
loadVueEyeinTranslation(eyeinTranslationConfig)
.then(vueEyeinTranslation => {
app.use(vueEyeinTranslation);
app.mount('#app');
})
src/main.js
import Vue from 'vue'
import loadVueEyeinTranslation from "vue-eyein-translation/vue2.js";
import eyeinTranslationConfig from "../eyein-translation.config.js";
eyeinTranslationConfig.localeState = Vue.observable({
value: null
});
// asynchronously load plugin
loadVueEyeinTranslation(eyeinTranslationConfig)
.then(vueEyeinTranslation => {
Vue.use(vueEyeinTranslation);
new Vue({
render: h => h(App)
}).$mount(`#app`);
})
nuxt.config.js
export default defineNuxtConfig({
// ...
modules: [
// ...
[`vue-eyein-translation`, {
locales: [`en-US`, `fr-CA`, `es-ES`],
inlineLocales: `en-US||fr-CA`,
assetsDir: `assets`,
additionalLocalesDirs: [`vue-components/locales`],
autoTranslate: {
locales: [`es-ES`],
async translationFunction(fromLocale, toLocale, textsToTranslate) {
const resp = await fetch(`http://localhost:3000/translate`, {
method: `POST`,
headers: {
"Content-Type": `application/json`
},
body: JSON.stringify({
texts: textsToTranslate,
from: fromLocale,
to: toLocale
})
});
return resp.json();
}
}
}]
]
// ...
})
Note: This step is not necessary for Nuxt 3
vite.config.js
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue2'
import viteEyeinTranslation from "vue-eyein-translation/vite-plugin-vue-eyein-translation.js";
import eyeinTranslationConfig from "./eyein-translation.config.js";
// https://vitejs.dev/config/
export default defineConfig(config => {
return {
// ...
plugins: [
vue(),
viteEyeinTranslation(eyeinTranslationConfig),
// ...
]
// ...
}
})
You can create a file eyein-translation.config.js
in the root directory
export default {
locales: [`en-US`, `fr-FR`, `es-ES`], // project will be translated in 3 languages: english, french and spanish
inlineLocales: `en-US||fr-FR`, // locales that will be inlined (No latency when locale changed, but higher bundle size)
additionalLocalesDirs: [ // Additional directories with locales files
`locales`
],
warnMissingTranslations: true
};
- All locale files will be in
/src/assets/locales
in JSON format - First inline locale will be used as fallback locale when no translation is found. In this example,
en-US
is the fallback locale
For Nuxt: You can then import it when you load the plugin:
import eyeinTranslationConfig from "../eyein-translation.config.js";
// main.js
loadVueEyeinTranslation(eyeinTranslationConfig)
// vite.config.js
viteEyeinTranslation(eyeinTranslationConfig)
// nuxt.config.js
[`vue-eyein-translation`, eyeinTranslationConfig]
You can configure auto translation with autoTranslate object in eyein-translation.config.js
file with something like this:
export default {
locales: [`en-US`, `fr-FR`, `es-ES`],
inlineLocales: `en-US||fr-FR`,
additionalLocalesDirs: [
`locales`
],
warnMissingTranslations: true,
autoTranslate: {
locales: [`es-ES`],
async translationFunction(fromLocale, toLocale, textsToTranslate) {
const resp = await fetch(`<your translator endpoint>`, {
method: `POST`,
headers: {
"Content-Type": `application/json`
},
body: JSON.stringify({
texts: textsToTranslate,
from: fromLocale,
to: toLocale
})
});
return resp.json();
}
}
};
- Translation inside Vue template, with custom
<t>
component:
<button><t>Click Me!</t></button>
- Translation of attributes, with
v-t
directive:
<input type="text" placeholder="Text to translate" v-t:placeholder title="My title" v-t:title>
You can also use a shorthand for multiple attribute with .
like this:
<input type="text" placeholder="Text to translate" title="My title" v-t.placeholder.title>
Another shorthand using .t
after the attribute name (But your IDE might alert you about unknown or missing attribute):
<input type="text" placeholder.t="Text to translate" title.t="My title">
Note: You cannot apply filters with shorthands. You must use the v-t:
syntax like this: v-t:title.upper
- Translation of hardcoded string inside Javascript, with
createTranslation
// Options API
export default {
name: `my-component`,
computed: { // use a computed property if you want automatic language switch when user change locale
jsTranslation() {
return this.createTranslation(`Javascript translation||Traduction dans le Javascript`)
}
}
}
// Composition API
const createTranslation = inject(`createTranslation`)
const jsTranslation = computed(() => createTranslation(`Javascript translation||Traduction dans le Javascript`))
- Translation objects (for example: from a database):
<template>
<h1 v-t:title="jsTranslationObject"> <!-- directive usage -->
<t :value="jsTranslationObject"></t> <!-- innerHtml usage -->
</h1>
</template>
<script>
export default {
name: `my-component`,
data() {
// Object format: {"<locale>": "<translation>"}
return {
jsTranslationObject: {"en": `English translation`, "fr-FR": `Traduction française`}
};
},
mounted() {
// javascript usage
console.log(this.tr(this.jsTranslationObject));
}
}
</script>
// Composition API
const tr = inject(`tr`);
console.log(tr(jsTranslationObject));
// with locale watch:
const tr = inject(`tr`);
const locale = inject(`locale`);
const watchedRef = computed(() => tr(jsTranslationObject, null, locale));
-
tr(translationObject, data = null, locale = null)
: Returns the translation with the given locale (current locale by default) -
getLocales()
: Returns the list of available locales -
getLocale()
: Returns the current locale in use -
getLocaleTranslations()
: Return the content of the translation file for current locale -
setLocale(locale)
: Change the current locale -
createTranslation()
: Tells the compiler to generate a translation entry inside
-
tr(translationObject, data = null, locale = null)
: Returns the translation with the given locale (current locale by default) -
getLocales()
: Returns the list of available locales -
useLocale()
: Returns the current locale as a cookie ref that can be changed to load other locales -
getLocaleTranslations()
: Return the content of the translation file for current locale -
createTranslation()
: Tells the compiler to generate a translation entry inside
-
<t>
: translation component
If you configured the plugin with multiple inline locales like inlineLocales: "en-US||fr-FR"
instead of just inlineLocales: "en-US"
,
you must add all translation inline while you are coding. You can do it using ||
:
<button><t>Close||Fermer</t></button>
This will consider the first part (before ||
) to be the english translation and the second part french translation.
You can add context to your translation by adding a meaning (ie a short description destined to the translator to give him the context in which the text appears). The text will be discarded of the resulting source code and be only in the translations files.
Example:
<button><t>Close||Fermer##Text of a button used to close a modal window</t></button>
The string Text of a button used to close a modal window
will be added to all translations files as a context for the translator.
This plugin generate an ID based on a combination of the fallback locale text and the meaning. For the previous example:
<button><t>Close||Fermer##Text of a button used to close a modal window</t></button>
The generated ID will be a hash of Close
+ Text of a button used to close a modal window
.
Note that the Fermer
text of the second inline locale is not used to generate the id.
For some situations, you want to create a custom ID. For example, for common translations between projects with the additionalLocalesDirs
feature.
You can create custom translation ids:
fr-FR.json
{
"ok": {
"source": "ok",
"target": "ok"
},
"cancel": {
"source": "cancel",
"target": "annuler"
}
}
And use it like this in the code:
<t>@@ok</t>
Markdown is supported inside translation to apply some styling or features (only in <t>
component):
<t>**bold** *italic* _underline_ ***italic and bold*** ~~strikethrough~~ ==highlighted==, H~2~O, x^2^</t>
<t>Click on [this link](https://example.com)</t>
As a result some characters must be escaped in translations:
<t>These characters should be escaped: \~ \= \* \_ \^</t>
You can add some props or modifiers to change your translations. This can be useful for your common translations used in a lot of places.
Examples:
In <t>
component:
<p><t upper>@@ok</t> will print OK</p>
<p><t lower>@@ok</t> will print ok</p>
<p><t capitalize>@@ok</t> will print Ok</p>
<p><t lower capitalize>OK</t> will print Ok</p>
In v-t
directive:
<p title="@@ok" v-t:title.upper> will print OK</p>
<p title="@@ok" v-t:title.lower> will print ok</p>
<p title="@@ok" v-t:title.capitalize> will print Ok</p>
<p title="OK" v-t:title.lower.capitalize> will print Ok</p>
You can have only one translation for the work "ok" but use filter to change the case.
Note: You cannot use the shorthand v-t.title.placeholder
if you use filters you must use v-t:title.upper v-t:placeholder.lower
- You can use
:d
prop of<t>
component to add data binding using{yourVariable}
inside the text
<t :d="{link: `https://example.com`}">Click on [this link]({link})</t>
Note: You must declare all your bindings in the :d
prop. You cannot use Vue default data bindind with {{yourVariable}}
.
- Data binding can be done inside attributes too:
<input v-t.title.placeholder="{world: `world`}" title="Hello {world}" placeholder="Another Hello {world}">
- Data binding inside javascript:
let world = `world`;
createTranslation(`Hello {w}`, {w: world});
- Translation object:
let translationFromDatabase = {en: `Hello {w}`};
console.log(this.tr(translationFromDatabase, {w: world}));
If the refs have the same name as what you want in your translations files:
<template>
<a v-t.title title="Hello {world}!">
<t>Hello {world}!</t>
</a>
</template>
<script setup>
const world = ref(`world`);
</script>
is equivalent of:
<template>
<p v-t.title="{world}" title="Hello {world}">
<t :d="{world}">Implicit data binding: {world}</t>
</p>
</template>
<script setup>
const world = ref(`world`);
</script>
Note: You can't mix implicit and explicit data binding. If you add a :d
property anything not defined inside the d property will be undefined.
You can use some filters to internationalize some of your data:
<t :d="{str1: `hello`, str2: `TRANSLATION`, str3: `world`}">{str1|capitalize} {str2|lower} {str3|upper}</t>
Result:
Hello translation WORLD
Note: filters work in directives too
<p v-t:title="{n: 2_000_000}" title="{n|number}"></p>
Result for some languages:
en-US: 2,000,000
fr-FR: 2 000 000
es-ES: 2.000.000
<t :d="{dateNow: new Date()}">Formatted date: {dateNow|dateLong}</t>
Result for some languages:
en-US: March 25, 2024 at 3:37 PM
fr-FR: lundi 25 mars 2024 à 15 h 37
es-ES: lunes, 25 de marzo de 2024, 15:37
See this link for details
Syntax:
{case for zero|case for one|case for two/few/many/other}
or
{case for zero|case for one|case for two|case for few|case for many|case for other}
<t :d="{n: 0}">{no time|1 minute|{n} minutes} left</p>
<t :d="{n: 1}">{no time|1 minute|{n} minutes} left</p>
<t :d="{n: 10}">{no time|1 minute|{n} minutes} left</p>
Result:
no time left
1 minute left
10 minutes left
<t :d="{n1: 1, n2: 2, n3: 3, n4: 4, n103: 103}">Ordinals: {n1|th} {n2|th} {n3|th} {n4|th} {n103|th}</t>
Result:
Ordinals: 1st 2nd 3rd 4th 103rd
Important: Ordinals must be configured inside translations files: /src/assets/locales/en-US.json
{
"$ordinal": {
"one": "st",
"two": "nd",
"few": "rd",
"other": "th"
},
"...": "..."
}
/src/assets/locales/fr-FR.json
{
"$ordinal": {
"one": "^er^",
"two": "^nd^",
"few": "^ème^",
"other": "^ème^"
},
"...": "..."
}
Note: you can use Markdown