Internationalization
Build multilingual applications with ease. Soli provides built-in i18n support for translations, number formatting, currency, dates, and pluralization.
Global by Design
The I18n class provides everything you need to localize your application: string translations with interpolation, pluralization rules, locale-aware number and currency formatting, and date localization.
Setting the Locale
Use I18n.locale() to get the current locale and I18n.set_locale() to change it.
// Get the current locale (defaults to "en")
let current = I18n.locale();
print(current); // "en"
// Set locale to French
I18n.set_locale("fr");
print(I18n.locale()); // "fr"
// Set locale to German
I18n.set_locale("de");
Basic Translation
Translate strings using I18n.translate(key, locale?, translations?). The function looks up the key in your translations hash.
// Define translations
let translations = {
"en.greeting" => "Hello",
"fr.greeting" => "Bonjour",
"de.greeting" => "Hallo",
"es.greeting" => "Hola",
"en.welcome" => "Welcome to our app!",
"fr.welcome" => "Bienvenue dans notre application!"
};
// Translate using current locale
I18n.set_locale("fr");
let msg = I18n.translate("greeting", null, translations);
print(msg); // "Bonjour"
// Translate with explicit locale
let hello = I18n.translate("greeting", "de", translations);
print(hello); // "Hallo"
// Falls back to key if not found
let missing = I18n.translate("unknown_key", null, translations);
print(missing); // "unknown_key"
Pluralization
Handle singular and plural forms with I18n.plural(key, n, locale?, translations?). Use _zero suffix for zero, _one for singular, and _other for plural.
let translations = {
"en.item_zero" => "No items",
"en.item_one" => "1 item",
"en.item_other" => "{count} items",
"fr.item_zero" => "Aucun article",
"fr.item_one" => "1 article",
"fr.item_other" => "{count} articles"
};
I18n.set_locale("en");
// Zero form
let none = I18n.plural("item", 0, null, translations);
print(none); // "No items"
// Singular form
let single = I18n.plural("item", 1, null, translations);
print(single); // "1 item"
// Plural form
let multiple = I18n.plural("item", 5, null, translations);
print(multiple); // "{count} items"
// French plural
let fr_plural = I18n.plural("item", 3, "fr", translations);
print(fr_plural); // "{count} articles"
Number Formatting
Format numbers according to locale conventions using I18n.format_number(n, locale?).
let value = 1234.56;
// English format (dot as decimal separator)
let en_num = I18n.format_number(value, "en");
print(en_num); // "1234.56"
// French format (comma as decimal separator)
let fr_num = I18n.format_number(value, "fr");
print(fr_num); // "1234,56"
// German format
let de_num = I18n.format_number(value, "de");
print(de_num); // "1234,56"
Currency Formatting
Format monetary amounts with I18n.format_currency(amount, currency, locale?). Supports common currency codes.
let price = 1999.99;
// US Dollars
let usd = I18n.format_currency(price, "USD", "en");
print(usd); // "$1,999.99"
// Euros with French locale
let eur = I18n.format_currency(price, "EUR", "fr");
print(eur); // "€1.999,99"
// British Pounds
let gbp = I18n.format_currency(price, "GBP", "en");
print(gbp); // "£1,999.99"
// Japanese Yen
let jpy = I18n.format_currency(2500, "JPY", "en");
print(jpy); // "¥2,500"
Date Formatting
Format dates using I18n.format_date(timestamp, locale?). Different locales use different date formats.
// Unix timestamp for January 15, 2026
let timestamp = 1768521600;
// US format: MM/DD/YYYY
let us_date = I18n.format_date(timestamp, "en");
print(us_date); // "01/15/2026"
// French format: DD/MM/YYYY
let fr_date = I18n.format_date(timestamp, "fr");
print(fr_date); // "15/01/2026"
// German format: DD.MM.YYYY
let de_date = I18n.format_date(timestamp, "de");
print(de_date); // "15.01.2026"
// ISO format (default for other locales)
let iso_date = I18n.format_date(timestamp, "ja");
print(iso_date); // "2026-01-15"
Translation Files
Organize translations in JSON files and load them at startup.
{
"en.app_name": "My Application",
"en.nav.home": "Home",
"en.nav.about": "About Us",
"en.nav.contact": "Contact",
"en.messages.success": "Operation completed successfully!",
"en.messages.error": "An error occurred. Please try again.",
"en.user.greeting": "Hello, {name}!",
"en.items_one": "You have 1 item",
"en.items_other": "You have {count} items"
}
// Load translations from JSON file
let en_json = file_read("config/locales/en.json");
let en_translations = json_decode(en_json);
let fr_json = file_read("config/locales/fr.json");
let fr_translations = json_decode(fr_json);
// Merge translations
let all_translations = hash_merge(en_translations, fr_translations);
// Use in your application
let greeting = I18n.translate("nav.home", "en", all_translations);
print(greeting); // "Home"
Supported Locales
The I18n class provides built-in formatting support for these locales:
| Locale Code | Language | Number Format | Date Format |
|---|---|---|---|
en |
English | 1,234.56 | MM/DD/YYYY |
fr |
French | 1.234,56 | DD/MM/YYYY |
de |
German | 1.234,56 | DD.MM.YYYY |
es |
Spanish | 1.234,56 | YYYY-MM-DD |
it |
Italian | 1.234,56 | YYYY-MM-DD |
Method Reference
I18n.locale()
Returns the current locale string. Defaults to "en" if not set.
I18n.set_locale(locale)
Sets the current locale. Returns the locale string.
I18n.translate(key, locale?, translations?)
Translates a key. Uses current locale if not specified. Falls back to the key itself if no translation found.
I18n.plural(key, n, locale?, translations?)
Returns zero (_zero), singular (_one), or plural (_other) form based on count.
I18n.format_number(n, locale?)
Formats a number according to locale conventions (decimal separator).
I18n.format_currency(amount, currency, locale?)
Formats a monetary amount with currency symbol. Supports USD, EUR, GBP, JPY.
I18n.format_date(timestamp, locale?)
Formats a Unix timestamp as a date string according to locale conventions.
Best Practice
Set the locale early in your request lifecycle, typically in a before filter or middleware.
You can detect the user's preferred language from the Accept-Language header or from user preferences stored in your database.