Internationalization
Put language packs in src/locales and Gemkit generates the i18n instance, language-prefixed URLs and
the server-side language detection.
src/locales/
en.json default language, /about
zh.json /zh/aboutA JSON file is a language, the file name is the language code. The default language has no path prefix
by default, other languages use /<lang>.
Configuration
pluginGemkit({
defaultLanguage: 'en',
prefixDefaultLanguage: false,
});| Option | Description |
|---|---|
defaultLanguage |
The default language. Defaults to en when present, otherwise the first language by file name. |
prefixDefaultLanguage |
Put the default language under a prefix too (/en/about). Unprefixed paths redirect to it. |
The default language must have a language file, otherwise the build fails.
Every page exists in every language. There are no duplicated page files in src/pages: /zh/about
renders the same src/pages/about.ts module with the zh language pack.
Generated module
$gemkit/i18n exports:
| Export | Description |
|---|---|
i18n |
The I18n instance, typed with the default language pack. |
languages |
The language codes, sorted by file name. |
defaultLanguage |
The default language code. |
prefixDefaultLanguage |
Whether the default language has a prefix. |
localizePath |
(lang, path?) => string, the path with the language prefix. |
import { languages, localizePath } from '$gemkit/i18n';
html`<p>${i18n.get('title')}</p>`;
// Switch language with a normal link, the page reloads.
html`${languages.map((lang) => html`<a href=${localizePath(lang)}>${lang}</a>`)}`;i18n is a Gem I18n instance. See the Gem i18n documentation
for variable substitution, fallback languages and cache.
Page content
Pages, layouts and elements read translations from i18n.get. Since the language pack is loaded before
rendering, the first server render and hydration use the same language:
@page({ title: () => i18n.get('about') })
export default class AboutPage extends GemElement {
@template()
#content = () => html`<h1>${i18n.get('about')}</h1>`;
}The language is selected from the URL, not from navigator.language. A request without a prefix uses
the default language; a request with an unknown prefix is a normal 404 (or the path is treated as part
of the default language path).
Switching language
Use a normal <a href> with localizePath. Switching language reloads the page: the base path, the
language pack and all content change, so a full reload is the simplest and most consistent behavior.
It also matches the server-rendered result.
html`<a href=${localizePath('zh')}>中文</a>`;Dynamic paths
paths
export function paths({ lang }: { lang: string }) {
return posts[lang].map(({ slug }) => ({ slug }));
}When prefixDefaultLanguage is enabled, Gemkit also writes redirect pages for unprefixed paths, so
static hosting redirects to the default language without a server.
Template
The <html lang> attribute is set by the server to the current language. Add the language to
src/index.html only if you want a default for non-rendered documents; Gemkit replaces it on rendered
pages.
API routes
API routes are not localized. They always use their path without a language prefix, for example
/api/likes/1, regardless of the current language.
