Skip to content
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

Dynamic message references #80

Open
9 tasks
stasm opened this issue Jan 15, 2018 · 36 comments
Open
9 tasks

Dynamic message references #80

stasm opened this issue Jan 15, 2018 · 36 comments
Labels
FUTURE Ideas and requests to consider after Fluent 1.0 syntax

Comments

@stasm
Copy link
Contributor

stasm commented Jan 15, 2018

It is sometimes desired to parametrize message references in placeables. In this issue I'd like to propose a new argument type, extending FluentType which could be used to programmatically pass message references as arguments to messages.

Problem Statement

Redundancy is considered good for localization. It allows localizers to tailor the wording and the grammar of the translation of each particular case. Also see Fluent Good Practices.

In general, the pattern of having one message per item is preferred over factoring the action out to its own message (Delete This { $item }) and passing the translated item in some way.

# Having two separate messages allows localizers
# to customize translations in each, if needed.
delete-picture = Delete This Picture
delete-video = Delete This Video

In some cases, however, this pattern doesn't scale well.

Consider this example from Firefox (source):

# %S is the website origin (e.g. www.mozilla.org)
getUserMedia.sharingMenuCamera = %S (camera)
getUserMedia.sharingMenuMicrophone = %S (microphone)
getUserMedia.sharingMenuAudioCapture = %S (tab audio)
getUserMedia.sharingMenuApplication = %S (application)
getUserMedia.sharingMenuScreen = %S (screen)
getUserMedia.sharingMenuWindow = %S (window)
getUserMedia.sharingMenuBrowser = %S (tab)
getUserMedia.sharingMenuCameraMicrophone = %S (camera and microphone)
getUserMedia.sharingMenuCameraMicrophoneApplication = %S (camera, microphone and application)
getUserMedia.sharingMenuCameraMicrophoneScreen = %S (camera, microphone and screen)
getUserMedia.sharingMenuCameraMicrophoneWindow = %S (camera, microphone and window)
getUserMedia.sharingMenuCameraMicrophoneBrowser = %S (camera, microphone and tab)
getUserMedia.sharingMenuCameraAudioCapture = %S (camera and tab audio)
getUserMedia.sharingMenuCameraAudioCaptureApplication = %S (camera, tab audio and application)
getUserMedia.sharingMenuCameraAudioCaptureScreen = %S (camera, tab audio and screen)
getUserMedia.sharingMenuCameraAudioCaptureWindow = %S (camera, tab audio and window)
getUserMedia.sharingMenuCameraAudioCaptureBrowser = %S (camera, tab audio and tab)
getUserMedia.sharingMenuCameraApplication = %S (camera and application)
getUserMedia.sharingMenuCameraScreen = %S (camera and screen)
getUserMedia.sharingMenuCameraWindow = %S (camera and window)
getUserMedia.sharingMenuCameraBrowser = %S (camera and tab)
getUserMedia.sharingMenuMicrophoneApplication = %S (microphone and application)
getUserMedia.sharingMenuMicrophoneScreen = %S (microphone and screen)
getUserMedia.sharingMenuMicrophoneWindow = %S (microphone and window)
getUserMedia.sharingMenuMicrophoneBrowser = %S (microphone and tab)
getUserMedia.sharingMenuAudioCaptureApplication = %S (tab audio and application)
getUserMedia.sharingMenuAudioCaptureScreen = %S (tab audio and screen)
getUserMedia.sharingMenuAudioCaptureWindow = %S (tab audio and window)
getUserMedia.sharingMenuAudioCaptureBrowser = %S (tab audio and tab)

Or the use-case @cruelbob gives in #79 (comment):

Collect meat from cows, pigs and sheep.

One of my favorite games, Heroes of Might and Magic III, pits armies consisting of over 140 different unit types in battles against each other. After every move, the battle log reads:

The Bone Dragon does 46 damage. 2 Griffins perish.

Or:

The Cyclops Kings do 233 damage. One Giant perishes.

If we wanted to avoid concatenation of sentences (two sentences per creature: one for do X damage and one for X creatures perish), we'd end up with 141² = 19,881 different permutations of creature pairs.

This doesn't scale well.

Proposed Solution

Introducing some redundancy should still be preferred for small sets of items. For large sets leading to lots and lots of permutations, it should be possible to parametrize the translation of placeables.

I'll use the example of HoMM3 because the other two also require the List Formatting feature to make sense.

