Jeanrry Loader
A Vue.js SFC loader for localizing your app at building time.
NOTE: This project is still working in progress. Use at your own risk!!
Features
- Localizing your component's template
- SSR compatible
- Use your favorite i18n/l10n frameworks, no need to learn new syntax
- Safe and fast
Supported I18n/L10n Frameworks
Installation
Run the following command in your terminal
npm install jeanrry-loader --save
# or
yarn add jeanrry-loader
# to use jeanrry loader, you should also install one of our supported i18n/l10n frameworks. currently we only support frenchkiss
npm install frenchkiss --save
# or
yarn add frenchkiss
Configure it in your vue.config.js
file:
// our default translator is frenchkiss
const frenchkiss = require('frenchkiss');
// set up, you can load files here
frenchkiss.set('en', {
hi: 'Hi!',
hello: 'Hello, {name}!',
welcome: 'Welcome to Your {name} App!'
});
// setting the default locale
frenchkiss.locale('en')
module.exports = {
chainWebpack: (config) => {
config.module
.rule('jeanrry-loader')
.test(/\.vue$/)
.resourceQuery(/\?vue.*(&type=template).*/)
.use('jeanrry-loader')
.loader('jeanrry-loader')
.options({
// some options, see the `Loader Options` part in readme file
});
},
};
Usage
Jeanrry loader only works on your SFC
*.vue
files!
Following usage examples are based on frenchkiss. The
t
function call is nearly the same asfrenchkiss.t
Basic Usage
<template>
<h3>{{ t('hi') }}</h3>
</template>
Use on component's props or element's attribute
<template>
<HelloWorld :msg="t('welcome', { name: 'Vue.js' })" />
</template>
Use with your component's data
<template>
<HelloWorld :msg="t('welcome', { name: name })" />
</template>
translate
attribute
Use the <template>
<HelloWorld :msg="t('welcome', { name: name })" translate=no> <!-- will not be translate --->
</template>
Goes to the translators part to see the apis of the translators.
Loader Options
Following is the complete default options
{
translator: frenchkissTranslator, // in jeanrry-loader/dist/translators
}
If you want to use $t
instead of t
:
import FluentTranslator from "jeanrry-loader/dist/translators/fluent-translator;"
FluentTranslator.functionNameMappings = { '$t' : 't' }
Translators
Jeanrry Loader doesn't define any new format or syntax of i18n/l10n. Instead, it use other existing i18n/l10n frameworks to translate. Translators in jeanrry loader is the bridge connecting the i18n/l10n frameworks and the loader.
Frenchkiss Translator
The default translator for jeanrry loader.
Installation
npm install frenchkiss --save
# or
yarn add frenchkiss
Setup
You can call any functions from frenchkiss except for the followings:
-
frenchkiss.onMissingVariable(fn: Function)
: you should let Vue to do it in browser runtime. -
frenchkiss.plural(lang: string, fn: Function)
: currently not supported, use plural expression instead.
Following functions should be called carefully:
-
frenchkiss.onMissingKey(missingKeyHandler: Function)
: when anError
thrown from themissingKeyHandler
, your correspondingt()
won't be called by the jeanrry loader. This is useful if you have at
function in your Vue instance and you want Vue to call it.
A typical setup should be like the following. Make sure they're called before the webpack running.
const frenchkiss = require('frenchkiss');
// set up, you can load files here
frenchkiss.set('en', {
hi: 'Hi!',
hello: 'Hello, {name}!',
welcome: 'Welcome to Your {name} App!'
});
// setting the default locale
frenchkiss.locale('en')
Fluent Translator
Gives you the ability to use Fluent, a localization system
for natural-sounding translations. The translator relies on @fluent/bundle
Installation
npm install @fluent/bundle --save
# or
yarn add @fluent/bundle
Setup
import {FluentBundle, FluentResource} from "@fluent/bundle";
import FluentTranslator from "jeanrry-loader/dist/translators/fluent-translator;
let resource = new FluentResource(`
-brand-name = Foo 3000
welcome = Welcome, {$name}, to {-brand-name}!
`);
let bundle = new FluentBundle("en-US");
let errors = bundle.addResource(resource);
if (errors.length) {
// Syntax errors are per-message and don't break the whole resource
}
FluentTranslator.bundle = bundle; // important
module.exports = {
chainWebpack: (config) => {
config.module
.rule('jeanrry-loader')
.test(/\.vue$/)
.resourceQuery(/\?vue.*(&type=template).*/)
.use('jeanrry-loader')
.loader('jeanrry-loader')
.options({
translator: FluentTranslator
});
},
};
Usage
<template>
<p>{{ t('welcome' { name: 'Anna' }) }}</p> <!-- Welcome, Anna, to Foo 3000! -->
</template>
APIs
t(id: string, args?: Record<string, string | NativeArgument>): string
-
id
can only be a string literal, or the function won't be call. Same asid
inFluentBundle.getMessage(id)
-
args
Same asargs
inFluentBundle.formatPattern(pattern, args)
except that it doesn't support FluentType to be the record value.
The return value can be a string literal or a string wrapped function call like ('0.0') === (new Intl.NumberFormat(['en-US'], { minimumFractionDigits: 1 }).format(score)) ? ('You scored zero points. What happened?') : ('other') === (new Intl.PluralRules(['en-US'], { minimumFractionDigits: 1 }).select(new Intl.NumberFormat(['en-US'], { minimumFractionDigits: 1 }).format(score))) ? ('You scored ' + '⁨' + new Intl.NumberFormat(['en-US'], { minimumFractionDigits: 1 }).format(score) + '⁩' + ' points.') : ('You scored ' + '⁨' + new Intl.NumberFormat(['en-US'], { minimumFractionDigits: 1 }).format(score) + '⁩' + ' points.')
How it works?
Unlike Vue-i18n or other i18n frameworks that runs in browser runtime, Jeanrry Loader run as a Vue *.vue
files pre-processor on your own machine. It find the translation expression in your *.vue
files and translate them before the Vue compiling. For example,
<template>
<HelloWorld :msg="t('welcome', { name: name })" />
</template>
will be "translated" to:
<template>
<HelloWorld :msg="(function(a,f,k,l,v ) { var p=a||{};return 'Hello, '+(p['name']||(p['name']=='0'?0:'name' in p?'':v('name',k,l)))+'!' })({ name: name })" />
</template>
And the Vue compiler will see the translated <template>
. But this process also brings the following limitations.
Limitations
Directly using double quotes in your translator settings will cause unexpected errors. For example, in frenchkiss:
frenchkiss.set('en', {
quot: '"'
});
will cause unexpected error, you should use escape instead
frenchkiss.set('en', {
quot: '"'
});
Characters like <
and >
are also recommended to be used in escape form <
and >
Building for multiple languages
TBD
Deployment
There's different flavors of deployment. You can deploy like wikipedia: have different domains en.example.com
, zh.example.com
for different locales. Or using different sub-paths example.com/zh/
, example.com/en/
like Vue.js official site. Or even using plain Content-Negotiation.
Deploying a multi-languages jeanrry compiled Vue.js app to different domains or different sub-paths should be simple as the original Vue.js app. So the following guides/recipes will focus on the content negotiation part. For Vue.js deployment, checkout the vue-cli docs.
Netlify
TBD
Apache HTTP Server
Build your app using the 'Building to the same dist
folder but have different index.html
' method mentioned above. Move your built files to the directory of your apache http server. Add MultiViews
to the Options
of your directory
<Directory "/srv/http">
Options Indexes FollowSymLinks MultiViews
</Directory>
and rename your *.html
files to index.html.en
, index.html.zh
, index.html.jp
etc. Restart your server and using browser with different Accept-Langauge
header values to see the effectiveness.
For detail config, checkout the Apache HTTP Server docs.
Nginx
TBD
Todos
-
lang
attribute support -
v-for
support (maybe) - Add tests.
- Performance optimizations.
Licenses
fluent-translator.ts
and it's compiled files are released in Apache-2.0 license. frenchkiss-translator.ts
and it's compiled files are released in MIT license. The rests are released in GPL-2.0 license.