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

tornado.locale.Locale.get_closest() not getting closest match #1858

Open
Deus-Vult opened this issue Oct 3, 2016 · 3 comments
Open

tornado.locale.Locale.get_closest() not getting closest match #1858

Deus-Vult opened this issue Oct 3, 2016 · 3 comments
Labels

Comments

@Deus-Vult
Copy link

The tornado.locale.Locale.get_closest() (and as such by extension tornado.locale.get) function is a bit malformed. It tries to match two character language codes directly against the frozenset in which the five character language codes are stored, which obviously fails. As a result it can only get exact matches and will return the default locale when using two character codes.

    @classmethod
    def get_closest(cls, *locale_codes):
        """Returns the closest match for the given locale code."""
        for code in locale_codes:
            if not code:
                continue
            code = code.replace("-", "_")
            parts = code.split("_")
            if len(parts) > 2:
                continue
            elif len(parts) == 2:
                code = parts[0].lower() + "_" + parts[1].upper()
            if code in _supported_locales:
                return cls.get(code)
            if parts[0].lower() in _supported_locales:
                return cls.get(parts[0].lower())
        return cls.get(_default_locale)

Specifically this part:

            if parts[0].lower() in _supported_locales:
                return cls.get(parts[0].lower())

I wrote the following simple function in my own code to bypass this problem but I bet someone else can write it a bit nicer into the intended function:

locale = self.request.headers.get('Accept-Language')

if locale:
    for l in tornado.locale.get_supported_locales():
        if locale == l.split("_")[0]:
            self.locale = tornado.locale.get(l)
@eljeffeg
Copy link

eljeffeg commented Apr 3, 2020

Bump - not sure why this has not been fixed.
get_browser_locale() is not providing the correct closests match. If I have de_DE in my locale files and Firefox returns de,en-US;q=0.7,en;q=0.3 in the Acceptable Languages Header, it should use the de_DE locale as the closest match, but it doesn't. It defaults to English.

Currently Tornado does this:

header supported match
de_DE de_DE True
de_DE de True
de de True
de de_DE False

That last value should be True

eljeffeg added a commit to eljeffeg/tornado that referenced this issue Apr 3, 2020
This fixes the situation where the browser returns a two character locale, such as de, but the supported_locales de_DE.
@adrianrv
Copy link

It seems header locale format vs unix locale format is being mixed.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language#Directives

Examples
Accept-Language: de
Accept-Language: de-CH
Accept-Language: en-US,en;q=0.5

In the other hand, get_browser_locale inside RequestHandler class ends calling Locale.get_closest() where the dash is substituted by underscore and a system locale is returned.

code = code.replace("-", "_")
parts = code.split("_")

I would expect get_browser_locale to return the "Accept-Language" header thats it. Instead is returning a system locale code. Then another method could set the locale based on the header doing the logic to match the best locale available.

@bdarnell
Copy link
Member

It seems header locale format vs unix locale format is being mixed.

Yeah, this is some old code that makes some questionable design decisions. It's not very principled in how it handles different locale formats.

I would expect get_browser_locale to return the "Accept-Language" header thats it.

You can already do that: self.request.headers.get('Accept-Language'). The whole point of get_browser_locale is to find the most preferred Locale object from tornado.locale that matches what's in the header. If you're using some other localization system instead of tornado.locale (which is perfectly fine!), just ignore this method and go straight to the request headers yourself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants