-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hook in responsive localization early #7
Comments
Thanks for your thoughts; it’s nice interacting with others that care about these topics as well! Localisation is something that we care about, and which will definitely be solved well inside Druid. In fact, Druid already uses Fluent, having some half-baked localisation support where it stores the Fluent resources for the current locale in We’re also well aware of all of the other nuances of localisation, that it’s not just text labels, but other properties as well. So that’s Druid. This is Crochet, an experiment in replacing Druid’s lensing approach. One of the motivating reasons for Crochet is that lensing means that the widget must manage reactivity itself—so Label uses a lens for its text, but if you want colour to be reactive, the Label widget needs to have deliberately used a lens there, and if it hasn’t, you’re out of luck. But under Crochet, all attributes become reactive at very little developer or runtime cost. This will make Crochet even better for localisation: now you can do much more involved UI changes by locale without the widgets having had to be carefully designed for it. Back to what Crochet is at present. It’s an experiment in one particular area: replacing lensing. Consequently, we’ve stayed away from adding any localisation yet, because we know it’ll work within the model (more easily, in fact), and implementing it at this time would be just a distraction from testing the viability of the overarching concept. Notwithstanding that, your post has made me think a bit more about how Crochet’s concept of attributes (not yet implemented, but discussed on our Zulip chat—join us if you like!) might fit in with Fluent attributes. There’s scope for some some really nice ergonomic synergies [there, I said it] there. I think it should end up at least as nice as the concepts you’ve listed there, which it definitely couldn’t have done in the lens-based approach. Summary:
|
Thanks for your response! The other side of the equation I'd like to direction your attention to is nested UI fragments. I'm going to use HTML here, but I think it translates to other UI paradigms quite well:
In Fluent, we implemented a very simple DOM Overlays which allow us to do: HTML:
L10n:
And we'll overlay those two preserving source information so that we can retranslate to another language from the source rather than from the output of source+en-US. Couple things to notice is that we allow for a subset of HTML elements to be introduced by the localizer - those elements are called The other elements must appear in the source and then we match them - That's just a beginning, but it's something that binding model gives you, and |
I understand why localization is always treated as an afterthought and every time a new approach to GUI comes up it comes with some version of:
but I'm here to argue that this is both a bad paradigm and that the reason it doesn't get fixed is that once the GUI toolkit is mature enough to actually tackle the problem, there's too much sunk cost in this model to revisit it.
So I'd like to suggest you actually tackle it early :)
First, translation is not a string that one plasters onto a widget. It's a binding. Bind your widget to a localization unit.
In 20-years-ago command line textual apps, you could get away with such glorified
printf
, but that model very badly translates onto UI trees.UI element may be nested, have some internal structure, it's value may have markup and structure (think,
<strong>
,<sup>
,<span>
in HTML inside a string), it may have attributes, both textual (button's accesskey or tooltip) but also non-textual (color may be culturally dependent, icon associated with a button may be different for some locales, like rewind back/fwd in RTL cultures or some emojis in Japanese culture) and so on.So instead of thinking of localization as a
printf
into aString
, we should start talking about a compound object with multiple elements inside it - a Label or a Button or a Menuitem, and a compound localization unit that contains information needed so that those two combined can be laid out and painted on the screen.Second, in reactive UI, retranslate every time the variable changes, or the locale changes (or the locale resources get updated!) during life cycle of the app.
Those two concepts work together really well - if you annotated by binding, you can just walk your UI Tree and retranslate at will using different localization resources whenever needed. You can cache the result, invalidate that cache and so on, without any overhead on the developer.
You can localize asynchronously (think, you localize into
fr-CA
but midway through the localization stage you realize that some button cannot be localized and you want to fallback onfr
resources, asynchronously load them and have the button be infr
as a fallback). You can do it because the localization pass is not related to how developer annotates the element with localization unit.How would it look like?
In HTML we do:
I think you can do better here. Maybe:
There are deep consequences to that change in three directions:
I can dive in all three of them, but I just wanted to start by suggesting shifting the approach to string injections early.
You can also treat an element as either controlled by l10n or manually:
This is actually fairly common in my experience of working with Fluent. An example is when there's a menuitem that either takes a value from some database (say, credit card name) or displays a localized message "Unknown Type".
In such case we want to write:
This Rust code can be called every time menuitem list has to be rebuilt, but a function that updates translations is independent from it and can run on a different scheduler and react to different events, and be asynchronously loading resources blocking layout/paint, but not blocking this function.
The text was updated successfully, but these errors were encountered: