• Stars
    star
    278
  • Rank 148,454 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 8 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Translate your Vue.js applications with gettext.

vue-gettext

Translate Vue.js applications with gettext.

Live demo.

No Vue 3 support

This project does not support Vue 3. Please, have a look at vue3-gettext for Vue 3 support.

Contribution

Please make sure to read the Pull request guidelines before making a pull request.

Known issues

Any help is greatly appreciated:

  • It could be tricky to parse some .vue files, see #28
  • Translations in attributes is not supported yet, see #9
  • vue-gettext is not SSR compliant, see #51

Introduction

vue-gettext is a plugin to translate Vue.js applications with gettext. It relies on the GNU gettext toolset and easygettext.

How does vue-gettext work at a high level?

  1. Annotating strings: to make a Vue.js app translatable, you have to annotate the strings you want to translate in your JavaScript code and/or templates.

  2. Extracting strings: once strings are annotated, you have to run extraction tools (gettext-extract and some GNU gettext utilities) to run over a Vue.js app source tree and pulls out all strings marked for translation to create a message file. 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.

  3. Translating message files: a translator needs to fill out the translations of each generated .po files.

  4. Compiling translations: once all message files have been translated, use gettext-compile to make the translated .po files usable in a Vue app. This will basically merge all translated .po files into a unique .json translation file.

  5. Dynamically render translated strings to the DOM: vue-gettext currently uses a custom component for this.

What does vue-gettext provide?

  • a custom component and a custom directive to annotate strings in templates and dynamically render translated strings to the DOM

  • a set of methods to annotate strings in JavaScript code and translate them

  • a language ViewModel exposed to every Vue instances that you can use to:

    • get all available languages (defined at configuration time)

    • get or set the current language (initially defined at configuration time)

    • access whatever you passed to the plugin mixin (defined at configuration time)

  • a global and reactive language property added to Vue.config you can use to get or set the current language outside of Vue instances

What does vue-gettext depend on?

  • easygettext

    • gettext-extract to extract annotated strings from template files and produce a .pot (Portable Object Template) file.

    • gettext-compile to produce the sanitized JSON version of a .po file.

  • Some GNU gettext utilities to extract annotated strings from JavaScript files and generate .po files

Those tools should be integrated in your build process. We'll show you an example later.

Installation

NPM

npm install vue-gettext

Basic installation

Basic installation with ES6 modules:

// ES6
import Vue from 'vue'
import GetTextPlugin from 'vue-gettext'
import translations from './path/to/translations.json'

Vue.use(GetTextPlugin, {translations: translations})

Configuration

There are a number of options you can use to configure the vue-gettext plugin:

Option Type Requirement Description
autoAddKeyAttributes {Boolean} optional If true, key attributes are auto-generated if not present in your code. See the key documentation and issues #29 and #66. Default value is false. Enable this option only if you know what you're doing.
availableLanguages {Object} optional 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
defaultLanguage {String} optional 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
muteLanguages {Array} optional Discard warnings for missing translations for all languages of the list. This is useful to avoid messages from the language used in source code.
languageVmMixin {Object} optional A mixin that will be passed to the main languageVm instance (exposed via $language) that can be used, for example, to add custom computed properties
silent {Boolean} optional Enable or disable logs/warnings for missing translations and untranslated keys. Default value is Vue.config.silent.
translations {Object} required The JSON file of the application's translations (produced by gettext-compile). It's exposed as a Vue global property as Vue.$translations

The key special attribute is primarily used as a hint for Vue's virtual DOM algorithm to identify VNodes when diffing ... Vue uses an algorithm that minimizes element movement and tries to patch/reuse elements of the same type in-place as much as possible.

Example:

// ES6
import Vue from 'vue'
import GetTextPlugin from 'vue-gettext'
import translations from './path/to/translations.json'

Vue.use(GetTextPlugin, {
  availableLanguages: {
    en_GB: 'British English',
    en_US: 'American English',
    es_US: 'Español',
    fr_FR: 'Français',
    it_IT: 'Italiano',
  },
  defaultLanguage: 'fr_FR',
  languageVmMixin: {
    computed: {
      currentKebabCase: function () {
        return this.current.toLowerCase().replace('_', '-')
      },
    },
  },
  translations: translations,
  silent: true,
})

