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');

MIDDAG © 2015-2026