Performance optimization of vue-i18n

TL;DR

- vue-i18n provide some ways translation performance improvements.
- Translation performance is `compiler module > custom directive v-t > $t method`.

Introduction

vue-i18n is an internationalization plugin for Vue.js developed inspired by i18n of Ruby on Rails. As well as I18n.t of Ruby on Rails, Beside the simple translation with $t, in currently, Vue applications can be translated by various methods such as Pluralization, DateTime / Number localization, and Component Interpolation.

However, in especially $t which we use the most, vue-i18n have problem affecting the rendering performance of Vue. It’s a problem that every time the re-rendering occurs in the Vue components, it will execute the translation proccess each time. For this reason, performance doesn’t get good results in Vue applications require it.

vue-i18n v7.3 or later, In order to solve such a problem, we support some options that user allow to optimize translation performance!


Custom Directive: v-t

vue-i18n provides an option to translate using the custom directive v-t.

How to usage

It’s built-in by default to vue-i18n, so you don’t need to install separately it. vue-i18n installation and after that execute Vue.use(VueI18n), you can translate immediately with using v-t.

the below translation example use v-t:

  • string syntax
<!-- string literal -->
<p v-t="'hello'"></p>
<!-- keypath binding with data or computed props -->
<p v-t="path"></p>
  • object syntax
<!-- plain object syntax -->
<p v-t="{ path: 'hello', locale: 'ja', args: { name: 'kazupon' } }"></p>
<!-- binding with data or computed props -->
<p v-t="{ path: path, args: { name: nickName } }"></p>

In about details of v-t, see the following documents:

With v-t as above, execute translation processing, when custom directive bind (bind hook function executed). As the result, it’s inserted into the textContent of the target DOM element.

Mechanism to reduce translation processing cost

The translation with $t was executed with the render function executed inside Vue (compiled in the case of template). In contrast, translation with v-t is hooked in the patch of Virtual DOM. Therefore, the execution timing of the translation process differs.

In Vue custom directive, when re-rendering occurs in the Vue components, some processing is executed with update hook function. In v-t, as well as $t, due to have some use cases of translation with reactive data, it also make translation processing in update hook function.

In Vue custom directive, the current value (binding.value) and the previous value (binding.oldValue) bound to custom directive are passed to hookable functions such as update hook.

In v-t, if these values ​​are the same with comparing these values, it will return and exit the hook function processing halfway. In this way, vue-i18n reduce the translation cost so as not to degrade the performance of Vue applications.

Server-Side Rendering (SSR)

In order to support SSR of Vue applications with using v-t, you need to install separately vue-i18n-extensions module.

$ npm install --save-dev vue-i18n-extensions

After installation, on the server-side, it’s specified in directives option of createRenderer function which creates a renderer as follows:

import { createRenderer } from 'vue-server-renderer'
import { directive as t } from 'vue-i18n-extensions'
// something setup ...
const renderer = createRenderer({ directives: { t } })
// something rendering ...

In about details of custom directive v-t for SSR, see the documents.

And also published an example of SSR, you may want to try it. 😉


Pre-translation with compiler module

vue-i18n provides a compiler module that enables pre-translation of custom directive v-t, using the compilation hooking mechanism provided with Vue Compiler.

Pre-translation is very effective due to it can maximize performance in Vue applications with static internationalization resources.

How to usage

To use pre-translation, you need to implementation like the following codes:

// Imports some modules
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import { module } from 'vue-i18n-extensions'
// Install VueI18n
Vue.use(VueI18n)
// Create VueI18n instance
const i18n = new VueI18n({
// ...
})
// Wrap with VueI18n instance
const i18nModule = module(i18n)

// Specify to `module` option of Vue compiler
// ...

Import Vue and VueI18n (in ES2015 use import, in CommonJS use require), install VueI18n with Vue.use, and create a VueI18n instance. So far, it’s the same as internationalization in Vue applications.

Import a module function that returns a compiler module from vue-i18n-extensions. This function returns the compiler module to translate within the Vue compiler. For this reason, it’s necessary to specify the VueI18n instance as an argument.

When you’ll be executing the module function with VueI18n instance specified, you can get the required hookable compiler module that need with Vue compiler.

In currently, there are following ways as to how to pre-translate using it.

  1. vue-template-compiler
  2. vue-loader / rollup-plugin-vue

The following is an example using vue-template-compiler.

import Vue from 'vue'
import VueI18n from 'vue-i18n'
import { compile } from 'vue-template-compiler'
import { module } from 'vue-i18n-extensions'

Vue.use(VueI18n)

const i18n = new VueI18n({
// ...
})
const i18nModule = module(i18n)

const { ast, render } = compile(`<p v-t="'hello'"></p>`, { modules: [i18nModule] })
console.log(ast.i18n) // output -> 'hello'
console.log(render) // output -> `with(this){return _c('p',{domProps:{"textContent":_s("hello")}})}`
// something do for `ast`, `render`
// ...

In order to vue-template-compiler is fairly low level, it takes considerable effort to build Vue applications using it. For this reason, it is common to use vue-loader / rollup-plugin-vue for the bundling tool are provided with Vue.js official.

When using vue-loader, the configuration of webpack is as follows:

const Vue = require('vue')
const VueI18n = require('vue-i18n')
const i18nExtensions = require('vue-i18n-extensions')
Vue.use(VueI18n)

const i18n = new VueI18n({
// ...
})

module.exports = {
module: {
rules: [{
test: /\.vue$/,
loader: 'vue',
options: {
compilerModules: [i18nExtensions.module(i18n)],
// other vue-loader options go here
loaders: {}
}
}]
}
}

As described above, when you are specifying the compiler module in compilerModules, vue-loader processes the low level part with using vue-template-compiler.

You can easy setup Vue application building enviroments with using webpack / vue-loader and vue-cli due to provided templates with Vue.js official. If you would like to use pre-translation, we recommend to use it.

See the example of pre-translation with vue-loader and rollup-plugin-vue the belows:

Mechanism to pre-translate

Pre-translation supported with vue-i18n is realized with using Vue compiler hookings mechanism as shown in the figure below:

From ‘Vue.js Extend with Compiler’

Vue compiler will be compile the template with the following flows:

  • HTML parser
    Parse the template, and generate AST Node Trees.
  • Optimizer
    Optimize for AST Node Trees.
  • Code Generator
    Generate render functions(render / staticRenderFns) from optimized AST Node Trees.

In each of the above processes, the Vue compiler support hookings.

The compiler module provided by vue-i18n-extensions will handle the hook function like the below:

  • transformNode
    Detect v-t, obtain the values of directive, and attach to AST Node.
  • genData
    Obtain the value of directive attached to AST Node with transformNode, translate with it, and set the translation result to the textContent key of domProps of VNodeData.

If domProps exists in VNode data at the patch of Virtual DOM, domProps is applied as an attribute value to the DOM element.

There are various built-in directives supported in Vue core. As an example, v-text is a directive with using this characteristic.

The translation result of vue-i18n is text. For this reason, pre-translation is realized with the same implementation as v-text.


Conclusion

In order to solve the rendering performance problem of Vue components in the translation of $t, this article introduced some options that can be optimize translation performance.

These options make it possible to build higher performance Vue applications.

We have published an example that can measure about these options. You can measure it like the following gif animation:

Performance measurement

Let’s check it with yourself! 👀

Show your support

Clapping shows how much you appreciated kazuya kawaguchi’s story.