Localization
Plugster ships a small, intentionally minimal localization helper. It is not a full i18n library — there is no locale fallback chain, no plural forms, no message bundles loaded from disk. What it offers is enough for the cases the framework cares about: a Plugster that has user-facing text needs a way to render that text in the active language, picked at view-render time by the server. The API is two methods on every Plugster instance: setLocales and translateTo.
setLocales
#
setLocales registers the per-language translation tables for this Plugster instance. The argument is an object whose keys are language codes (the same codes you put on data-lang) and whose values are dictionaries mapping the full English text to its translated version.
export class SignInPlugster extends Plugster {
constructor(outlets, createOneTimePin) {
super(outlets);
this._createOneTimePin = createOneTimePin;
this.setLocales({
'es': {
'Sign in': 'Iniciar sesión',
'Email': 'Correo electrónico',
'Sending the one-time pin...': 'Enviando el código de un solo uso...'
}
});
}
}
Notice that the convention is full English text as the key, not a short code like 'sign-in.title'. The reason is that the default render — the case where no translation is found — falls through to the key itself. With full English as the key, the worst case is the page rendering in English; with short codes, the worst case is the page rendering visible identifiers like sign-in.title at the user.
setLocales belongs in the constructor. It does not touch the DOM and is safe to call before init() runs.
translateTo
#
translateTo(language, text) looks up the translation for the given English text in the given language, and returns the original text unchanged if there is no entry. It is meant to be called from afterInit() and from any handler that needs to render text after that.
afterInit() {
let self = this;
self.lang = self._.root.data('lang');
self._.titleLabel.text(self.translateTo(self.lang, 'Sign in'));
self._.emailInput.attr('placeholder', self.translateTo(self.lang, 'Email'));
self._.submitButton.on('click', function () {
self._.statusLabel.text(self.translateTo(self.lang, 'Sending the one-time pin...'));
self._createOneTimePin(self._.emailInput.val());
});
}
Three behaviors of translateTo worth knowing:
-
Missing language table → return the input. If
setLocaleswas never called for the requested language, the method returns the English text unchanged. The page renders correctly, just in English. - Missing key in an existing table → return the input. If the language table exists but does not contain the requested key, the method returns the English text unchanged. Same outcome: English fallback.
-
Empty or undefined language → return the input. If
self.langis empty (thedata-langattribute was not set), the lookup short-circuits and returns the input. No need to guard the call site with anif.
Getting the active language #
The framework does not pick the active language for you. The convention is that the server template that renders the view writes the language code onto the view element via data-lang, and the controller reads it from the implicit root outlet during afterInit():
<div data-controller-name="SignInPlugster"
data-lang="{{ lang_code }}">
...
</div>
afterInit() {
this.lang = this._.root.data('lang');
// ... use this.lang with translateTo(...)
}
Storing the value on the instance (this.lang) keeps every call site short. It also means subsequent runtime changes to the data-lang attribute do not affect already-rendered text — Plugster's localization is a one-shot per render, not a reactive binding.
Where localization belongs #
For static page text that the server template owns (titles, headings, body copy that lives in the HTML directly), use the server's localization machinery — the {{ _() }} helper in Jinja-rendered Plugster views, for example. The server already knows the active language and can render the right text into the HTML before the Plugster ever sees it.
setLocales / translateTo are for text the Plugster itself produces at runtime: status labels updated after a network call, button labels swapped during a flow, items appended into a list outlet (whose row template is static HTML and therefore unreachable by the server's localizer). Anything the controller writes into the DOM after init() is a candidate.
Putting it together #
<div data-controller-name="SignInPlugster"
data-lang="{{ lang_code }}">
<h2 data-outlet-id="titleLabel"></h2>
<input data-outlet-id="emailInput" type="email">
<button data-outlet-id="submitButton"></button>
<p data-outlet-id="statusLabel"></p>
</div>
import {Plugster} from "https://cdn.jsdelivr.net/gh/paranoid-software/plugster@1.0.14/dist/plugster.min.js";
export class SignInPlugster extends Plugster {
constructor(outlets) {
super(outlets);
this.setLocales({
'es': {
'Sign in': 'Iniciar sesión',
'Email': 'Correo electrónico'
}
});
}
afterInit() {
let self = this;
self.lang = self._.root.data('lang');
self._.titleLabel.text(self.translateTo(self.lang, 'Sign in'));
self._.submitButton.text(self.translateTo(self.lang, 'Sign in'));
self._.emailInput.attr('placeholder', self.translateTo(self.lang, 'Email'));
}
}
Render the page with lang_code = 'en' and you get the English text via the fallback path. Render it with lang_code = 'es' and the same code paths produce Spanish output. No additional wiring is needed.