I'd like to make it possible to pass external arguments which resolve to message references. Given the following FTL:

-creature-bone-dragon =
    {
       *[singular] Bone Dragon
        [plural] Bone Dragons
    }
-creature-griffin =
    {
       *[singular] Griffin
        [plural] Griffins
    }

# … Hundreds more …

battle-log-attack-perish =
    { $attacker_count ->
        [one] The { $attacker_name[singular] } does
       *[other] The { $attacker_name[plural] } do
    } { $damage_points } damage. { $perish_count ->
        [one] One { $defender_name[singular] } perishes.
       *[other] { $defender_count } { $defender_name[plural] } perish.
    }

…both $attacker_name and $defender_name would be arguments of type FluentReference (extending FluentType; same as FluentNumber and FluentDateTime). The developer would pass them like so:

let msg = ctx.getMessage("battle-log-attack-perish");
log(ctx.format(msg, {
    attacker_name: new FluentReference("-creature-bone-dragon"),
    attacker_count: 1,
    defender_name: new FluentReference("-creature-griffin"),
    perish_count: 2,
    damage_points: 46
}));

This change mostly requires additions to the MessageContext resolution logic. Syntax-wise, the VariantExpression and the AttributeExpression should be changed to accept both message identifiers as well as external arguments as parent objects (like in the $attacker_name[singular] example above).

Open Questions

  1. Should we also allow public messages to be dynamically referenced like this?

Sign-offs

(toggle)
@Pike
  • I support this.
  • I don't care.
  • I object this.
@stasm
  • I support this.
  • I don't care.
  • I object this.
@zbraniecki
  • I support this.
  • I don't care.
  • I object this.

Also CC @flodolo.

@vladkolotvin
Copy link

vladkolotvin commented Jan 15, 2018

In some languages there are grammatical cases(https://en.wikipedia.org/wiki/Grammatical_case). This feature can help with this problem.
Example in russian:
Nominative case - У меня есть коровы, свиньи и овцы. (I have cows, pigs and sheep.)
Genitive case - У меня нет коров, свиней и овец. (I have no cows, pigs and sheep.)

@stasm
Copy link
Contributor Author

stasm commented Jan 16, 2018

Grammatical cases are already well-supported by Fluent; see http://projectfluent.org/fluent/guide/variants.html. But you're right—these two features will synergize well :)

@Pike
Copy link
Contributor

Pike commented Jan 23, 2018

I think we should do this, the use-cases look good enough. Localizers' life will be hard in these cases, but less hard than with the alternative.

@stasm
Copy link
Contributor Author

stasm commented Jan 23, 2018

Thanks, @Pike. A few more examples which illustrate why it's useful to resolve the references on the localization side rather than in the code (and pass translated strings as arguments).

Let's assume a game UI which logs what the player sees:

You see a fairy.
You see an elf.

English Localization

-creature-fairy = fairy
-creature-elf = elf
    .StartsWith = vowel

you-see =
    You see { $object.StartsWith ->
        [vowel] an { $object }
       *[consonant] a { $object }
    }.

The you-see message can inspect the English-specific StartsWith attribute and choose between the correct indefinite article a or an. For conciseness, -create-fairy doesn't define the StartsWith attribute at all; the default variant in you-see will be used.

German Localization

-creature-fairy = Fee
    .Genus = Femininum
-creature-elf =
    {
       *[Nominativ] Elf
        [Akkusativ] Elfen
    }
    .Genus = Maskulinum

you-see =
    Du siehst { $object.Genus->
       *[Maskulinum] einen { $object[Akkusativ] }
        [Femininum] eine { $object[Akkusativ] }
        [Neutrum] ein { $object[Akkusativ] }
    }.

The you-see message can inspect the German-specific Genus attribute and choose between the correct indefinite article for the gender of $object. The object is also correctly accorded with the verb sehen which requires the Akkusativ.


PS. The examples above don't solve capitalization (fairy and elf are always lowercase in the English translation), but I'm leaving it out on purpose. It may be solved by nested variants or a function.

@zbraniecki
Copy link
Collaborator

zbraniecki commented Mar 8, 2018

While working on https://bugzilla.mozilla.org/show_bug.cgi?id=1435915 I found a use case for this feature.

There's an API there which constructs a description of the application handler.

It can be a localizable term, like "Portable Document Format (PDF)" or "Video Podcast", it can be a generic description like { $extension } file, or it can be a raw string.

I handle all three scenarios using a strategy from Gaia days - the API circulates an "l10n type" object:

// a string -> l10nId
// an object -> {id: l10nId, args: l10nArgs}
// an object -> {raw: string}

{id: "applications-type-video-podcast-feed"},
{id: "applications-file-ending", args: {extension: ".mp4"}},
{raw: "Windows Video File"}, // this one comes straight from the OS

Those strings are resolved in a loop and displayed in a table in Firefox Preferences in a column "type description".

Now, the trick is that there's a place in the API which separates how this string is displayed in case there are two entries with the same description.

This can happen because for example, there are two file types for "Video Podcast" or "Windows Video File".

In that case, there's a special string in Fluent:

applications-type-description-with-type = { $description } ({ $type })

which is used to display Windows Video File (.mp4) separately from Windows Video File (.mpg).

With support for this UI I could use the FluentReference as $description instead of having to resolve the string with formatValue and pass it as a string.

I'm going to workaround it for now, but just thought it may be useful to know that we already encountered a use case in Firefox.

@zbraniecki
Copy link
Collaborator

This would also help with cases where a message value is used as an attribute in another element.

Example 1: https://searchfox.org/mozilla-central/rev/588d8120aa11738657da93e09a03378bcd1ba8ec/browser/locales/en-US/browser/preferences/preferences.ftl#35

could be:

pane-general-title = General
pane-search-title = Search

category =
    .tooltiptext = { $paneTitle }

Example2:

the applications-type-description are used as values, but then placed into the XUL as <item typeDescription="..."/>. It would be useful to make it:

item-type-description =
    .typeDescription = { $typeDescription }

Granted, I don't know how will we store it in data-l10n-args.

@stasm stasm added the syntax label Mar 26, 2018
@zbraniecki
Copy link
Collaborator

Aaand another use case:

language-pl = Polish
language-fr = French

region-pl = Poland
region-us = United States

locale-pattern = { $language } ($region})

