0.6.1 • Published 4 years ago

@mpt/preact-i18n v0.6.1

Weekly downloads
-
License
MIT
Repository
github
Last release
4 years ago

Logo Splash

Preact I18n

Developer friendly full stack localization for preact apps. This is a proof of concept and things might break or change at any time.

Getting Started

npm i -D @mpt/preact-i18n

Configuration

The configuration for a package is stored in a json5 file i18n.json5

{
    // Translation data storage path:
    projectData: "./i18n-data.json",

    // The namespace for this package.
    // It is recommended to use the npm package name as namespace.
    namespace: "~",

    // An array of globs whre to find jsx/tsx source files.
    sources: [
        "src/**"
    ],

    // The output path for compiled language resources:
    // "[lang]" is replaced with the language tag.
    output: "dist/lang/[lang].json",

    // The language, sources are written in:
    sourceLanguage: "en",

    // An array of languages this package is translated to.
    // Default is an empty array.
    languages: ["de", "ch"]
}

Runtime Setup

It is recommended to move the runtime configuration to a separate module:

import { I18n, Language, languageFactory } from "@mpt/preact-i18n";

export const i18n = new I18n({
    // A set of clients that are used to fetch language resources:
    clients: [
        new FetchClient()
    ],

    // Include the default language factory to
    // support pluralization and interpolation:
    languageFactory,

    // When enabled, the document root "lang" attribute
    // will be set to the current language name.
    setLangAttribute: true,

    // The following options should match your configuration:
    namespace: "~",
    sourceLanguage: "en"
});

// Export text fragment components:
export const T = i18n.T;
export const TX = i18n.TX;

Usage

import { h, render } from "preact";
import { Language } from "@mpt/preact-i18n";
import { i18n, T } from "./i18n";

// Always set the language before rendering anything:
// Otherwise all text fragments will be empty.
await i18n.setLanguageAuto(["en", "de", "ch"], "en");

render(
    <Language.Provider use={i18n}>
        <T value="Hello World!" id="0"/>
    </Language.Provider>,
    document.body
);

Text Components

There are two types of text components. <T> for simple text and <TX> for more complex things.

<T value="Hello World!" />
// Hello World!

Pluralization

<TX value={["Apple", "Apples"]} count={3} />
// Apples

<TX value={["Apple", "Apples"]} count={1} />
// Apple

<TX value={["{count} apple", "{count} apples"]} count={7} />
// 7 apples

The number of forms depends on the language. You can lookup the number in plurals.json5.

Interpolation

<TX value="Hello {name}!" fields={{ name: "World" }} />
// Hello World!

Escaping { and }

Escaping is not supported, but you can add two simple fields for that purpose if needed:

<TX value="class {name} {<} ... {>}" fields={{ "<": "{", ">": "}", name: "Example" }} />
// class Example { ... }

Formatting

Formatters are functions that are used by text components to format interpolated values.

new I18n({
    formatters: new Map([
        [Date.prototype, (value: any, language: Language, format?: string) => {
            return value.toLocaleString(language.name);
        }],

        ["hex", (value: number | bigint) => {
            return value.toString(16);
        }]
    ])
});

<TX value="The current date is {now}" fields={{ now: new Date() }} />
// The current date is 7/19/2020, 6:15:33 PM

<TX value="The memory address is {address,hex}" fields={{ address: 0xE2740C980AC100B0n }} />
// The memory address is E2740C980AC100B0

Formatters are selected as follows:

  • If a formatter name is specified (e.g. hex), that formatter is used.
  • If the value is an object, the formatter is selected based on the prototype chain.
  • If the primitive type is not "string", the formatter is selected based on the primitive type.
  • Else, the value is converted using String(value)

Namespacing & Context

When writing a package with components, you don't want your translations to collide with others. To prevent that, you can create an I18nContext that provides text fragment components that automatically look up translations using a different namespace:

import { createContext } from "@mpt/preact-i18n";

const { T, TX } = createContext({
    // The following options should match your configuration:
    namespace: "~",
    sourceLanguage: "en",

    // Additional formatters that are only available to this context:
    formatters: ...
});

export { T, TX };

Advanced Topics

Language context

The language context is used to pass the current language instance to text components. This can be used to set the lang attribute on the root element of your component if the component is beeing used in an application over which you have no control.

import { Language } from "@mpt/preact-i18n";

<Language.Consumer>{language => {
    return <div lang={language?.name}>
        ...
    </div>;
}</Language.Consumer>

Update handlers

Update handlers can be used to detect when the current language has been changed. This is used by the I18n class internally if setLangAttribute is true:

i18n.addUpdateHandler(() => {
    if (i18n.language) {
        document.documentElement.lang = i18n.language.name;
    }
});

Translation Workflow

Command Line

# Start translation workflow:
preact-i18n start

# Run diagnostics and compile translations:
preact-i18n compile

Writing Translations

Translations are written by editing the project's translation data file (i18n-data.json) It is recommended to use a specialized editor for writing translations like this vscode extension.

0.6.1

4 years ago

0.6.0

4 years ago

0.5.0

4 years ago

0.4.4

4 years ago

0.4.3

4 years ago

0.4.1

4 years ago

0.4.0

4 years ago

0.3.1

4 years ago

0.3.0

4 years ago

0.2.2

4 years ago

0.2.1

4 years ago

0.2.0

4 years ago

0.1.10

4 years ago

0.1.11

4 years ago

0.1.9

4 years ago

0.1.8

4 years ago

0.1.7

4 years ago

0.1.6

4 years ago

0.1.5

4 years ago

0.1.4

4 years ago

0.1.3

4 years ago

0.1.2

4 years ago

0.1.1

4 years ago

0.1.0

4 years ago