Internationalising your Plugin/Theme
You are free to write your plugin and/or theme in whatever language you'd like, although if you wish to support multiple languages, NodeBB has a language engine that you can utilise.
In this article, we'll be referring to plugins. Keep in mind that themes are also plugins, so the implementation is the same for both.
Step 1: Directory layout of translations
To begin, let's define some language keys and translations! In your plugin, create a new directory to house your translations. Keep in mind that the structure of the files inside this folder must match that of NodeBB itself: Each sub-directory is named after a language key (e.g. en-GB), and contains .json files housing the translations themselves.
In the commands above, I've created my languages folder, with two languages, English (en-GB), and Spanish (es).
Step 2: Add your translations
In the sub-directories created in Step 1, I'll create text files with a .json extension. These file will house the plugin's translations.
In languages/en-GB/myplugin.json:
In languages/es/myplugin.json:
You can name the file whatever you'd like, but keeping it something recognizable (e.g. related to your plugin) is ideal. You can also name a file the same name as an existing language file in NodeBB. NodeBB will automatically merge the two localisation sets together into a single set. For example, NodeBB contains a user.json file. You can also name your file user.json and you can call language strings from both files in your plugin/theme.
Note
Remember to change the name myplugin to something related to your plugin!
Step 3: Tell NodeBB that you have language files to load
NodeBB won't automatically know you have translations to load, so we'll need to add a line to our plugin.json file to tell NodeBB to load our language files, and where those files reside.
Open up plugin.json and add a new property called languages:
The value for this property is the path to wherever your language files reside, relative to the plugin's root folder. In this case, I've placed my languages in a folder called languages directly in my plugin's root folder, so I just need to put in languages. If my languages were in a sub-folder called public, then the correct value here would be public/languages.
Step 4: Use your translations in your plugin
There are a number of ways you can use your translations in your plugin:
Server-side
In your server-side code, you can invoke the translation engine as follows:
var translator = require.main.require('./src/translator');
translator.translate('[[myplugin:greeting]]', function(translated) {
console.log('Translated string:', translated);
});
Note
Server-side invocation will translate to server's default language in ACP settings: http://<hostname>/admin/general/languages
Client-side
In the browser, you can invoke the translation engine as follows:
require(['translator'], function(translator) {
translator.translate('[[myplugin:greeting]]', function(translated) {
console.log('Translated string:', translated);
});
});
Note
Client-side invocation will translate to the language in user settings: http://<hostname>/user/<username>/settings
Templates
In your templates, you don't need to do anything special to invoke the translation engine, it is run through automatically, and parses any language strings matching the following syntax: [[resource:key]]. So for our plugin:
Passing variables into a translation
A translation string can contain positional placeholders %1, %2, and so on.
Supply the values as comma-separated arguments inside the language-string reference:
The same comma syntax works wherever a language string is accepted, including
server-side strings such as a notification's bodyShort.
When you build the language string from dynamic values, don't concatenate them by
hand โ a parameter that itself contains a , or a % will break the surrounding
translation. Use translator.compile, which escapes those characters in each
parameter for you:
const translator = require.main.require('./src/translator');
// translator.compile(key, ...params)
bodyShort: translator.compile('myplugin:welcome', username, count)
Using the translator with async/await
translator.translate is callback-style. To use it inside an async function, wrap
it in a Promise:
const translator = require.main.require('./src/translator');
const greeting = await new Promise((resolve) => {
translator.translate('[[myplugin:greeting]]', resolve);
});
(Optional) Step 5: Tell NodeBB that a particular language is the default
NodeBB itself supports around 40 languages, so you couldn't possibly be expected to translate them into every language! To define a specific language as default, add the defaultLang property to your plugin.json file:
Now, if a user utilising a language not supported by your plugin loads a language resource for your plugin, they will see the Spanish translation, as it is the designated fallback language.