symfony / symfony-docs

The Symfony documentation
https://symfony.com/doc
Other
2.18k stars 5.13k forks source link

[Translate][4.2] transchoice is deprecated, new way of doing things is missing #11002

Closed mhujer closed 5 years ago

mhujer commented 5 years ago

transChoice method and transchoice filter are deprecated since 4.2, but the documentation does not show the example of the new way of doing things.

The note about the deprecation was added to documentation in https://github.com/symfony/symfony-docs/pull/10973, but the examples still show the old way (using transChoice).

The example in the blogpost (https://symfony.com/blog/new-in-symfony-4-2-intlmessageformatter) is rather complicated.

arjenm commented 5 years ago

Also, don't forget to clearly add the task to rename translation files to include "+intl-icu".

It seems the deprecation message is additionally unclear since it requires a lot more than simply replacing the transchoice-call with a trans-call:

mhujer commented 5 years ago

@arjenm Did you manage to get the new approach working? I gave up after a while :-(

arjenm commented 5 years ago

Yes we did :)

But it was indeed a lot of trial and error and using resources that turned out to be linked (via via via) in that blog post (and were much more helpful) :(

In your templates this is roughly what had to change:

Before:
{{ 'some.number_of_items'|transchoice(numberOfItems, {'%count%': numberOfItems}) }}
After:
{{ 'some.number_of_items'|trans({'count': numberOfItems}) }}

And in the translations, its a bit more complex:

Rename the files from something like messages.en.yml to messages+intl-icu.en.yml (or whatever extension you use) If you rename it though, no old style transchoice in that file will work, since the new translator simply does not understand it (which is also not documented!).

And change the contents:

# Before
some:
  number_of_items: "{0} No items found.|{1} One item found.|]1,Inf[ %count% items found."

# After
some:
  number_of_items: "{count, plural,
        =0 {No items found.}
        one {One item found.}
        other {# items found.}
      }"
# Or without newlines and/or nested inside some sentence:     
some:
  number_of_items: "{count, plural, =0 {No items found.} one {One item found.} other {# items found.}}"
  nested_inside_text: "some text before {count, plural, =0 {No items found.} one {One item found.} other {# items found.}} and after""

Note the 'one' and 'other' are special keys that indicate certain groups of numbers depending on the locale. There can be different groups per locale (i.e. some have one, two, few, many, other), although as far as I understood it, other is always the "fallback". Since only a few locales have a specific 'zero' class, you can also explicitly specify 0 (with that =0).

For more allowed formats and more information, these resources turned out to be quite helpful: https://messageformat.github.io/messageformat/page-guide http://userguide.icu-project.org/formatparse/messages http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html

In the end the new variant is actually more powerful. As the blog post shows, you can combine and nest a mixture of plurals and 'selects'. I.e. the male/female/other example from the blog post.

We also converted a use case for that, to test how it worked: we have a listing of a mixture of different article types and wanted to have a dynamic variation in both the naming of that type and its pluralization. So now we do something like: 'listing.article_type'|trans({'articleType': article.getType(), 'count': list.length()}), where we earlier had an ugly hack with a concat like this: 'listing.article_type' ~ article.getType()|transchoice(list.length(), {'%count%': list.length()}),

Which actually turned out to be quite similar to that complex example from, the blog post :P

mhujer commented 5 years ago

@arjenm thanks for the detailed guide, I will try, if I can make it work 👍

mhujer commented 5 years ago

@arjenm Thank you again! I just removed all transchoice filters from our application and your guide really helped me in the process 👏

mhujer commented 5 years ago

Surprisingly, translation placeholders with trans method stop working when the new filename format is used. Not sure if it is an expected behaviour or not.

Translations:

app:
    allTheThings: "All the things in %localityName%"

Twig:

{{ 'app.allTheThings' | trans({
    '%localityName%': locality.getName()
}) }}
arjenm commented 5 years ago

Well, you did rename the file and thereby started using a completely different 'MessageFormatter'.

But that is something we also realized after I wrote this:

If you rename it though, no old style transchoice in that file will work, since the new translator simply does not understand it (which is also not documented!).

That statement is true for all translations with variables, including transchoice.

They need to change like so:

app:
    allTheThings: "All the things in {localityName}"

The {} marks a variable. Plural, select and others are actions upon that variable and are thus included within the {}.

Twig:

{{ 'app.allTheThings' | trans({
    'localityName': locality.getName()
}) }}

I'm not sure if {%localityName%} would be valid translation syntax, the intl-system is a bit picky with its variable naming. If that does work, the twig file doesn't need to change (but its a bit ugly to use those old style str_replace-variables syntaxes which you don't need anymore).

sstok commented 5 years ago

Sharing these here, IMO the most helpful guide to the ICU message format:

https://format-message.github.io/icu-message-format-for-translators/ https://format-message.github.io/icu-message-format-for-translators/editor.html

javiereguiluz commented 5 years ago

Fixed by #11523.