vm.$language

After the plugin initialization, a languageVm Vue instance is injected into every component as vm.$language.

It exposes the following properties:

  • vm.$language.available: an object that represents the list of the available languages (defined at configuration time)

  • vm.$language.current: the current language (defined at configuration time)

  • whatever you passed to the plugin mixin

You can use vm.$language.current and vm.$language.available to e.g. easily build a language switch component with a single template:

<template>
  <div>
    <select name="language" v-model="$language.current">
      <option v-for="(language, key) in $language.available" :value="key">{{ language }}</option>
    </select>
  </div>
</template>

Vue.config.language

After the plugin initialization, a global and reactive language property is added to Vue.config that you can use to get or set the current language outside of Vue instances.

> Vue.config.language
'en_GB'
> Vue.config.language = 'fr_FR'

You can use Vue.config.language to e.g. configure a third party plugin in a filter:

import moment from 'moment'
import Vue from 'vue'

const dateFormat = function (value, formatString) {
  moment.locale(Vue.config.language)
  return moment(value).format(arguments.length > 1 ? formatString : 'dddd D MMMM HH:mm:ss')
}

Workflow

  1. Annotate your strings

  2. Extract translations (make makemessages)

  3. Translate message files

  4. Compile translations (make translations)

   Annotate    |       Extract        |              Translate                 |        Compile
--------------------------------------------------------------------------------------------------------
component.js
component.vue ---> /tmp/template.pot ---> app/locale/fr_FR/LC_MESSAGES/app.po ---> app/translations.json
template.html

1a) 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>

Comment

<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 support

Since interpolation inside attributes are deprecated in Vue 2, we have to use another set of delimiters. Instead of the "Mustache" syntax (double curly braces), we use %{ and }:

<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 v-translate with interpolation

It's not possible (yet) to detect changes on the parent component's data, so you have to add an expression to the directive to provide a changing binding value. This is so that it can do a comparison on old and current value before running the translation in its update hook.

It is described in the official guide:

update: called after the containing component has updated, but possibly before its children have updated. The directive's value may or may not have changed, but you can skip unnecessary updates by comparing the binding's current and old values...

<p
  v-translate='{count: count, brand: brand}'
  :translate-n="count"
  translate-plural="<strong>%{ count }</strong> %{brand} cars"
  translate-comment="My comment for translators"
  >
  <strong>%{ count }</strong> %{brand} car
</p>

Caveat when using either the component <translate> or directive v-translate with interpolation inside v-for

It's not possible (yet) 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 (yet) 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 attached to Vue.prototype.

Singular

vm.$gettext(msgid)

Plural

vm.$ngettext(msgid, plural, n)

Context

vm.$pgettext(context, msgid)

Context + Plural

vm.$npgettext(context, msgid, plural, n)

Interpolation support

You can use interpolation in your JavaScript using another method attached to Vue.prototype: vm.$gettextInterpolate.

...
methods: {
  alertPlural (n) {
    let translated = this.$ngettext('%{ n } foo', '%{ n } foos', n)
    let interpolated = this.$gettextInterpolate(translated, {n: n})
    return window.alert(interpolated)
  },
},
...

vm.$gettextInterpolate dynamically populates a translation string with a given context object.

2) Extracting strings

This should be a step in your build process and this can be done in several ways.

Here are the things we must do:

  1. extracting annotated strings from templates (.html and/or .vue files),

  2. extracting annotated strings from JavaScript code (.js and/or .vue files),

  3. creating a main .pot template based on the extracted strings,

  4. creating editable .po files for each available language.

You'll need to install easygettext and use gettext-extract to extract annotated strings from template files and produce a .pot file.

You'll also need some GNU gettext utilities, namely msgmerge, msginit and msgattrib to generate .po files from the .pot dictionary file.

We use a Makefile with a makemessages target to automate this step. To give you an example, I included a Makefile with a makemessages target in this project that you can include in your build process.

Extracting strings and generating .po files becomes as easy as running:

make makemessages

3) Translating message files

The translator needs to fill out the translations of each generated .po files.

This can be done by you or outsourced to other firms or individuals since .po files are the industry standard for multilingual websites.