@stasm stasm modified the milestones: Syntax FUTURE, Syntax 0.8 Apr 4, 2018
@stasm
Copy link
Contributor Author

stasm commented Apr 10, 2018

From https://bugzilla.mozilla.org/show_bug.cgi?id=1451450#c6:

We'll need to support VariantExpressions and AttributeExpressions on both MessageReferences and ExternalArguments:

-term[varname]
$object[varname]

-term.attr
$object.attr

Which I think is best solved by adding another level of nesting to the AST, unfortunately. Right now, -term[varname] parses as:

{
    "type": "VariantExpression",
    "id": {
        "type": "Identifier",
        "name": "-term"
    },
    "key": {
        "type": "VariantName",
        "name": "name"
    }
}

In order to support both MessageReferences and ExternalArguments and to be able to serialize them, I think it should rather parse as:

{
    "type": "VariantExpression",
    "of": {
        "type": "MessageReference",
        "id": {
            "type": "Identifier",
            "name": "-term"
        }
    },
    "key": {
        "type": "VariantName",
        "name": "name"
    }
}

This is best visualized with the spans of $object[varname]:

$object[varname]

 +----+          Identifier
+-----+          ExternalArgument
        +------+ VariantName
+--------------+ VariantExpression

@spookylukey
Copy link
Contributor

spookylukey commented Apr 11, 2018

In Django we have a use case for this feature, not just as a matter of convenience - without it we wouldn't be able to generate correct translations at all. We have exactly the "Delete the selected %s items" case, but in our case, as a framework, %s is the name of a model, provided usually in English by a developer. It's therefore not known to the Django authors, but is known to the app developers. If we are translating into French, for example, the word "selected" becomes either " "sélectionnés" or "sélectionnées" depending on the gender of the substituted model name.

We would also want some way for FluentReference to provide a fallback - what happens if e.g. the application code passes in FluentReference("-creature-a-new-creature") but -creature-a-new-creature doesn't exist at all in the FTL file? We'd want to pass in FluentReference("-creature-a-new-creature", "a new creature"), and "a new creature" would be used for all variants if -creature-a-new-creature is not defined at all.

(Whether Django, with its current investment in gettext, would be able to move to fluent is another matter, but the point applies to other framework-like code, and the choices of frameworks can affect the choices of a lot of other things).

@stasm stasm removed this from the Syntax 0.8 milestone May 23, 2018
@zbraniecki
Copy link
Collaborator

I think I'm stuck with https://bugzilla.mozilla.org/show_bug.cgi?id=1435915#c15 until this lands.

