Skip to content

[RFC][Translator] Deprecate fallback catalogues and remove them in 3.0 #14380

@mpdude

Description

@mpdude

I would like to propose deprecating the concept of fallback catalogues, in particular the addFallbackCatalogue() and getFallbackCatalogue() methods, in MessageCatalogueInterface and to remove the corresponding implementations from MessageCatalogue in Symfony 3.0.

I will try to work out the details and come up with a PR once the basic idea gets approval.

Here's why.

Background

A MessageCatalogue contains a set of translations (message id => translation string mappings) for a particular locale (e.g. de_DE).

The concept of "fallback catalogues" is currently used to implement a "chain of responsibility" pattern across different MessageCatalogue instances, each on holding translations for a different locale.

That way, you can query the "root" catalogue for the translation for a given message id (the get() method) and the "best" translation is returned. This happens without exposing to the client which catalogue (locale) this translation was actually taken from. Internally, the call is forwarded to the respective fallback catalogue if a particular catalogue does not contain a message itself.

By means of the getFallbackCatalogue() method, you could also explicitly navigate across catalogues (starting from the root). The has() and defines() methods can be used to check for messages in an entire catalogue chain or single catalogue, respectively.

Reasons for removal

1 - Complete transparency doesn't work for clients anyway

The Translator class, which is the main MessageCatalogueInterface client inside Symfony, has two main methods: trans() and transChoice().

While trans() can make use of transparent retrieval of messages, transChoice() needs to be aware of a translation’s locale in order to apply pluralization rules correctly.

Thus, the transparent lookup across catalogues (with the current design) only works out in half of the primary use cases.

2 - Allow for optimizations inside Translator

Because the Translator class allows to get the MessageCatalogue instances used internally, clients were able to retrieve the primary and thus fallback catalogues from it.

Now, in Symfony 2.7 an attempt was made to optimize Translator memory usage. Smaller fallback catalogues would be cached and contain only those messages for IDs not present in the "parent" catalogues.

At least in the available implementation, clients accessing the fallback catalogues would see different sets of messages depending on whether the Translator had a warm cache or not. This is one of the reasons why this change was reverted for the time being.

If the fallback catalogues were not part of the MessageCatalogueInterface, the Translator could hold smaller MessageCatalogue instances internally and query them in fallback order. But as long as clients can reach out for them (and expect "complete") fallback catalogues, that optimization is not possible.

3 - A consistent (and optimizable) API design would be difficult to achieve

An alternative approach to this optimization would be to make fallback catalogues always contain only those messages not present in one of the "parent" catalogues. Strictly speaking, this would still be a BC break (code behaves differently right now), but could even be argued to be in line with what the API docs suggest.

However I am afraid that such an API would be very cumbersome to use (depending on catalogues being nested/wired up in a particular order and/or not changing catalogues once the chain has been established) or a little mess internally (every catalogue knowing about its ancestor chain and virtually exposing messages only if not already present upstream).

Replacement

As mentioned under 2) above, clients could simply hold a list of catalogues to query successively instead of relying on the internal fallback mechanism.

If really needed, a ChainedMessageCatalogue could possibly be provided to "decorate" the chain of responsibility pattern around individual catalogues.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions