Internationalization

Using Gem's i18n module, you can quickly add multi-language support to your application.

Getting started

import { I18n } from '@mantou/gem/helper/i18n'; const en = { title: 'This is I18n', }; const i18n = new I18n({ fallbackLanguage: 'en', resources: { en, zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }, }); console.log(i18n.get('title'));

When instantiating the I18n object, the appropriate language is automatically selected according to the browser settings. Language packs support remote reading for on-demand loading.

TIP

It is recommended to use Crowdin, Pontoon similar services to maintain your language pack

You can directly use i18n.get('title') in the element to read the value in the language pack.

@customElement('app-root') class App extends GemElement { render = () => { return html` <button @click=${() => i18n.setLanguage('zh')}>zh</button> <p>${i18n.get('title')}</p> `; } }

Cache

The user selects the language or the remote language package can be cached, and it is used directly when the user enters the App next time. Use cache to specify, the cached data will be saved to localStorage.

const i18n = new I18n({ cache: true, fallbackLanguage: 'en', resources: { en, zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }, });

Variable substitution

In multilingual projects, variables are often embedded, such as:

html`Hello, ${username}`;

When using Gem, you can use $ to mark variables, for example:

const i18n = new I18n({ fallbackLanguage: 'en', resources: { en: { hello: 'Hello, $1! $2=> $1, Hello!', }, }, }); html`${i18n.get('hello', 'World', 'reverse')}`; // Hello, World! reverse=> World, Hello!

The variable can also be a custom rendering function, which can customize the rendering of the content provided in the language template. The following example is to render the word detail specified in the language entry into a link:

const i18n = new I18n({ fallbackLanguage: 'en', resources: { en: { detail: 'See $1<detail>, $1<detail2>', }, }, }); html`${i18n.get('detail', (s) => html`<a href="#">${s}</a>`)}`;

Language specified in URL

Some multi-language front-end projects may need SEO support, so you need to specify different language versions in the URL. urlParamsType allows I18n to check the URL when instantiating, and supports multiple types:

  • path: root path, for example /zh/home
  • querystring: located in URL query string, the name is specified with urlParamsName
  • ccTLD: Subdomain, such as jp.website.com
  • gTLD: Top-level domain name, such as website.jp
const i18n = new I18n({ fallbackLanguage: 'en', urlParamsType: 'ccTLD', resources: { en: { title: 'This is I18n', }, }, });

TIP

When urlParamsType is path, then the links in the page and the history routing switch need to be modified, this operation can be defined globally by setting history.basePath:

new I18n({ urlParamsType: 'path', fallbackLanguage: 'zh-CN', resources: { 'zh-CN': zhCN, }, onChange(lang) { if (!history.basePath) { history.basePath = '/' + lang; const { path, query, hash } = history.getParams(); history.replace({ path, query, hash }); } else { const { path, query, hash } = history.getParams(); history.basePath = '/' + lang; history.replace({ path, query, hash }); } }, });

Sub module

I18n.createSubModule creates sub-modules, you can split language packages by route to speed up the rendering of the first screen.

Localize

You can use <gem-route> to render different content according to different languages. You only need to specify the trigger to i18n:

const localizeRoutes = [ { pattern: 'zh-*', async getContent() { import('...'); return html`other element`; }, }, ]; html`<gem-route .trigger=${i18n} .routes=${localizeRoutes}></gem-route>`;

Example

import { customElement, GemElement, html, render } from '@mantou/gem'; import { I18n } from '@mantou/gem/helper/i18n'; import type { RouteItem } from 'duoyun-ui/elements/route'; import 'duoyun-ui/elements/route'; import '../elements/layout'; const en = { title: 'This is I18n', hello: 'Hello, $1! $2=> $1, Hello!', detail: 'See $1<detail>, $1<detail2>', }; const i18n = new I18n<typeof en>({ cache: true, fallbackLanguage: 'en', resources: { en, de: { title: 'Das ist I18n', }, zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }, onChange: console.log, }); const i18nModule = i18n.createSubModule<typeof en>('test', { en, de: { title: 'Das ist I18n', }, zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }); const localizeRoutes: RouteItem[] = [ { pattern: 'en', getContent() { return html`en element`; }, }, { pattern: 'de', getContent() { return html`de element`; }, }, { pattern: '*', getContent() { return html`other element`; }, }, ]; @customElement('app-root') export class App extends GemElement { render() { return html` <button @click=${() => i18n.setLanguage('de')}>de</button> <button @click=${() => i18n.setLanguage('zh')}>zh</button> <button @click=${() => i18n.setLanguage('en')}>en</button> <button @click=${() => i18n.resetLanguage()}>reset</button> <p>Current language: ${i18n.currentLanguage}</p> <p>${i18n.get('title')}</p> <p>${i18n.get('hello', 'World', 'reverse')}</p> <p>${i18n.get('detail', (s) => html`<a href="#">${s}</a>`)}</p> <h2>sub module</h2> <p>${i18nModule.get('title')}</p> <p>${i18nModule.get('hello', 'World', 'reverse')}</p> <p>${i18nModule.get('detail', (s) => html`<a href="#">${s}</a>`)}</p> <h2>localize content</h2> <dy-route .trigger=${i18n} .routes=${localizeRoutes}></dy-route> `; } } render( html` <gem-examples-layout> <app-root></app-root> </gem-examples-layout> `, document.body, );