@stasm
Copy link
Contributor Author

stasm commented May 25, 2018

Some more explanation would help :) Do you mean something like the following?

applications-action-always-ask =
    .label = Always ask
applications-action-generic-label = {$menuitem.label}

And then in JS:

setAttributes(
    labelElement,
    "applications-action-generic-label",
    {
        menuitem: new FluentReference(menuitemElement.getAttribute("data-l10n-id")),
    }
);

It's still an open question for me whether we should allow dynamic reference to messages. In fact, I'd prefer to start by allowing dynamic references to terms only. I have concerns about dynamic reference being abused in scenarios where they're not about grammar. In bug 1435915 comment 16 I suggested a slightly more verbose alternative which will fix the problem outline in the bug. IIUC, the real fix would be to encapsulate the variable shape of the translation with a WebComponent.

@stasm stasm added the FUTURE Ideas and requests to consider after Fluent 1.0 label May 25, 2018
@stasm stasm removed syntax labels Oct 16, 2018
@spookylukey
Copy link
Contributor

I'm starting to think that the energy that needs to go into maintaining this would often be greater than the energy of just creating 200 strings with a script. Conceptually doing Term references in your source language, but not in the actual translation process.

For some use cases - like the ones I mentioned for Django, which will apply to other frameworks - this solution is simply not an option, because we don't know the strings ahead of time, they are supplied by other developers. We'd be left with the kind of 'solution' that @zbraniecki has, which leaves you with broken translations for many uses cases (inability to deal with case/gender agreement etc.).

@Pike
Copy link
Contributor

Pike commented Jan 24, 2020

So, Zibi's example is actually interesting in two ways:

Firstly, it emulates message references. And message references are easy, and also kinda pointless as they're completely atomic.

Secondly, it adds fallback for missing message references. In his code example, messages are resolved on the Localization abstraction instead of the Bundle. Which solves a lot of problems we have with message references right now. Even just static ones. I'd love to discuss how message references work as part of the resolver standardization. But I'm also realistic about not getting a fully sync and fully async resolver implemented for all impls that want both. Neither of js, python, or rust have generic sync/async programming, right?

To Luke's comment: Terms are effectively language-dependent APIs. Messages referencing terms need to know the API, and all terms for that use need to implement the same API. With static term references, that's already nasty. With dynamic term references, it's an order of magnitude worse.

And when you talk about different software packages ... .

Say, the German team of the django localizers decides to change the Term API for contrib.admin. Now, all generic apps with models need to update their l10n, and all custom templates that use model names need to update. And at best, you get release notes to communicate that. I guess.

Also, to clarify, I'm just saying that Mozilla isn't the right org to drive this. That doesn't mean that we shouldn't build the Fluent ecosystem such that someone else can give this a shot. Their task is going to be to figure out these things, beyond writing down APIs and syntax.

@zbraniecki
Copy link
Collaborator

Are you saying that for a scenario like #80 (comment) we should generate lang x region combinations into an FTL file for Mozilla needs?

@Pike
Copy link
Contributor

Pike commented Jan 24, 2020

I would just go for computed values and retranslations.

@alabamenhu
Copy link
Contributor

It's true that allowing dynamic references would put more work on the translator. But that's the eternal balance that must be played: more work for the developer to create hundreds of nearly identical strings but for swapping out a word or two, or more work for the translator to understand the tech side.

But I think the reality is that most strings are fairly basic, and only a handful would require the level of detail that would make a localizer need to look up some syntax. But that's already somewhat expected, after all, given how Fluent is designed to help us move away from "Number of files: X" to "No files" but "1 file" or "2 files", etc. — that work befalls the translator. Rare would be an application that needs extremely complex logic requiring dynamic message references, but better to make it possible than preclude it entirely.

I can completely understand Mozilla not wanting to be the driving force if it doesn't have an internal use case (althpugh it sounds like it does), but defining a standard syntax and providing a baseline (even if suboptimal) implementation would do well to further the adoption outside of Mozilla and prevent splintering of the format.

@zbraniecki
Copy link
Collaborator

Another example of where this would be helpful in Firefox - https://bugzilla.mozilla.org/show_bug.cgi?id=1642725

Without that, formatValue is required to format the message before passing the result as an argument to setAttributes.
The issue with that is three fold:

  • It is a papercut, easy to make mistake in, and requiring more boilerplate code
  • It runs the risk of inner message resolving in one locale, and outer in a fallback or reverse.
  • It prevents a single l10n cycle for the document, because each formatting will run in a separate microtask.

