Internationalization

The @effuse/i18n package provides type-safe internationalization with reactive locale switching.

Setup

import { createI18n } from '@effuse/i18n';

const translations = {
  en: {
    greeting: 'Hello, {{name}}!',
    app: {
      title: 'My App',
    },
    items: {
      count_one: '{{count}} item',
      count_other: '{{count}} items',
    },
  },
  es: {
    greeting: '¡Hola, {{name}}!',
    app: {
      title: 'Mi Aplicación',
    },
    items: {
      count_one: '{{count}} elemento',
      count_other: '{{count}} elementos',
    },
  },
};

createI18n({
  defaultLocale: 'en',
  fallbackLocale: 'en',
  translations,
  detectLocale: true,
  persistLocale: true,
});

Type-Safe Translations

For full type safety, use defineTranslations and pass the inferred type to createI18n.

import { defineTranslations, createI18n } from '@effuse/i18n';

const en = defineTranslations({
  welcome: 'Welcome back, {{name}}!',
  actions: {
    save: 'Save Changes',
  },
});

const i18n = createI18n<typeof en>({
  defaultLocale: 'en',
  translations: { en },
});

Using Translations

Use the useTranslation hook in your components:

import { define, computed, signal } from '@effuse/core';
import { useTranslation } from '@effuse/i18n';

const Greeting = define({
  script: () => {
    const { t, locale, setLocale } = useTranslation();
    const userName = signal('World');

    return {
      greeting: computed(() => t('greeting', { name: userName.value })),
      toggleLocale: () => setLocale(locale.value === 'en' ? 'es' : 'en'),
    };
  },
  template: ({ greeting, toggleLocale }) => (
    <div>
      <p>{greeting}</p>
      <button onClick={toggleLocale}>Change Language</button>
    </div>
  ),
});

Nested Keys

Access nested translations using dot notation:

const { t } = useTranslation();

t('app.title'); // "My App"

Variable Interpolation

Pass variables using the {{variable}} syntax:

t('greeting', { name: 'Developer' }); // "Hello, Developer!"

Pluralization

Define plural forms with _one, _other, _zero, _two, _few, _many suffixes:

// In translations
items: {
  count_one: '{{count}} item',
  count_other: '{{count}} items',
}

// Usage
t('items.count', { count: 1 });  // "1 item"
t('items.count', { count: 5 });  // "5 items"

API Reference

createI18n(options)

Initializes the i18n instance. Call once at app startup.

OptionTypeDefaultDescription
defaultLocalestringThe default locale to use.
fallbackLocalestringFallback when translation is missing.
translationsRecord<string, T>Translation objects keyed by locale.
detectLocalebooleanfalseAuto-detect locale from browser.
persistLocalebooleanfalsePersist locale to localStorage.

useTranslation()

Returns translation utilities for use in components.

PropertyTypeDescription
t(key: string, params?) => stringTranslate a key with optional params.
localeSignal<string>Reactive current locale signal.
setLocale(locale: string) => Promise<void>Change the current locale.
hasKey(key: string) => booleanCheck if a translation key exists.

getI18n()

Returns the global i18n instance for direct access outside components.

import { getI18n } from '@effuse/i18n';

const i18n = getI18n();
i18n.setLocale('es');

Next Steps