There is also a wide range of translation tools available in the gettext ecosystem. Some of them are listed on Wikipedia.

4) Compiling translations

This step focuses on making the translated .po files usable in your Vue.js app.

Once translated, install easygettext and use gettext-compile to merge all translated .po files into a unique .json translation file.

Embed the .json translation file back into your application. This is done only one time at vue-gettext configuration time.

We use a Makefile with a translations target to automate this step.

Compiling translations becomes as easy as running:

make translations

Look at the included Makefile for an example.

Usage of translate without Vue

For convenience translate can be imported directly in JavaScript files for cases where you need the translations from translations.json outside of vue-gettext (see #113):

import {translate} from 'vue-gettext';

const {gettext: $gettext, gettextInterpolate} = translate;

const str = $gettext('Hello, %{name}');
const strFR = $gettext('Hello, %{name}', 'fr');
const interpolated = gettextInterpolate(str, { name: 'Jerom' })

Elsewhere

Support for Pug templates

If you are using a template language, i.e. Pug.js in Single File Component within a webpack setup (using vue-loader), have a look at vue-webpack-gettext.

Credits

This plugin was inspired by:

License

MIT

More Repositories

1

aioamqp

AMQP implementation using asyncio
Python
273
star
2

zbarlight

A simple wrapper for zbar
Python
163
star
3

easygettext

Simple gettext tokens extraction tools for HTML and Jade files.
JavaScript
79
star
4

libcanardbc

Library to read DBC files of CAN bus networks
Yacc
74
star
5

django-cid

Correlation Id for Django
Python
36
star
6

systematic

An opinionated ES6 toolchain for the browser.
JavaScript
25
star
7

xlsx_streaming

Stream data as xlsx
Python
22
star
8

caneton

Offspring of libcanardbc to decode CAN messages from Python code or CLI
Python
20
star
9

getconf

Simple Python configuration utilities.
Python
18
star
10

django-mds

Python
15
star
11

sentry-sourcemaps

Seamlessly upload your source maps to Sentry; comes with a CLI interface.
JavaScript
15
star
12

grocker

Grocker, docker image builder
Python
11
star
13

wireshark-canvas

This repository is intended to help using the CANvas plugin for Wireshark on Linux.
Shell
11
star
14

react-bubble

A simple and customizable bubble for react
JavaScript
9
star
15

django-select2-rocks

Yet another integration glue between Django and Select2 JS
JavaScript
9
star
16

flow-inlinestyle

JavaScript
5
star
17

pyhound

A command-line client for the Hound source code search engine.
Python
5
star
18

dokang

Lightweight web document repository with a search engine
Python
5
star
19

react-doks

React static documentation generator, aimed at merging the development and the documentation workflow
JavaScript
5
star
20

check-oldies

The developer companion that warns about unattended FIXME and TODO annotations
Python
4
star
21

docker-registry-purger

A simple cleaner for private docker-registries
Python
4
star
22

bluepicker

Vanilla date, time, and range picker with ergonomic input
JavaScript
4
star
23

try-hard

tryHard is a convenient tool when writing your Javascript tests and you need to test asynchronous state changes with no control on the timing
JavaScript
3
star
24

ansibilo

Set of tools for Ansible
Python
3
star
25

devpi-remote_user

Python
2
star
26

react-hooks-by-example

TypeScript
2
star
27

papaya

LDAP-backed Django authentication module, including openid.
Python
2
star
28

sphinx-dust

Sphinx extension to help keep docs up-to-date
Python
1
star
29

bizdatim

Library for business hour arithmetic. Fork from https://bitbucket.org/shelldweller/python-bizdatetime.
Python
1
star
30

k8s-proxy-image-swapper

Go
1
star
31

mds-agency-validator

HTTP validator for MDS Agency API
Python
1
star
32

curtis

A simple sentry cli
Python
1
star
33

pylint-strict-informational

Python
1
star
34

cogite

A command line GitHub/GitLab helper for your pull requests
Python
1
star
35

redux-waitfor-middleware

JavaScript
1
star
36

granadilla

LDAP management tools
Python
1
star
37

karma-webpack-errors-reporter

WIP: A better Webpack error reporting plugin.
JavaScript
1
star
38

eslint-plugin-typeorm

JavaScript
1
star