BoxLang 🚀 A New JVM Dynamic Language Learn More...
The simplest, fastest, and most powerful internationalization (i18n) plugin for Wheels 3.x+
• Lightweight • Zero dependencies • JSON or Database backed • Built-in pluralization • Full fallback support
t() – Simple key-based translation with variable interpolationtp() – Pluralization support (.zero,
.one, .other)wheels plugin install wheels-i18n
Add these settings in your config/settings.cfm file:
set(i18n_defaultLocale="en");
set(i18n_availableLocales="en,es");
set(i18n_fallbackLocale="en");
set(i18n_translationSource="json");
set(i18n_translationsPath="/app/locales");
set(i18n_cacheTranslations=false);
Below is a description of all available i18n configuration settings and their default values:
| Setting Name | Default | Description |
|---|---|---|
| i18n_defaultLocale | en
| Default locale if none is set in session |
| i18n_availableLocales | en
| A comma-separated list of all supported locales: "en,es" |
| i18n_fallbackLocale | en
| Used if a translation key is missing in current locale |
| i18n_translationSource | json
| Translation method: "json" or "database" |
| i18n_translationsPath | /app/locales
| Path for JSON files (only used with json source) |
| i18n_cacheTranslations | false
| Cache translations in memory (recommended for production) |
Pro Tip : Set i18n_cacheTranslations=true in production for fast performance.
Create this file: /app/locales/en/common.json
{
"welcome": "Welcome to my app!",
"greeting": "Hello, {name}!",
"save": "Save",
"posts": {
"zero": "No Post Found",
"one": "{count} Post Found",
"other": "{count} Posts Found"
},
"nav": {
"home": "Home",
"about": {
"service": "Service",
"portfolio": "Portfolio"
},
"contact": "Contact"
}
}
Same for different language: /app/locales/es/common.json
{
"welcome": "Bienvenido a nuestra aplicación",
"greeting": "¡Hola, {name}!",
"save": "Guardar",
"posts": {
"zero": "No se encontraron publicaciones",
"one": "{count} publicación encontrada",
"other": "{count} publicaciones encontradas"
},
"nav": {
"home": "Hogar",
"about": {
"service": "Servicio",
"portfolio": "Cartera"
},
"contact": "Contacto"
}
}
Your application should follow the following localization structure:
/app
/locales
/en
common.json
forms.json
/es
common.json
forms.json
#t("common.welcome")#
#t("common.greeting", name="Sarah")#
#t("common.nav.about.service")#
#tp("common.posts", count=5)#
Your Are Done!
https://raw.githubusercontent.com/wheels-dev/wheels-i18n/main/assets/translation-via-json.mp4
Add these settings in your config/settings.cfm file:
set(i18n_defaultLocale="en");
set(i18n_availableLocales="en,es");
set(i18n_fallbackLocale="en");
set(i18n_translationSource="database");
set(i18n_cacheTranslations=false);
Optional: Customize database table and column names if your schema differs.
set(i18n_dbTable="i18n_translations");
set(i18n_dbLocaleColumn="locale");
set(i18n_dbKeyColumn="translation_key");
set(i18n_dbValueColumn="translation_value");
Setting Default Description
i18n_dbTable i18n_translations Translation
table i18n_dbLocaleColumn locale Locale
column i18n_dbKeyColumn translation_key
Translation key i18n_dbValueColumn translation_value
Translation value
Note:
These settings are only used when
i18n_translationSource="database". If not
defined, the plugin automatically uses the default values.
Create the database table using a standard Wheels migration:
wheels dbmigrate create table i18n_translations
Then replace the generated file with this content:
// app/migrator/migrations/XXXX_cli_create_table_i18n_translations.cfc
component {
function up() {
t = createTable(name = 'i18n_translations', force='false', id='true', primaryKey='id');
t.string(columnNames = 'locale', limit = '10', allowNull = false);
t.string(columnNames = 'translation_key', limit = '255', allowNull = false);
t.text(columnNames = 'translation_value', allowNull = false);
t.timestamps();
t.create();
addIndex(table="i18n_translations", columnNames="locale");
addIndex(table="i18n_translations", columnNames="translation_key");
}
function down() {
dropTable("i18n_translations");
}
}
Then this command in CLI to run your migration:
wheels dbmigrate up
If your project already has a translations table, simply map it:
set(i18n_translationSource="database");
set(i18n_dbTable="translations");
set(i18n_dbLocaleColumn="lang");
set(i18n_dbKeyColumn="key_name");
set(i18n_dbValueColumn="value_text");
No plugin code changes are required.
Insert your translations keys according to your database to run your translation. here's a sample in MySQL
INSERT INTO i18n_translations (locale, translation_key, translation_value, createdAt, updatedAt) VALUES
('en', 'common.welcome', 'Welcome to our application', NOW(), NOW()),
('en', 'common.greeting', 'Hello, {name}!', NOW(), NOW()),
('en', 'common.goodbye', 'Goodbye', NOW(), NOW()),
('en', 'common.posts.zero', 'No Post Found', NOW(), NOW()),
('en', 'common.posts.one', '{count} Post Found', NOW(), NOW()),
('en', 'common.posts.other', '{count} Posts Found', NOW(), NOW()),
('es', 'common.welcome', 'Bienvenido a nuestra aplicación', NOW(), NOW()),
('es', 'common.greeting', '¡Hola, {name}!', NOW(), NOW()),
('es', 'common.goodbye', 'Adiós', NOW(), NOW()),
('es', 'common.posts.zero', 'Ningún Post Encontrado', NOW(), NOW()),
('es', 'common.posts.one', '{count} Post Encontrado', NOW(), NOW()),
('es', 'common.posts.other', '{count} Posts Encontrados', NOW(), NOW());
#t("common.welcome")#
#t("common.greeting", name="Sarah")#
#tp("common.posts", count=5)#
Your Are Done!
https://raw.githubusercontent.com/wheels-dev/wheels-i18n/main/assets/translation-via-database.mp4
Want translators or clients to edit translations live in the browser?
You can easily build your own admin area using standard Wheels tools:
That’s it — your translators can now update text instantly.
Many agencies love this workflow. You’re in full control — build it exactly how you want.
Design Philosophy: All configuration options have sensible defaults. You only need to configure what differs from your application. This keeps setup fast while remaining fully flexible.
#t("key")# → Translate#t("key", name="[param]")# → With variables#tp("key", count=[param])# → Pluralization
(.zero, .one, .other)#currentLocale()# → Get current language#changeLocale("es")# → Switch language#availableLocales()# → Array of supported languagest()
The core function to translate a key to the current locale, with parameter interpolation and fallback logic.
// Basic Usage
#t("common.welcome")# // (Output: Welcome to our application)
// With parameter interpolation
#t(key="common.greeting", name="John Doe")# // (Output: "Hello, John Dow!")
tp()
Translates a key and automatically selects the correct singular (.one) or plural (.other) form based on the count argument. The count is also available for interpolation as {count}.
Note: This implementation assumes the simple English plural rule (1 is singular, anything else is plural).
// Zero usage (Count = 0)
#tp(key="common.posts", count=0)# // (Output: "No Post Found")
// Singular usage (Count = 1)
#tp(key="common.posts", count=1)# // (Output: "1 Post Found")
// Plural usage (Count > 1)
#tp(key="common.posts", count=5)# // (Output: "5 Posts Found")
changeLocale()
Sets the application locale in Session and returns a boolean based on success.
// Change to Spanish
changeLocale("es");
// Unsupported locale
changeLocale("jp"); // false
currentLocale()
Gets the current application locale from the Session, or the default locale if not set.
locale = currentLocale(); // "en"
availableLocales()
Returns an array of all configured available locales.
locales = availableLocales(); // ["en", "es", "fr"]
$
box install Wheels-i18n