Separate out message value part responsible for formatting #186
Replies: 5 comments 3 replies
-
I think I disagree with Zibi's premise, but possibly agree with his conclusions.
This is almost where we're at, as I'd argue that the not-literal thing that is shared between the model architectures isn't actually a "placeholder", but a "function" or a "function reference". To me, that implies a slightly wider implementation than just "placeholder", i.e. that it specifically also allows for the representation of "elements" as presented here; a generic function can really do anything at all, including wrapping its arguments in an element. I do agree that there's a clearly identifiable use case for an "element", but I'm not yet convinced that there's sufficient benefit for differentiating it from a generic "function". @zbraniecki, do you have an idea yet for what its interface would look like? That would help at least me in reasoning further about it. |
Beta Was this translation helpful? Give feedback.
-
I've seen 2 models in use. One is with SSML, which many voice assistants use. So you have a lot of XML/HTML mixed in. The other is with Markdown, which Apple supports with an AttributedString. I've heard that XML is too hard for the "average" person, especially with grammar dependencies. So the decision was to use Markdown with AttributedString, and the inflection syntax was simplified. Though the simplification with Markdown makes the inflection more ambiguous for some languages and less functional. The simplification works better for handling a single variable and the language is clear on the starting point for the inflection (the starting point in Russian and German can be hard to determine). Once you start handling multiple variables, the Markdown model with a simple inflect starts breaking down. Some of the overview of of the Markdown with inflection support can be seen here: https://developer.apple.com/documentation/foundation/data_formatting/building_a_localized_food-ordering_app I prefer XML support, and I find Markdown acceptable too. |
Beta Was this translation helpful? Give feedback.
-
If/as we're now initially going with a model that doesn't allow for function composition, I agree that formatting elements make sense as a separate thing from type Element =
| {
type: 'element';
tag: 'empty' | 'start'; // If 'empty', the element has no contents
name: string;
key?: string; // Optional, allows explicit identity when a 'name' repeats
attr?: Record<string, Literal | Variable>;
meta?: Meta;
}
| {
type: 'element';
tag: 'end';
name: string;
key?: string;
meta?: Meta;
}; One really rather interesting effect of accepting this is that we may subsequently need a third output form to properly handle these. Consider for instance a message like:
Rather than only emitting that formatted as ['Hello ', <b>friend Bob</b>, '!']` rather than requiring a user to use the parts output and manually construct that result. This is particularly important for environments such as React and others that really frown on manually forcing the string to be re-parsed. Going a step further, what about elements with dangerous attributes?
To make that work, the link obviously needs an And so I think we need an API like type formatToElements = (
resId: string,
msgId: string[],
scope?: Scope,
elements?: SourceElement[]
) => FormattedElement[] which would, when possible, use and modify the given The actual implementation of such a function is clearly beyond the scope of this WG, given that the details are really dependent on the context. But we do need to account for it in the formatting, and describe the behaviour of the function that needs to be registered in order to format an |
Beta Was this translation helpful? Give feedback.
-
This is how I was thinking (in the EM model) about A message is a sequence of parts, and a part is a piece of plain text or a placeholder (which has a function associated with it). But naming it a function was the bad name. I think should be a "formatter" (class?) that has (at least) 2 methods: So the evaluation part of the engine would also have a
The
So "Hello {VAR(user)}, your card expires on {DATE(expDate, {year:numeric, month:full, day:numeric}" with
The exact representation of the "render to parts" is TBD. So and HTML implementation would create a small DOM, or generate html-as-text, whatever the user wants. And something that adds text-to-speech info can do other things |
Beta Was this translation helpful? Give feedback.
-
Elements: I will quote here an example I gave in #190 The "elements" used for "styling" also need functions. For example When you "render" this message in a GUI environment (let's say a html widget) the "render" function would generate When you do that in a console the "render" function will generate ansi escape sequences And when the console app has the output redirected to a file the elements are rendered to nothing. A developer would implement This way you can reuse the message in various environments and platforms. For example Android can generate a This goes the direction that Zibi touched in For inspiration see |
Beta Was this translation helpful? Give feedback.
-
At the moment, all proposals for MF2.0 data model contain two "types" of parts of a message: Text and Placeholder.
This mimics very closely the data model of Fluent (reference AST or Rust AST).
That means, that every "part" of a message value is either text literal, or a placeholder that is eventually meant to be resolved into a text literal.
So, an data like
[Text("Hello, "), Placeholder(Variable("userName")), Text("!")]
can be expected to be late-resolved into[Text("Hello, "), Text("John"), Text("!")]
.That sequence is then concatenated and lo-and-behold here we have
Hello, John!
string returned!This model works as long as we only operate with the realm of localization API as a way to retrieve a "plain" single string out of each message.
But, as the group knows, I believe that this way of thinking is oversimplified and ignores the reality of the industry we design for. In particular, in #118 (and even further in #65), I argue that the produced string rarely is fed into a plain text consumer.
That the reality of today is that majority of software are rich user interfaces, either visual (HTML, DOM, React, Android UI, QML) or audio (voice assistants), and soon VR/XR/AR UIs will start being a major topic.
From the perspective of the text+placeholder model, any semantic interaction between localization output and the UI itself requires a second-pass parsing of the localization output, and is opaque from the perspective of the localization.
Example:
key = Hello, <b>{ $userName }</b>!
orkey = Hello, { EMPHASIS($userName) }
whereEMPHASIS
surrounds the variable in<b></b>
or any combination of those will always return a sequence ofText
elements to be concatenated into a final string.What happens next is that then this string gets parsed, for example by DOM parser and injected into DOM tree not as a text string, but as a DOM fragment.
That, in my opinion is a mistake. We are limiting our ability to provide augmentation to l10n tooling, and to build chain and runtime software, by pretending that "we just return text" and turning a blind eye to what happens after.
DAG vs Tree
In MF2.0 discussions, Mihai and Eemeli provided two different proposals on how to "resolve" it which currently lies at the core of our conversation.
Example
Let's look at how Fluent handles DOM:
(fluent)
You can see that
<b>
is "hidden" as part of text making it very hard for tooling to reason about it.In MF2.0 models I see exploration of moving that to
Placeholder
and, I'd argue, "hiding" it there:(sequence)
(DAG)
(tree)
There are pros and cons of the three solutions. Sequence is simple, DAG allows for various meta information about the order, interplotting, etc. and tree secures validation.
And "hiding" it in placeholder is of course much better than in plain text, because you can provide meta information to it.
But I'd argue that there is a separate level on which we could and should think of it.
Part::Element
I suggest that we separate out
Element
(orFormatting
) as a part of the message value alongsideText
andPlaceholder
.It is not strictly necessary, since
Placeholder
is a catch-all, but it provides a separation of concerns between placeholders, which are runtime-focused (variables, function calls, dynamic references etc.) or at least require a full context of message groups to be available (message references) to reason about them, from formatting nodes that are equally available as textual elements, but have semantic meaning distinct from text:In this model, the tooling can easily reason about the formatting information, and the types of arguments we support for
Element
can be separated fromPlaceholder
.Element
could reason about open/close, or standalone, or order, which are not relevant to placeholders, and in previous models we would likely want to add them to placeholder just for what I call element.This model still allows for semantic UI in CAT tools which can identify the "draggable" part to be starting from
tag1
and ending on its closing, and tools can flag incomplete translations just like in DAG and tree models. (not in sequence or current Fluent model).This model could also be useful for voice assistance where instead of
b
we could have information about voice tone, actor, or loundness to be used for given section.We could even forgo
b
and useemphasis
maybe to allow for a single message to be used in both contexts? Or maybe it's a stretch. @grhoten - wdyt?Finally, this would allow us to reason about formatting structure in early -resolution even if we'd allow (which we may not initially!) things like:
and then in our function registry put:
those two files available early, have enough information for a validator to reason about the validity of the message formatting structure!
Therefore, I'd like to suggest the group to consider such an alternation to the model, which I believe may help us disentangle the DAG/tree value, constrains and limitations and provide improved ability to semantically reason about the data.
Beta Was this translation helpful? Give feedback.
All reactions