viur-framework / viur-core

The core component of ViUR, the Python framework for modern web development.
https://www.viur.dev
MIT License
13 stars 14 forks source link

Rethink i18n + i10n #1338

Open sveneberth opened 2 days ago

sveneberth commented 2 days ago

We have this current.language property together with an i18n module and some more containers, but their implementation lacks a consistent and well thought-out concept. As always with ViUR, unfortunately.

Here is an overview of the components that make use of it:

Component Uses AliasMap Always resolve and enforce Alias?
Router._select_language Yes (Allows to store alias) -
Cache No -
i18n.LanguageWrapper Yes Yes
i18n.translate Yes No
StringBone.buildDBFilter fallback No -
HtmlRender.getTemplateFileName No -
ViRender.setLanguage No (only available_languages are valid) -
utils.seoUrlToEntry No -
utils.seoUrlToFunction No -
JinjaFunction getLanguage Optional -
Module.seo_language_map (SEO alias of module) No (only available_languages are used) -

As you can see, it is purely arbitrary whether a method always resolves the aliases to the language, selects the alias first and only selects the language as a fallback or only uses the main languages. This means that the current.language constantly changes its meaning between language and alias. How are you supposed to generate consistent output with this?

But what is exactly the purpose of this alias? Is it supposed to represent a region, as you would do with locales? But that doesn't work as soon as a region has several languages.

For example, Belgium has three official languages: Dutch, French, and German. Or Canada: French and English.

If this alias map is a dict str -> str, it cannot be mapped. The keys would overwrite each other.

conf.i18n.language_alias_map = {"en", "de", "nl", "fr"}
conf.i18n.language_alias_map = {
  "be": "nl",
  "be": "fr",
  "be": "de",
  "ca": "fr",
  "ca": "en",
}

How could we solve this in the future?

Instead of a language, we use a locales. Consisting of the ISO 639 two-letter language code e.g. de, and DE as ISO 3166 two-letter country code. Resulting in: de-DE.

Then it must be possible in any bones, translations, etc. to store both:

If the user has now stored the locale fr-BE in his session, the method first tries to load the value for fr-BE, if this does not exist (or is empty), it falls back to the language "fr". In the worst case, of course, to a fallback language.