Skip to content

Plugin translations and language catalogs

Put plugin interface text in language catalogs. Use English and German JSON catalogs, namespaced keys and translated output in views.

example.hello/
src/
translations/
en.json
de.json

If you supply translation catalogs, en.json is required. Each file contains a flat JSON object of string keys and string values. Locale filenames include en.json, de.json and de_CH.json; do not nest translations in objects.

Use your plugin ID as a namespace. For src/translations/en.json:

{
"plugin.example.hello.title": "Hello Example",
"plugin.example.hello.greeting": "Greeting",
"plugin.example.hello.saved": "Your changes have been saved."
}

For src/translations/de.json:

{
"plugin.example.hello.title": "Hallo Beispiel",
"plugin.example.hello.greeting": "Begrüssung",
"plugin.example.hello.saved": "Deine Änderungen wurden gespeichert."
}

Use the same stable keys in every language. Do not reuse keys owned by Core or another plugin.

Registry definitions pair a translation key with a usable fallback:

CFML
{
'translationKey' = "plugin.example.hello.title",
'defaultName' = "Hello Example"
}

These are label fields within a navigation, settings or integration definition, not a complete definition. Integration descriptions similarly use descriptionKey and defaultDescription. Keep fallbacks meaningful even before catalog import.

In a view, resolve the label and encode it for its output context:

CFML
<cfoutput>
<label for="greeting">
#encodeForHtml(request.t(
"plugin.example.hello.greeting",
"Greeting"
))#
</label>
</cfoutput>

Controllers can use request.t(key, fallback) for page titles and messages during a request. Translation does not perform HTML escaping: use encodeForHtml for text and encodeForHtmlAttribute for attribute values. Do not cache one user’s translated text globally.

Translate interface labels, not user-entered settings or customer data.

Enable the plugin through Settings → Plugins. NetSeller imports its catalogs after migrations succeed. Values are imported for languages that exist in the installation; supplying a JSON file does not create a new language.

Administrators can edit imported labels under Settings → Languages, in the Plugins translation area.

Deploy changed catalogs and enable the plugin again; if it is enabled, disable it first. Application reinitialization alone does not import the files.

Import writes the supplied values again, including values previously edited in the backoffice. Preserve customer-specific wording before reimporting. Keys removed from all of the plugin’s catalogs are removed from its imported translations during synchronization.

Disabling a plugin retains translations. Deleting its plugin data removes the plugin-owned keys and values.

Symptom Check
Activation reports a catalog error Valid JSON, flat string values, supported locale filename and an English source catalog.
A new label still shows its fallback Matching key, successful activation/import, and the language’s presence in the installation.
Activation reports a key ownership conflict Use your own plugin namespace; do not claim another plugin’s or Core’s key.
Backoffice wording changed after an update Catalog import replaces supplied values; restore the intended wording after import.

The bexio integration ships en.json, de.json, fr.json and it.json. Its keys use the namespace plugin.bexio.integration., for example:

{
"plugin.bexio.integration.connection_connected": "Connected",
"plugin.bexio.integration.connection_not_connected": "Not connected"
}

This keeps interface states translatable without translating external account names or stored connection data. Use your own plugin ID in equivalent keys; do not copy the bexio namespace into another plugin.