The last is an example of the first in this case - the developer causes all setAttributes to be called after awaiten formatValues is resolved which pushes back the l10n microtask in which they all get translated significantly, just to resolve the microtask with formatValues.

Having Dynamic References would make this code clean, intentional, and easy to optimize the DOM bindings around.

@zbraniecki
Copy link
Collaborator

Another potential case - https://phabricator.services.mozilla.com/D80944

In this case, we need to evaluate how the brand name of the product affect the structure of the sentence. It may not be possible to easily place the brand in nominative form, or it may be that we'll have to denote that the argument is in nominative form and ask localizers to adapt the sentence to it.

A scenario I imagine might be the most flexible is:

For locales where the sentence doesn't depend on any aspect of the variable, use dynamic references:

browser-ie = Internet Explorer
browser-chrome = Google Chrome
browser-safari = Safari

autocomplete-import-logins = Import your login from { MESSAGES($browserMessageId) }

For locales where it does,

autocomplete-import-logins = { $browserCode ->
    [chrome]  Import your login from Chrome
  *[other] Import your login from { MESSAGES($browserMessageId) }

This would allow localizers to adapt sentences which they need and leave the generic form (potentially imperfect) as a other fallback.

@stasm
Copy link
Contributor Author

stasm commented Jun 25, 2020

Another potential case - https://phabricator.services.mozilla.com/D80944

The solution this patch settled on is also what I think is the best localization practice for a small number of variants:

autocomplete-import-logins-from-chrome = Import your login from Google Chrome
autocomplete-import-logins-from-ie = Import your login from Internet Explorer
autocomplete-import-logins-from-safari = Import your login from Safari
# etc.

This has the best chance of producing translation of good quality. The localizers have full context in each string, and are also free to introduce any changes to spelling, declension, and others, as they see fit, because each string is independent.

@alerque
Copy link
Contributor

alerque commented Jun 25, 2020

The solution this patch settled on is also what I think is the best localization practice

If that's the best practice, how is Fluent better for this use case than a YAML file with key/value pairs? Sure breaking out every possible variation into a separate key gives you absolute control, but then the burden of translators goes way up (and translation editing tooling gets tasked with trying to lighten the load through suggestions from similar strings etc. which turns into a mess when you start updating anything).

@stasm
Copy link
Contributor Author

stasm commented Jun 25, 2020

That's a good question, thanks! I think the notion of the localizer's control is key. A simple key/value pair store takes away this control when we consider plurals, genders, or some forms of declensions. If the source language (often: English) doesn't support a grammatical feature required by the target language, the possibility of creating a well-sounding translation is limited.

In the Import your login from… example, however, the reason for the variation is not language-specific: the list of supported browsers is known ahead of time and constant across languages. In this case, I think separate messages offer the most control to localizers, again. If a language requires declension or a different article of some browser names, the localizer can modify the relevant string inline. Does that answer your question?

@m-aciek
Copy link

m-aciek commented Aug 12, 2021

Hi, another use case example. It comes from Django. The snippet provides correct Polish translations to sentences consisting of model objects' count and the model's name, like "1 user" or "5 groups" in English. In Polish one of grammatical genders – "masculine personal" (męskoosobowy) – is an exception from others, and requires genitive instead of nominative in plural form for one of the plural categories.

Polish localization

auth.ftl

-user = użytkownik
    .gender = masculine personal
-users = {$case -> 
   *[nominative] użytkownicy
    [genitive] użytkowników
}
-group = group
    .gender = feminine
-groups = {$case ->
   *[nominative] grupy
    [genitive] grup
}

admin.ftl

number-of-model-objects = {$name.gender ->
    [masculine personal] {$count ->
        [one] { $count } { $name }
        [few] { $count } { $name-plural(case: "genitive") }
       *[many] { $count } { $name-plural(case: "genitive") }
    }
   *[other] { $count ->
        [one] { $count } { $name }
        [few] { $count } { $name-plural }
       *[many] { $count } { $name-plural(case: "genitive") }
    }
}
main.py
l10n = FluentLocalization(["pl"], ["admin.ftl", "auth.ftl"], loader)
for number in (0, 1, 2, 5, 22):
    print(
        l10n.format_value(
            "number-of-model-objects",
            {'count': number, 'name': FluentReference('-user'), 'name-plural': FluentReference('-users')},
        )
    )
for number in (0, 1, 2, 5, 22):
    print(
        l10n.format_value(
            "number-of-model-objects",
            {'count': number, 'name': FluentReference('-group'), 'name-plural': FluentReference('-groups')},
        )
    )
standard output
0 użytkowników
1 użytkownik
2 użytkowników
5 użytkowników
22 użytkowników
0 grup
1 grupa
2 grupy
5 grup
22 grupy
English (for comparison)

auth.ftl

-user = user
-users = users
-group = group
-groups = groups

admin.ftl

number-of-model-objects = {$count ->
    [one] { $count } { $name }
   *[other] { $count } { $name-plural }
}

main.py

l10n = FluentLocalization(["en"], ["admin.ftl", "auth.ftl"], loader)
for number in (0, 1, 2, 5, 22):
    print(
        l10n.format_value(
            "number-of-model-objects",
            {'count': number, 'name': FluentReference('-user'), 'name-plural': FluentReference('-users')},
        )
    )
for number in (0, 1, 2, 5, 22):
    print(
        l10n.format_value(
            "number-of-model-objects",
            {'count': number, 'name': FluentReference('-group'), 'name-plural': FluentReference('-groups')},
        )
    )

standard output

0 users
1 user
2 users
5 users
22 users
0 groups
1 group
2 groups
5 groups
22 groups

@alerque
Copy link
Contributor

alerque commented Aug 12, 2021

Just a heads up, Linguist has Fluent support now, so you can mark your code blocks for highlighting:

```fluent
example = foo
```

SirNickolas added a commit to SirNickolas/SublimeFluent that referenced this issue Mar 25, 2022
These are experimental, not yet standardized constructs. The syntax for
dynamic term references (-$term-var) was taken as chosen by an unofficial
Perl 6 Fluent implementation.

projectfluent/fluent#130
projectfluent/fluent#80 (comment)
@Ygg01
Copy link

Ygg01 commented Feb 12, 2023

So any news on implementing this? Or should we just implement our own?

@eemeli
Copy link
Member

eemeli commented Mar 11, 2023

This is not currently being worked on. In large part progress here is blocked due to the uncertainty of how or whether MessageFormat 2 will be able to support dynamic message references, and not wanting to introduce new Fluent features that may be challenging to make compatible with MF2.

@Shelim
Copy link

Shelim commented Apr 13, 2023

@eemeli Even as optional feature, to be able to be only explicitly enabled in given project (with red-alert-style warnings that it may break compatibility with MF2)? It is a highly no-go for dynamic content (as stated above - I am actually investigating Fluent for a game and the Heroes III example is my exact deal-breaker with numbers. Paying translator to translate 10,000 message variants is an awful idea to translate a single line...)

@Shelim
Copy link

Shelim commented Apr 13, 2023

Status update: I was actually able to workaround my way into this. Relevant playground [Polish, following Heroes III example]: https://projectfluent.org/play/?id=dbf872642497ea2b98efe2afa7585dc1

Steps:

  1. Get the FluentMessage of the target creature ( for example "dragons")
  2. Create new Attribute collection and include translation of the target FluentMessage ("killed-by" -> "dragons")
  3. Iterate all attributes on the received message and add them to attributes (example attribute "gender" will produce attribute "killed-by-gender" -> "male")
  4. Get the target FluentMessage for being killed by with the populated attributes collection

It is actually fairly easy to include this in application-side code and is obvious enough for the translators to keep this as viable solution

@zbraniecki
Copy link
Collaborator

The downside of your approach, which may not be really that relevant for your use case, is that you resolve your first message and second separately. It means that any locale change requires both calls to be re-run for the new locale, which is quirky (at least in DOM scenario).

Main value of dynamic references is that it folds this sequence into a single API call between L10n system and the caller securing locale consistency.

It's a bit analogous to as if instead of having You have { $count } emails have the number resolved in MF2, we'd ask developers to format the number, and pass formatted number as a string to MF2 keeping track of locale alignment between the two. We could, but it'd clearly be a bad architecture.

@Shelim
Copy link

Shelim commented Apr 13, 2023

@zbraniecki This is why I call this a "workaround" 😄 But since it is obviously hitting a wall for a proper implementation (and there is no other good quality alternative) I will take this one. It suit my needs good enough and I do not use DOM at all in desired game.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FUTURE Ideas and requests to consider after Fluent 1.0 syntax
Projects
None yet
Development

No branches or pull requests