Skip to content

i18n Guide

The i18n system bridges host-provided translations (Moodle core/str, WordPress wp.i18n) with a unified React API. Strings can be pre-loaded for performance or fetched on demand.

Architecture

Host provides:
├── Pre-loaded strings via Inertia shared props (theme.strings)
└── Async resolver for missing keys (host-specific)

I18nProvider merges both:
├── Server strings (sync, fast)
└── Async strings (on-demand, cached in state)

useTranslation() → { t, tAsync }

The t(key) function returns the translation synchronously -- if the key is not pre-loaded, it returns the key itself as a fallback. The tAsync(key, component?) function uses the injected resolver to fetch missing keys on demand and caches them for subsequent calls.

Provider Setup

Wrap your app with I18nProvider and optionally pass an async resolver for on-demand string fetching:

tsx
// App.tsx
import { I18nProvider } from '@middag-io/react';

<I18nProvider asyncResolver={myResolver}>
    <App />
</I18nProvider>

No resolver needed for pre-loaded strings

If all your strings are pre-loaded via Inertia shared props, the async resolver is optional. The provider works with sync-only strings out of the box.

Host Resolvers

Each host platform provides its own async resolver:

ts
// Moodle resolver
const moodleResolver = async (key, component) => {
    const { get_string } = await import('core/str');
    return get_string(key, component || 'local_myplugin');
};
ts
// WordPress resolver
const wpResolver = async (key) => {
    return wp.i18n.__(key, 'my-plugin');
};

Resolver signature

The resolver receives (key: string, component?: string) and returns Promise<string>. The component parameter is Moodle-specific (the Frankenstyle plugin name).

Pre-loading Strings

For best performance, pre-load frequently used strings from the backend via Inertia shared props. This avoids async lookups at render time:

php
// PHP (Moodle)
// In Moodle shared props:
$strings = [
    'dashboard_title' => get_string('dashboard_title', 'local_myplugin'),
    'save' => get_string('save', 'core'),
    'cancel' => get_string('cancel', 'core'),
];

return Inertia::share('theme', ['strings' => $strings]);
php
// PHP (WordPress)
$strings = [
    'dashboard_title' => __('Dashboard', 'my-plugin'),
    'save' => __('Save', 'my-plugin'),
    'cancel' => __('Cancel', 'my-plugin'),
];

return Inertia::share('theme', ['strings' => $strings]);

Mock i18n (Development)

The mock build uses a standalone i18n system that does not depend on Inertia or a host application:

  • MockI18nProvider -- standalone provider with flat key lookups, no Inertia dependency.
  • Flat dot-prefix keys -- t('dashboard.title') looks up dashboard.title in the locale map.
  • Factory pattern -- page data factories accept t as a parameter: createDashboardPageContract(t).
  • Locale files -- organized in mock/i18n/locales/{en,pt-BR}/ with per-domain modules.

Adding Translations

Step-by-step guide to adding new translations in the mock build:

  1. Create or edit a locale file -- Add keys to mock/i18n/locales/en/your-domain.ts:
ts
// mock/i18n/locales/en/your-domain.ts
export const yourDomain: Record<string, string> = {
    'your_domain.title': 'My Title',
    'your_domain.description': 'A helpful description.',
};
  1. Update the barrel export -- Add your domain to mock/i18n/locales/en/index.ts:
ts
// mock/i18n/locales/en/index.ts
import { yourDomain } from './your-domain';

export const en: Record<string, string> = {
    ...yourDomain,
    // ... existing domains
};
  1. Repeat for other locales -- Add the same keys with translated values in mock/i18n/locales/pt-BR/your-domain.ts.

  2. Use in factories -- Reference keys via t():

ts
const title = t('your_domain.title');

Released under the proprietary license.