vue3-gettext 💬
Translate Vue.js applications with gettext.
Table of Contents
- Installation
- Basic usage
- Workflow
- Configuration
- Annotating strings
- Message extraction and compilation
- Dependencies
- Contribute
- Credits
- License
Installation
npm i @jshmrtn/vue3-gettext
npm i -D easygettext
main.js
import { createGettext } from "@jshmrtn/vue3-gettext";
import { createApp } from "vue";
import translations from "./path/to/translations.json";
const gettext = createGettext({
availableLanguages: {
en_GB: "British English",
},
defaultLanguage: "en_GB",
translations,
});
const app = createApp(App);
app.use(gettext);
Basic usage
Use the component or directive to annotate translatable strings:
<translate>Hello!</translate>
<span v-translate>Hello!</span>
Or inject the plugin using useGettext
(Example of a language switcher):
<template>
<div>
<select v-model="language.current">
<option v-for="(language, key) in language.available" :key="key" :value="key">{{ language }}</option>
</select>
</div>
</template>
<script>
import { useGettext } from "@jshmrtn/vue3-gettext";
export default {
setup() {
const language = useGettext();
return {
language,
};
},
};
</script>
Workflow
-
Annotate strings: annotate all the translatable strings in your project using the <translate>
component, the v-translate
directive or by calling the gettext functions (gettext
, pgettext
, ngettext
, npgettext
) directly.
-
Extract strings: you can now extract all strings to create message files. A message file is just a plain-text file with a .po
file extension, representing a single language, that contains all available translation strings as keys and how they should be represented in the given language.
vue3-gettext
provides scripts to make this straightforward. Take a look at the Message extraction and compilation section.
-
Translate message files: a translator needs to fill out the translations of each generated .po
files. I recommend you use software like poedit (some alternatives are listed on wikipedia here).
-
Compile translations: once all message files have been translated, use gettext-compile
to make the translated .po
files usable in a Vue app. This will merge all translated .po
files into a .json
file ready to be used by vue3-gettext
.
Configuration
The options you can pass to createGettext
are:
availableLanguages
Type: { [key: string]: string }
Default: { en_US: "English" }
An object that represents the list of the available languages for the app whose keys are local names (e.g. en
or en_US
) and whose values are language names used for the display in UI, e.g. English (United States)
. It's exposed in all Vue instances via vm.$language.available
Example
availableLanguages: {
en_GB: "British English",
fr_FR: "Français",
it_IT: "Italiano",
},
defaultLanguage
Type: string
Default: 'en_US'
The local name of the default language, e.g. en_US
. This will be the current active language. It's exposed in all Vue instances via vm.$language.current
Example
defaultLanguage: 'en_GB',
translations
Type: { [key: string]: { [key: string]: any } }
Default: {}
The JSON file of the application's translations (produced by gettext-compile
).
Example
translations: {
"en_Use": {
"Color": "Color",
...
},
"de_DE": {
"Color": "Farbe",
...
}
},
mutedLanguages
Type: string[]
Default: []
Discard warnings for missing translations for all languages of the list. This is useful to avoid messages from the language used in source code.
Example
mutedLanguages: ['fr_FR', 'de'],
silent
Type: boolean
Default: false
Enable or disable logs/warnings for missing translations and untranslated keys.
Example
silent: true,
setGlobalProperties
Type: boolean
Default: true
Sets the options $gettext
$pgettext
$ngettext
$npgettext
$gettextInterpolate
$language
on app.config.globalProperties
, making the globally available in your templates. It is not recommended to disable this as easygettext
will not extract strings if your functions are not named exactly like this.
provideDirective
Type: boolean
Default: true
Registers the v-translate
directive on you application. If you disable this option and want to provide the directive yourself, you must make sure to name it translate
otherwise extraction will fail.
provideComponent
Type: boolean
Default: true
Registers the <translate>
component on you application. If you disable this option and want to provide the component yourself, you must make sure to name it translate
otherwise extraction will fail.
Full configuration example:
import { createGettext } from "@jshmrtn/vue3-gettext";
import { createApp } from "vue";
import translations from "./path/to/translations.json";
const gettext = createGettext({
availableLanguages: {
en_GB: "British English",
fr_FR: "Français",
it_IT: "Italiano",
},
defaultLanguage: "en_GB",
mutedLanguages: ["fr_FR"]
translations,
silent: true,
});
const app = createApp(App);
app.use(gettext);
Dependencies
Message extraction and compilation
vue3-gettext
exposes two scripts to simplify this process:
The extract script will create an output directory containing a .pot
file, directories and a .po
file for each locale. You can now edit the .po
files to translate your app before compiling them.
You may set a source directory to extract messages from, an output directory and a comma separated list of all the locales in your application.
Example call:
vue-gettext-extract --src ./src --out ./src/language --locales "de_CH,en_US"
vue-gettext-compile
The compile script will merge the contents of all the .po
files and combine them into a single translations.json
file that you can use with vue3-gettext
(in the createGettext
function).
You may set the directory where all your locales are located (the --out
directory of the extract script) and the locales
Example call:
vue-gettext-compile --dir ./src/language --locales "de_CH,en_US"
Recommended setup
We recommend setting up the scripts in your package.json
like this:
"scripts": {
"gettext": "npm run gettext:extract && npm run gettext:compile",
"gettext:extract": "vue-gettext-extract --src ./src --out ./src/language --locales \"de_CH,en_US\"",
"gettext:compile": "vue-gettext-compile --dir ./src/language --locales \"de_CH,en_US\""
},
Annotating strings in templates (.html
or .vue
files)
Use the component or the directive
Strings are marked as translatable in your templates using either the translate
component or the v-translate
directive:
<translate>Hello!</translate> <span v-translate>Hello!</span>
This will automatically be translated. For instance, in French, it might read Bonjour!.
Singular
<translate>Hello!</translate>
Plural
<translate :translate-n="count" translate-plural="%{ count } cars">%{ count } car</translate>
Context
<translate translate-context="Verb">Foo</translate>
<translate translate-comment="My comment for translators">Foo</translate>
Custom parameters
You can set up translation strings that are agnostic to how your app state is structured. This way you can change variable names within your app, it won't break your translation strings.
<translate :translate-params="{name: userFullName}">Foo %{name}</translate>
HTML support: difference between the component and the directive
It proves to be tricky to support interpolation with HTML content in Vue.js components because it's hard to access the raw content of the component itself.
So if you need to include HTML content in your translations you may use the directive.
The directive has the same set of capabilities as the component, except for translate-params which should be passed in as an expression.
<p
v-translate="{count: carNumbers}"
:translate-n="carNumbers"
translate-plural="<strong>%{ count }</strong> cars"
translate-comment="My comment for translators"
>
<strong>%{ count }</strong> car
</p>
Custom HTML tag for the translate
component
When rendered, the content of the translate
component will be wrapped in a span
element by default. You can also use another tag:
<translate tag="h1">Hello!</translate>
Interpolation
You can use the tokens %{
and }
to for string interpolation within messages:
<translate>Hello %{ name }</translate>
Directive, interpolation and raw HTML in data
Raw HTML in data is interpreted as plain text, not HTML. In order to output real HTML, you will need to use the render-html
attribute and set it to true
.
<p v-translate render-html="true">Hello %{ openingTag }%{ name }%{ closingTag }</p>
Dynamically rendering arbitrary HTML on your website can be very dangerous because it can easily lead to XSS vulnerabilities. Only use HTML render-html="true"
on trusted content and never on user-provided content.
Caveats
Caveat when using either the component <translate>
or directive v-translate
with interpolation inside v-for
It's not possible to access the scope within v-for
, example:
<p>
<translate v-for="name in names">Hello %{name}</translate>
<span v-for="name in names" v-translate>Hello %{name}</span>
</p>
Will result in all Hello %{name}
being rendered as Hello name
.
You need to pass in custom parameters for it to work:
<p>
<translate v-for="name in names" :translate-params="{name: name}">Hello %{name}</translate>
<span v-for="name in names" v-translate="{name: name}">Hello %{name}</span>
</p>
Caveat when using v-translate
with Vue components or Vue specific attributes
It's not possible to support components or attributes like v-bind
and v-on
. So make sure that your HTML translations stay basic for now.
For example, this is not supported:
<p v-translate>Please <button @click="doSomething">click</button> here to view <my-account></my-account></p>
1b) Annotating strings in JavaScript code (.js
or .vue
files)
Strings are marked as translatable in your Vue instances JavaScript code using methods from the plugin.
Singular
const { $gettext } = useGettext();
$gettext(msgid);
Plural
const { $ngettext } = useGettext();
$ngettext(msgid, plural, n);
Context
const { $pgettext } = useGettext();
$pgettext(context, msgid);
Context + Plural
const { $npgettext } = useGettext();
$npgettext(context, msgid, plural, n);
Interpolation support
You can use interpolation in your JavaScript using the method interpolate
in combination with one of the annotation functions.
const { $ngettext, interpolate } = useGettext();
const translated = $ngettext("%{ n } foo", "%{ n } foos", n);
const interpolated = interpolate(translated, { n: n });
interpolate
dynamically populates a translation string with a given context object.
Contribute
Please make sure your code is properly formatted (the project contains a prettier
config) and all the tests run successfully (npm run test
) when opening a pull request.
Please specify clearly what you changed and why.
Credits
This plugin heavily relies on the work of the original vue-gettext
.
License
MIT