5 ms·
One thing that I've always struggled w.r.t i18n is having to split messages up into often times less coherent chunks in order to add things like links or toolti
by lewisl9029 8y ago
One thing that I've always struggled w.r.t i18n is having to split messages up into often times less coherent chunks in order to add things like links or tooltips or styling elements in the middle of the text, which makes it more difficult to localize messages as a holistic piece independent of the source language.
For a slightly contrived example to demonstrate this, let's say you have a string like this:
"Please click here 7 times to confirm"
Where you want to make the "click here 7 times" look like a link by wrapping it in a <a> tag, or just styled differently using a styled <span>.
Using something like react-intl, which is what I've used in the past, you'd have to do something like this:
<FormattedMessage
id="confirm"
defaultMessage={`Please { confirmLink } to confirm`}
values={{
confirmLink:
<a>
<FormattedMessage
id="confirm-link"
defaultMessage={`click here {clickCount, number} {clickCount, plural,
one {time}
other {times}
}`}
values={{ clickCount: 7 }}
/>
</a>
}}
/>
If then some language happens to require a completely different sentence structure that changes the ordering such that the "to confirm" part needs to be interleaved somewhere in the middle of the "click here 7 times" message to sound fluent, this would not be able to accommodate that.
I'm wondering how people generally deal with this, in React and elsewhere.
- Sylos 8y agoFrom an accessibility point of view, it's also recommended to avoid links that only span over such half-sentences. Screen reader users will often navigate your page by cycling through the links that are on the page and then they'll get only the link-text read out, not the surrounding text.
- lewisl9029 8y agoMore and more, I'm starting to think maybe plain text isn't always the best abstraction to be using for defining messages for i18n. If messages were to be defined in terms of whatever primitive you're using to build your UI (i.e. React components if you're using React, and raw html template nodes if you're working with plain html), then all of this impedance mismatch might disappear. In the React case, a component oriented approach to i18n could maybe look something like this: const DefaultMessage = ({ clickCount }) => <span> Please <a>click here {clickCount} {pluralize(clickCount, {one: "time", other: "times"})} </a> to confirm </span> // some theoretical language that requires putting "to confirm" between // "click here" and "7 times", using English for clarity const MessageInSomeOtherLanguage = ({ clickCount }) => <span> Please <a>click here to confirm {clickCount} {pluralize(clickCount, {one: "time", other: "times"})} </a> </span> // some theoretical component that renders different components // based on the language and passes through props <FormattedComponent id="confirm" defaultMessage={DefaultMessage} props={{ clickCount: 7 }} /> This feels a lot more elegant and flexible to me. Though it would make it more difficult for non-technical folks to contribute to translations, which might not be much of a concern if your company has the resources to support localization teams in-house. Am I overlooking any other obvious downsides to this approach? Does anyone know of any libraries that offers a similar API, or have experience using a similar approach?
- ivan_gammel 8y agoIt will be way easier to maintain, if the L10n resources won’t be mixed with the markup or code. Check this for example: https://github.com/resource4j/resource4j https://github.com/resource4j/resource4j
- madeofpalk 8y agoThe problem here is tooling and workflows. Often you'll be using a SaaS product to manage translations, like Lokalise or similar products. At the end of the day, these just give you a whole bunch of strings to put in your app. I've found this really hard. At my last place we just ate the cost (and ugliness) of included HTML in this strings and dangerously inserting them into the page.
- pas 8y agoThat's always a problem, even with templates. Angular does sanitization. Which is of course not ideal from a runtime performance point, but they probably weighted the cost-benefit and found it to be an okay trade-off. Though if the i18n string files are present at build time, then this sanitization step could be done there.
- michaelmior 8y agoDefinitely a problem. I think what will really make Fluent take off is if someone can provide good translation tools and workflow. The syntax looks great, but for most projects, I suspect it's impractical to expect translators to write Fluent manually.
- stasm 8y agoOur goal was to design a simple DSL which is easy to read and make small edits to. Copying and pasting is a powerful learning method :) We're also working on creating richer and more streamlined authoring experience in Pontoon, Mozilla's translation management system. You can read about the current state of Fluent support in Pontoon in my colleague's post at https://blog.mozilla.org/l10n/2019/04/11/implementing-fluent-in-a-localization-tool/ https://blog.mozilla.org/l10n/2019/04/11/implementing-fluent....
- stasm 8y agoThis 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!
- DavidPiper 8y agoI'm currently fighting (again) the same kind of battles with react-intl. I'd also be keen to hear others' ideas to make this easier / better. Another example is when you need to inject an image as a text decoration that includes text, or only makes sense in a particular part of the sentence. One workaround I've considered is to add the decoration directly to the font you're using, so you can literally translate the decoration as text, but that doesn't usually feel like a reasonable solution, and still might not solve the problem for all languages.
- ascar 8y agoGNU gettext is a well working framework for i18n. For the sentence ordering we include all variables in the translations, but split translations on styles. We then give the translator the text in order of html appearance in source code for context. Translators can then rearrange everything but the variable across the string and also leave stuff blank when necessary. It's not perfect, but works in most cases. And in the end developers and designers must consider the "translateability" of the UI. It's always possible to create untranslateable UI.
- zbraniecki 8y agoHi! We've been working with and evaluating Gettext when we started Fluent. Our opinion is similar to Unicode's - Gettext is fundamentally flawed design for internationalization purposes. Here's you can find more detailed explanation of our position - https://github.com/projectfluent/fluent/wiki/Fluent-vs-gettext https://github.com/projectfluent/fluent/wiki/Fluent-vs-gette... Please, don't take it as a criticism of using it. We just don't think it scales and we don't think it's possible to produce high quality sophisticated multilingual UI's with it, but if it works for you, don't touch it :)
- ngrilly 8y ago> Our opinion is similar to Unicode's - Gettext is fundamentally flawed design for internationalization purposes. Did the Unicode consortium express critics about gettext? Could you provide some reference about this?
- zbraniecki 8y agoI don't know if there's any public statement about this. I base my position on experience at Unicode Conference and work on CLDR and ICU. I understand that it diminishes the value of my claim. I can also point out to ICU MessageFormat - which has been designed much after Gettext and, I'd dare to say on purpose, bares no resemblance to it.
- 8y ago
- Zenbit_UX 8y agoThis is how I handle those cases in vue: clickHere: { one: 'Please' two: { singular: ' click here {0} time ', plural: ' click here {0} times ' }, three: 'to continue', link: 'google.com/en/' } <span> {{ this.$i18n('clickHere.one') }} <a href="{{ this.$i18n('clickHere.link') }}" >{{ this.count > 1 ? this.$i18n('clickHere.two.plural', this.count) }} : this.$i18n('clickHere.two.singular', this.count) }}<\a> {{ this.$i18n('clickHere.three') }} <\span> Don't forget to localize your links! English might not need it but many languages eventually will point to a different url.