mini\I18n
namespace
I18n - Internationalization
Overview
Mini's I18n system provides translations and locale-aware formatting using PHP's native intl extension and ICU MessageFormat.
How it works:
- Call
t("Hello {name}", ['name' => 'World'])in your code - Translator looks up the string in
_translations/{lang}/_routes/yourfile.php.json - ICU MessageFormatter handles variable substitution and pluralization
- Result is returned in the user's language
Key features:
- File-per-source organization - translations mirror your source file structure
- Auto-creation - missing strings automatically added to
default/folder - Fallback chain - tries current locale → default language →
defaultfolder - ICU MessageFormat - industry-standard pluralization and formatting
Adding a New Language
Use the translations tool to add a new language:
# 1. Ensure default translations exist for all t() calls
vendor/bin/mini translations add-missing
# 2. Create the new language files
vendor/bin/mini translations add-language nb
This creates _translations/nb/ with all your translation strings set to null (falling back to default until translated). Now translate the strings in those files.
3. Set the locale per request
// In your route or bootstrap
\Locale::setDefault('nb');
date_default_timezone_set('Europe/Oslo');
That's it. No configuration files or registration needed.
Manual approach
You can also create language folders manually. Translation files are JSON and mirror your source file paths:
_translations/
├── default/ # Fallback (usually English)
│ └── _routes/
│ └── index.php.json
└── nb/ # Norwegian Bokmål
└── _routes/
└── index.php.json
// _translations/nb/_routes/index.php.json
{
"Welcome": "Velkommen",
"Hello {name}": "Hei {name}",
"{count, plural, =0{no messages} one{# message} other{# messages}}": "{count, plural, =0{ingen meldinger} one{# melding} other{# meldinger}}"
}
Using Translations
The t() function
// Simple text
echo t("Welcome"); // "Velkommen"
// With variables
echo t("Hello {name}", ['name' => 'Ola']); // "Hei Ola"
// Pluralization
echo t("{count, plural, =0{no items} one{# item} other{# items}}", ['count' => 5]);
// "5 items"
Locale-aware formatting
\Locale::setDefault('nb');
echo Fmt::currency(1234.56, 'NOK'); // "kr 1 234,56"
echo Fmt::number(1234567.89); // "1 234 567,89"
echo Fmt::dateShort(new DateTime()); // "19.11.2025"
Translation File Structure
Files are organized to mirror your source code:
_translations/
├── default/ # Fallback language
│ ├── _routes/
│ │ ├── index.php.json # For _routes/index.php
│ │ └── users/
│ │ └── profile.php.json
│ └── src/
│ └── Services/
│ └── Mailer.php.json
├── nb/ # Norwegian
│ └── _routes/
│ └── index.php.json
└── de/ # German
└── _routes/
└── index.php.json
Why this structure?
- Easy to find which file needs translation
- Each file stays small and focused
- Git diffs show exactly what changed
- Auto-creation puts new strings in the right place
Setting the Locale
From user preference
session();
if (isset($_SESSION['locale'])) {
\Locale::setDefault($_SESSION['locale']);
}
From browser
$locale = \Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '');
if ($locale) {
\Locale::setDefault($locale);
}
From URL
if (isset($_GET['lang'])) {
$_SESSION['locale'] = $_GET['lang'];
\Locale::setDefault($_GET['lang']);
}
ICU MessageFormat Patterns
Pluralization
// Basic plural
t("{n, plural, one{# item} other{# items}}", ['n' => 1]);
// 1 → "1 item", 2 → "2 items"
// With zero case
t("{n, plural, =0{no items} one{# item} other{# items}}", ['n' => 0]);
// 0 → "no items"
// Multiple variables
t("{name} has {n, plural, one{# message} other{# messages}}", [
'name' => 'Ola',
'n' => 3
]);
// "Ola has 3 messages"
Select (gender, status, etc.)
t("{gender, select, male{He} female{She} other{They}} liked your post", [
'gender' => 'female'
]);
// "She liked your post"
Ordinals
t("You finished {place, selectordinal, one{#st} two{#nd} few{#rd} other{#th}}", [
'place' => 2
]);
// "You finished 2nd"
Formatting Functions
All formatting respects the current locale set via \Locale::setDefault():
// Numbers
Fmt::number(1234.56); // "1,234.56" or "1.234,56"
Fmt::percent(0.75); // "75%"
// Currency
Fmt::currency(19.99, 'USD'); // "$19.99"
Fmt::currency(19.99, 'EUR'); // "19,99 €"
// Dates
$date = new DateTime();
Fmt::dateShort($date); // "11/19/25" or "19.11.25"
Fmt::dateLong($date); // "November 19, 2025"
Fmt::timeShort($date); // "2:30 PM" or "14:30"
// File sizes
Fmt::fileSize(1536000); // "1.5 MB"
Fallback Behavior
When looking up a translation, Mini tries in order:
- Current locale (
_translations/nb/...) - Default language (
_translations/en/...if configured) - Default folder (
_translations/default/...) - Source text (returns the original string)
If autoCreateDefaults is enabled (default), missing strings are automatically added to _translations/default/.
Configuration
Custom translator
<?php
// _config/mini/I18n/Translator.php
use mini\I18n\Translator;
return new Translator(
translationsPaths: Mini::$mini->paths->translations,
autoCreateDefaults: true
);
Path aliases for packages
When using vendor packages that have their own translations:
$translator->addPathAlias(
absolutePath: __DIR__ . '/../../vendor/acme/blog',
alias: 'ACME-BLOG'
);
This maps translations from that package to _translations/{lang}/ACME-BLOG/....
Environment variables
MINI_LOCALE- Default locale (e.g.,en_US,nb_NO)MINI_TIMEZONE- Default timezone (e.g.,Europe/Oslo)
Translation Management Tool
Mini includes a CLI tool for managing translation files:
vendor/bin/mini translations # Show translation status (missing/orphaned strings)
vendor/bin/mini translations add-missing # Add missing strings to default files
vendor/bin/mini translations remove-orphans # Remove strings no longer in source code
vendor/bin/mini translations add-language nb # Create new language (Norwegian)
vendor/bin/mini translations update-language nb # Sync language files with default
Keeping translations in sync
After adding new t() calls to your code:
# Add new strings to default
vendor/bin/mini translations add-missing
# Update all language files
vendor/bin/mini translations update-language nb
vendor/bin/mini translations update-language de
Removing a language
Simply delete the language folder:
rm -rf _translations/nb
The tool scans your PHP files for t() calls and ensures translation files stay synchronized with your source code.
Quick Reference
// Set locale
\Locale::setDefault('nb');
date_default_timezone_set('Europe/Oslo');
// Translate
t("Hello {name}", ['name' => 'World']);
// Format
Fmt::currency(99.50, 'NOK');
Fmt::dateShort(new DateTime());
Fmt::number(1234.56);
// Plural pattern
{count, plural, =0{none} one{# item} other{# items}}
// Select pattern
{gender, select, male{He} female{She} other{They}}