Using Fluent
This tutorial will show how to use fluent to localize the message in our <h1>
element.
If you don't have a Parcel project set up with fluent files, please refer to the previous sections.
First, we need to add and import our Fluent dependencies.
For this tutorial we will install fluent/bundle
and fluent/dom
via the following command:
terminal
npm install @fluent/bundle @fluent/dom
And verify that the dependencies are added to your package.json
.
package.json
"dependencies": { "@fluent/bundle": "^0.17.0", "@fluent/dom": "^0.8.0", ... }
Then we need to import some items from these dependencies in index.js
.
src/index.js
import { DOMLocalization } from "@fluent/dom"; import { FluentBundle, FluentResource } from "@fluent/bundle";
Next, we need to add the metadata to the HTML regarding which languages we have available.
Add the following lines to the <head>
section of your index.html
file.
src/index.html
<meta name='defaultLanguage' content='en-US' /> <meta name='availableLanguages' content='en-US, es-MX' />
Now we are going to add a JavaScript function to index.js
to retrieve this meta data.
src/index.js
function getMeta(elem) { return { available: elem.querySelector('meta[name="availableLanguages"]') .getAttribute("content") .split(",").map(s => s.trim()), default: elem.querySelector('meta[name="defaultLanguage"]') .getAttribute("content"), }; }
Now that we are able to retrieve the metadata for which locales are supported, we need a way to resolve the paths to the Fluent resources that we created for those locales.
We are going to add the following line to the <head>
section of the index.html
file:
src/index.html
<link name='localization' content='./localization/{locale}/main.ftl' />
src/index.html
<!DOCTYPE html> <html> <head> <meta charset='UTF-8' /> <meta name='defaultLanguage' content='es-MX' /> <meta name='availableLanguages' content='en-US, es-MX' /> <link name='localization' content='./localization/{locale}/main.ftl' /> <script type='module' src='index.js'></script> </head> <body> <h1 id='welcome'>Localize me!</h1> </body> </html>
We will now need an asynchronous JavaScript function to fetch a FluentResource
from a given path, replacing the {locale}
template with an actual given locale.
src/index.js
async function fetchResource(locale, resourceId) { const url = resourceId.replace("{locale}", locale); const response = await fetch(url); const text = await response.text(); return new FluentResource(text); }
Once we are able to fetch a resource, we will want to be able to turn that resource into a FluentBundle
.
src/index.js
async function createBundle(locale, resourceId) { let resource = await fetchResource(locale, resourceId); let bundle = new FluentBundle(locale); let errors = bundle.addResource(resource); if (errors.length) { // Syntax errors are per-message and don't break the whole resource } return bundle; }
Finally, we will want a top-level generateBundles
function (remember, this is what DOMLocalization
is initialized with) to provide a generator over the available fluent bundles for our supported locales.
src/index.js
async function* generateBundles(resourceIds) { const defaultLanguage = getMeta(document.head).default; yield await createBundle(defaultLanguage, resourceIds[0]); const availableLanguages = getMeta(document.head).available; for (const locale of availableLanguages) { yield await createBundle(locale, resourceIds[0]); } }
This function will first grab the bundle for the default language, and then go through the list of available languages. Yes, there is a redundancy here where the default language is also in the list of available languages. For simplicity, we'll leave it this way.
Out final steps are to grab the translatable <h1>
element, our template path for the resourceIDs
, and construct our DOMLocalization
object to perform the translations.
src/index.js
const welcome = document.getElementById("welcome"); console.log(welcome); const resources = document.querySelector("link[name=localization]"); const resourcePathTemplate = resources.attributes.content.nodeValue; console.log(resourcePathTemplate); const l10n = new DOMLocalization([resourcePathTemplate], generateBundles); l10n.connectRoot(document.documentElement); l10n.setAttributes(welcome, "hello"); l10n.translateRoots();
With this final step, you should be able to invoke
terminal
npm start
and now see Hello!, as specified in our Fluent file for en-US
. You should also be able to change the default language in index.html
to es-MX
and see ¡Hola!.
Congratulations! You've just set up a project from scratch that uses Fluent DOM to localize HTML elements.
In the next section we will cover how to use data-l10n-id
directly to translate DOM elements without doing it manually from JavaScript.