/qwik-speak

Translate your Qwik apps into any language

Primary LanguageTypeScriptMIT LicenseMIT

Qwik Speak ⚡️

Node.js CI Playwright

Internationalization (i18n) library to translate texts, dates and numbers in Qwik apps

Live example on StackBlitz

Speak context

stateDiagram-v2
    State1: SpeakState
    State2: SpeakLocale
    State3: Translation
    State4: SpeakConfig
    State5: TranslateFn
    State1 --> State2
    State1 --> State3
    State1 --> State4
    State1 --> State5
    note right of State2
        Creates subscriptions 
        and rerenders components with translations
    end note
    note right of State3: Immutable
    note right of State4: Immutable
    note right of State5
        Immutable
        QRL functions to be serializable
    end note
Loading

Usage

Usage

Getting started

npm install qwik-speak --save-dev

Getting the translation

import { translate as t } from 'qwik-speak';

export default component$(() => {
    return (
        <Host>
            <h1>{t('app.title', { name: 'Qwik Speak' })}</h1> {/* I'm Qwik Speak */}
        </Host>
    );
});

Getting dates & numbers

import { formatDate as fd, formatNumber as fn } from 'qwik-speak';

export default component$(() => {
    return (
        <Host>
            <p>{fd(Date.now(), { dateStyle: 'full', timeStyle: 'short' })}</p> {/* Wednesday, July 20, 2022 at 7:09 AM */}
            <p>{fn(1000000, { style: 'currency' })}</p> {/* $1,000,000.00 */}
        </Host>
    );
});

Configuration

import { SpeakConfig, Translation } from 'qwik-speak';

export const appTranslation: Translation = {
    "en-US": {
        "app": {
            "title": "I'm {{name}}"
        }
    },
    "it-IT": {
        "app": {
            "title": "Io sono {{name}}"
        }
    }
};

export const config: SpeakConfig = {
    defaultLocale: { language: 'en-US', currency: 'USD', timeZone: 'America/Los_Angeles' },
    supportedLocales: [
        { language: 'it-IT', currency: 'EUR', timeZone: 'Europe/Rome' },
        { language: 'en-US', currency: 'USD', timeZone: 'America/Los_Angeles' }
    ],
    assets: [
        appTranslation
    ]
};

Assets can be translation data, as in the example, or string paths to load json files or others types by implementing getTranslation$ below

// File: src/routes/_layout.tsx
import { useSpeak } from 'qwik-speak';

export default component$(() => {
    /**
     * Init Speak (only available in child components)
     */
    useSpeak(config);

    return (<></>);
});

Adding translation data to a context

import { Speak } from 'qwik-speak';

export default component$(() => {
  return (
    /**
     * Add Home translation (only available in child components)
     */
    <Speak assets={[homeTranslation]}>
      <Home />
    </Speak>
  );
});

Additional languages

import { Speak } from 'qwik-speak';

export default component$(() => {
  return (
    <Speak assets={[homeTranslation]} langs={['en-US']}>
      <Home />
    </Speak>
  );
});

The translation data of the additional languages are preloaded along with the current language. They can be used as a fallback for missing values by implementing handleMissingTranslation$ below, or for multilingual pages

Hacking the library

import { $ } from '@builder.io/qwik';

export const getTranslation$: GetTranslationFn = $((lang: string, asset: string | Translation) => {
    /* Must contain the logic to get translation data: by default it uses only an asset of Translation object */
});

export const resolveLocale$: ResolveLocaleFn = $(() => {
    /* Must contain the logic to resolve which locale to use during SSR */
});

export const storeLocale$: StoreLocaleFn = $((locale: SpeakLocale) => {
    /* Must contain the logic to store the locale on Client when changes */
});

export const handleMissingTranslation$: HandleMissingTranslationFn = $((key: string, value?: string, params?: any, ctx?: SpeakState) => {
    /* Must contain the logic to handle missing values: by default returns the key */
});

export const translateFn: TranslateFn = {
    getTranslation$: getTranslation$,
    /* other functions */
};
export default component$(() => {
    useSpeak(config, translateFn); // Use Speak with config & translation functions

    return (<></>);
});

An example of these implementations can be found in the app

Speak config

  • defaultLocale The default locale

  • supportedLocales Supported locales

  • assets An array of string paths, or an array of Translation objects: each asset is passed to the getTranslation$ function to obtain data according to the language

  • keySeparator Separator of nested keys. Default is .

The SpeakLocale object contains the lang, in the format language[-script][-region], where:

  • language: ISO 639 two-letter or three-letter code
  • script: ISO 15924 four-letter script code
  • region: ISO 3166 two-letter, uppercase code

and optionally contains:

  • extension Language with Intl extensions, in the format language[-script][-region][-extensions] like en-US-u-ca-gregory-nu-latn to format dates and numbers
  • currency ISO 4217 three-letter code
  • timezone From the IANA time zone database
  • units Key value pairs of unit identifiers

APIs

Hooks

  • useSpeak(config: SpeakConfig, translateFn?: TranslateFn, langs?: string[]) Creates a new Speak context, resolves the locale & loads translation data

Functions

  • translate(keys: string | string[], params?: any, ctx?: SpeakState, lang?: string) Translates a key or an array of keys

  • formatDate(value: any, options?: Intl.DateTimeFormatOptions, locale?: SpeakLocale, lang?: string, timeZone?: string) Formats a date

  • formatNumber(value: any, options?: Intl.NumberFormatOptions, locale?: SpeakLocale, lang?: string, currency?: string) Formats a number

  • changeLocale(newLocale: SpeakLocale, ctx: SpeakState) Changes locale at runtime: loads translation data and rerenders components that uses translations

Speak context

  • useSpeakContext() Returns the Speak context

  • useSpeakLocale() Returns the locale in Speak context

  • useTranslation() Returns the translation data in Speak context

  • useSpeakConfig() Returns the configuration in Speak context

Development Builds

Run the app

npm start

Test the library

npm test
npm run test.e2e

Production Builds

npm run build

What's new

Released v0.0.6

License

MIT