4 ms·
This is a great point and something that we've seen come up very often in building UIs. The good practice which we recommend to developers at Mozilla is to avoi
by stasm 7y ago
This is a great point and something that we've seen come up very often in building UIs. The good practice which we recommend to developers at Mozilla is to avoid splitting or nesting messages, because it makes it harder for translators to see the entire translation at once.
We've taken a layered approach to designing Fluent: what we're announcing today is the 1.0 of the syntax and file format specification. The implementations are still maturing towards 1.0 quality, but let me quickly describe what our current thinking is.
For JavaScript, we're working on low-level library which implements a parser of Fluent files, and offers an agnostic API for formatting translations. On top of it we hope to see an ecosystem of glue-code libraries, or bindings, each satisfying the needs of a different use-case or framework.
I've been working on one such binding library called fluent-react. It's still in its 0.x days, but it's already used in a number of Mozilla projects (e.g. in Firefox DevTools). In fluent-react translations can contain limited markup. During rendering, the markup is sanitized and then matched against props defined by the developer in the source code, in a way that overlays the translation onto the source's structure. Hence, this feature is called Overlays. See https://github.com/projectfluent/fluent.js/wiki/React-Overlays https://github.com/projectfluent/fluent.js/wiki/React-Overla....
Here's how you could re-implement your example using fluent-react. Note that the <a>'s href is only defined in the prop to the Localized component.
<Localized
id="confirm"
$clickCount={7}
a={<a href="..."></a>}
>
{"Please <a>click here {$clickCount ->
[one] 1 time
*[other] {$clickCount} times
}</a> to confirm."}
</Localized>
I'd love to get more feedback on ideas in fluent-react. Please feel free to reach out if you have more questions!
- chrischen 7y agoYou should take a look at js-lingui. Child components are automatically converted to symbols by way of a babel-macro (no run time parsing of complex translations).
- Vinnl 7y agoWould this also allow for translating e.g. the <a>'s `title` attribute, or e.g. an `aria-label`?
- stasm 7y agoIt's something that I definitely plan to add. There's even an open issue about it! https://github.com/projectfluent/fluent.js/issues/185 https://github.com/projectfluent/fluent.js/issues/185
- Vinnl 7y agoGreat! It sounds like a very cool project to work on :)
- Zenbit_UX 7y agoWhat's stopping you from doing that now? Just add a new translation for those labels in the source document and bind it to the attribute tag.
- lewisl9029 7y agoI've seen a few libraries that use a similar approach of parsing strings for pseudo-elements and then matching them with React elements to avoid splitting up messages, but I've always felt a lot of resistance towards adopting something like that because it means incurring the runtime cost of parsing a string for elements when you can easily have hundreds or thousands of messages being rendered at once. (Call it a premature optimization if you must, but I've been bitten enough times in the past for adopting libraries/approaches that scaled poorly performance wise and had to pay the cost in untimely, painful refactors.) I feel there's a fundamental impedance mismatch here because we're defining messages as strings but the rest of our UI as React components. I described here a potentially different component-oriented approach as an attempt to get rid of this impedance mismatch: https://news.ycombinator.com/item?id=19681129 https://news.ycombinator.com/item?id=19681129 I'd love to hear some thoughts on that approach from folks with more real-world experience working with i18n than I do (which is not a whole lot sadly, given the nature of the kinds of projects I've worked on in the past).
- ivan_gammel 7y agoI’ve been trying to solve this problem in the resource4j library for Java, which can cache rendered strings, but in the end it’s always a memory vs performance problem. I didn’t publish any artificial benchmarks, but in couple real projects (resource4j+thymeleaf) performance impact was usually negligible.
- Andrex 7y agoWhy fluent-react and not (in addition to) fluent-web-components? :(
- Vinnl 7y agoOne advantage I can imagine is that you can prerender the React components, outputting e.g. plain HTML. With tools like e.g. React Static, that means you can somewhat ergonomically generate different static websites for different languages, avoiding the runtime costs of looking up the correct strings.
- cbenz 7y agoUsing the Svelte JS library (https://svelte.technology https://svelte.technology) you can have both: server-side rendered components [1] and compile to web components (custom elements) [2] Another advantage is that the components compile to vanilla JavaScript, so we don't rely on a runtime library to run the application. [1] https://svelte.technology/guide#server-side-rendering https://svelte.technology/guide#server-side-rendering [2] https://svelte.technology/guide#custom-elements https://svelte.technology/guide#custom-elements
- drdaeman 7y ago> `[one] 1 time *[other] {$clickCount} times` How does this work for languages that have more complex pluralization rules? E.g. in Russian it's "1 раз", "2 раза", "11 раз", "12 раз", "22 раза" and "55 раз" - the case depends on the number ending, with exceptions for 11, 12, 13 and 14.
- schoen 7y agoArabic is even more complicated! https://en.wikipedia.org/wiki/Arabic_grammar#Cardinal_numerals https://en.wikipedia.org/wiki/Arabic_grammar#Cardinal_numera...
- zbraniecki 7y agoThat's a great question! Fluent relies on Unicode Plural Rules [0] which allow us to handle all (as far as Unicode knows) pluralization rules for cardinal and ordinal (and range) categories :) [0] http://cldr.unicode.org/index/cldr-spec/plural-rules http://cldr.unicode.org/index/cldr-spec/plural-rules
- stasm 7y agoIt's up to the localizer to define variants corresponding to the language's plural categories. For Russian, that's (one, few, many). Interestingly, this particular example could simplified to (few, *), because "раз" is good for both 1, 5, 11, 55, etc. See https://projectfluent.org/play/?id=7d22f87c04b23b86d9f9149d5d1090fc https://projectfluent.org/play/?id=7d22f87c04b23b86d9f9149d5... for an example of this in action. Authoring tools can help here, too. Pontoon, Mozilla's translation management system, pre-populates plural variants based on the number of plural categories defined in Unicode's CLDR.