Using fluent in npm packages

(Heidi Kasemir) #1

I’m interested in using fluent to localize the react ui of some npm installable packages. (think something like

The trouble I’m coming across, is that since the package is developed separately from the application, it makes the most sense to bundle the translations in with the package, but I don’t know how to access those files in this case.

In my app, the ftl files are served from the /public folder, which I fetch as needed and pass to my LocalizationProvider, but if the ftl files for a package are in the node_modules folder, how do I get at them?

(Staś Małolepszy) #2

Interesting! fluent-react has been intentionally designed to be agnostic about where the translations come from and how they are retrieved. Nevertheless, this is an interesting use-case which brings to mind similar challenges we’ve discussed relating to web components.

In your setup, are you trying to put the LocalizationProvider in the package itself, or do you use your app’s LocalizationProvider augmented with the translations from the package?

Some build tools might offer a way forward. Earlier today I moved all of fluent-react's example app to Parcel. One interesting detail I encountered was that in Parcel, all static files used by the app at any point needed to be somehow imported into the app. Only then would Parcel put them in the dist directory.

I had to add the following import line to one of the example’s code:

When queried in the runtime, ftl is an object of {"en-US": "/public_url/en-US.ebf32f.ftl", "pl": "/public_url/pl.acb78e.ftl", … }. Fetching the translation files is then as simple as await fetch(ftl[localeCode]).

Maybe in your setup you could leverage the build system in a similar fashion?

(Heidi Kasemir) #3

sorry for the late reply, I only get to spend time on this a couple times a week…

This looks really interesting! I’ll look into using parcel.

I think ideally, the component would ship with its own strings so the app consuming it doesn’t have to know about them.

I guess this means the component would need its own LocalizationProvider

(Heidi Kasemir) #4

I think I might be overcomplicating it in my head, actually, I feel like most components will have a small enough number of strings that there shouldn’t be a noticeable advantage to fetching them asynchronously. The only trick will be how to maintain the ftl file format so that we can integrate with pontoon. So at build time, it would take the ftl contents and package them into a similar format to this:

(Staś Małolepszy) #5

Inlining translations like that looks like the easiest solution indeed, especially if the number of available locales isn’t too high.

Regardless of the exact method of fetching translations, I think it would be good to have an established good practice for how to ensure external components (e.g. from npm) use the same language as the whole app. Just relying on navigator.languages might not be enough if the app allows users to manually set their language preferences, or if the user’s first language is not available in the app.

How about this?

External components which are intended to be used as dependencies in larger projects should by displayed in the language of entire app. They should accept an optional languages prop which is an array of languages negotiated between the user’s preference and the languages available in the app.

<WidgetFromNPM languages={currentLanguages} .../>

When the prop is missing, navigator.languages should be used instead. External components should then perform their own language negotiation based on the languages they support.