Good Practices for Developers
This document outlines the good practices for making the code localizable with Fluent.
Fluent implementations usually offer a declarative API. In fluent-dom
avoid
the imperative formatValue
method:
let value = await l10n.formatValue("message-id");
element.textContent = value;
Instead prefer the declarative setAttributes
API:
l10n.setAttributes(element, "message-id");
fluent-dom
will make sure that the element is re-translated if the user's
current language changes and will also properly handle any attributes defined
in the translation. A safe subset of HTML markup can also be supported.
Usually it is convenient to make the code operate on l10n identifiers at all times and set them on UI elements rathen than retrieve a translation manually.
(There are valid use-cases for using formatValue
: modal dialogs, push
notifications, alert()
etc. In these cases the translation is displayed as
a one-off and doesn't persist in the UI so there's usually no need to
re-translate it on language change.)
It's tempting to abstract common parts of translations via parametrization. Resist the temptation: it obfuscates the translations and makes it harder for localizers to see the full context of their work. Instead prefer some redundancy by repeating the common parts of translations in multiple messages.
This is wrong:
reaction-thumbs-up = a thumbs up
reaction-smiley-face = a smiley face
reacted-with = { $user_name } has reacted with { $reaction_type }.
let reaction_type = notification.type === "THUMBS_UP"
? l10n.formatValue("reaction-thumbs-up")
: l10n.formatValue("reaction-smiley-face");
l10n.setAttributes(element, "reacted-with", {reaction_type});
This is better because it avoids the imperative call to formatValue
:
reacted-with-thumbs-up = { $user_name } has reacted with a thumbs up.
reacted-with-smiley-face = { $user_name } has reacted with a smiley face.
l10n.setAttributes(element, `reacted-with-${notification.type}`);
This is best because it improves the grep
-ability of the code:
let messages = {
THUMBS_UP: "reacted-with-thumbs-up",
SMILEY_FACE: "reacted-with-smiley-face",
};
l10n.setAttributes(element, messages[notification.type]);
Only use select expressions when the language requires them; do not use select expressions if the app logic requires them. Select expressions and their variants are optional and private; some languages might choose to not implement variants at all.
This is OK because his
, her
and their
are required by the English
grammar:
shared-schedule =
{ $other_user_name } has shared { $other_user_gender ->
[male] his
[female] her
*[other] their
} schedule with you.
This is wrong because THUMBS_UP
and SMILEY_FACE
are required by the app and
choosing one of them as the default variant doesn't make much sense:
reacted-with =
{ $user_name } has reacted with { $reaction_type ->
*[THUMBS_UP] a thumbs up
[SMILEY_FACE] a smiley face
}.
This is also wrong because add
and remove
are required by the app:
item-action =
Are you sure you want to { $action ->
*[add] add
[del] remove
} this item?
The default variant (here: *[add]
) should make at least some sense for all
possible values of the selector ($action
). In the snippet above showing the
default variant in case of a problem with $action
can lead to data loss.
Use separate messages to make sure all localizations have translations for all the cases required by the app. This is the fix:
item-add = Are you sure you want to add this item?
item-del = Are you sure you want to remove this item?
One more example. This is OK:
new-notifications =
{ $num ->
[0] No new notifications.
[one] New notification.
*[other] { $num } new notifications.
}
This is wrong because the 0
case serves a different purpose than the two
other variants.
items-selected =
{ $num ->
[0] Select items.
[one] One item selected.
*[other] { $num } items selected.
}
Some locales might not even define the 0
case and by Fluent's design there's
no way of detecting it because variants are private. This is the fix:
items-select = Select items
items-selected =
{ $num ->
[one] One item selected.
*[other] { $num } items selected.
}