Skip to content

i18n — Message Catalog

Nene\Func\I18n is a lightweight, dependency-free message-catalog helper that provides locale-aware string translation with {placeholder} substitution.

When to use

Use I18n when you need to:

  • Return user-facing messages in more than one language
  • Keep translatable strings out of controller logic
  • Substitute dynamic values (user name, counts, etc.) into translated messages

For large, complex projects consider a dedicated i18n library. I18n is intentionally minimal.

API

use Nene\Func\I18n;
Method Description
I18n::setLocale(string $locale): void Set the default locale used when no locale is passed to t().
I18n::locale(): string Get the current default locale (default: 'en').
I18n::load(string $locale, array $messages): void Register (or merge) a key→message map for the locale.
I18n::t(string $key, array $params = [], string $locale = ''): string Look up and substitute a message.
I18n::reset(): void Clear all catalogs and restore defaults. Use in test tearDown.

Registering messages

Call load() once at bootstrap (e.g. in ini/xSystemIni.php or a controller's preAction()):

I18n::setLocale('ja');

I18n::load('ja', [
    'greeting'   => 'こんにちは、{name}さん!',
    'item.count' => '{count}件あります',
    'error.auth' => '認証が必要です',
]);

I18n::load('en', [
    'greeting'   => 'Hello, {name}!',
    'item.count' => '{count} item(s) found',
    'error.auth' => 'Authentication required',
]);

Calling load() twice with the same locale merges the arrays (later keys win).

Translating

// Uses the current default locale ('ja')
echo I18n::t('greeting', ['name' => '太郎']);
// → こんにちは、太郎さん!

// Explicit locale override
echo I18n::t('greeting', ['name' => 'Taro'], 'en');
// → Hello, Taro!

// No params needed for static strings
echo I18n::t('error.auth');
// → 認証が必要です

Placeholder syntax

Placeholders are written as {key} and replaced by the matching key in $params:

I18n::load('en', ['msg' => 'Dear {title} {last},']);
echo I18n::t('msg', ['title' => 'Dr.', 'last' => 'Smith']);
// → Dear Dr. Smith,
  • Unknown placeholders are left as-is (not removed).
  • A key may appear multiple times in one message.

Fallback chain

  1. Requested locale (or default locale when no locale argument is given).
  2. Default locale, if different from the requested one.
  3. The key string itself when no translation is found.
I18n::setLocale('en');
I18n::load('en', ['hello' => 'Hello!']);
// 'ja' has no 'hello' → falls back to 'en'
echo I18n::t('hello', [], 'ja');  // → Hello!

// Completely unknown key → key returned as-is
echo I18n::t('no.such.key');  // → no.such.key

Locale naming

Any string is accepted as a locale identifier. Common conventions:

Identifier Use
'en' English
'ja' Japanese
'zh-TW' Traditional Chinese
'fr' French

Use a consistent convention across your project.

Testing

Call I18n::reset() in tearDown() to prevent catalog state from leaking between tests:

protected function tearDown(): void
{
    I18n::reset();
}

Organising catalogs

For larger projects, keep catalogs in dedicated PHP files:

resources/
  lang/
    ja.php   → returns array<string,string>
    en.php   → returns array<string,string>

Load at bootstrap:

I18n::setLocale($userLocale);
I18n::load('ja', require __DIR__ . '/../resources/lang/ja.php');
I18n::load('en', require __DIR__ . '/../resources/lang/en.php');
  • Nene\Func\Validator — input validation with translatable error messages.
  • config/error_codes.php — framework-level error messages (not translated through I18n, returned as-is in the JSON envelope).