Opened 10 years ago

Closed 3 years ago

Last modified 3 years ago

#23689 closed Bug (fixed)

Django detects HTTP Accept-Language header in case-sensitive manner

Reported by: wayneye Owned by: Zainab Amir
Component: Internationalization Version: 4.0
Severity: Normal Keywords:
Cc: Claude Paroz, Daniel Samuels Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

This issue was originally discussed in django-developers: https://groups.google.com/forum/#!topic/django-developers/1Y9LZSAOSnE

Per w3c, rfc2616 and bcp47, Language tags should be parsed in case-insensitive, however, I noticed that Django detects HTTP Accept-Language headers in case-sensitive manner.

For example, the following headers:

Chrome:   Accept-Language:  zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4
Firefox:  Accept-Language:  zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3

Django will correctly display Traditional Chinese for Chrome, but won't for Firefox because of lower-cased TW.

The fix contains two parts:

  1. Fix potential case-sensitive places in code to follow case-insensitive (for example parse_accept_lang_header())
  2. Fix documentation, correct the sentence "Browsers send the names of the languages they accept in the Accept-Language HTTP header using this format. Examples: it, de-at, es, pt-br. Both the language and the country parts are in lower case. ", which obviously incorrect, Chrome uses tags like zh-TW, pt-BR.

Change History (17)

comment:1 by Claude Paroz, 10 years ago

Case sensitivity should have been resolved by 2bab9d6d9ea095c4bcaeede2df576708afd46291
I have done some local tests and couldn't reproduce your issue. Having a failing test case would help.

Last edited 10 years ago by Tim Graham (previous) (diff)

comment:2 by Carl Meyer, 10 years ago

Resolution: needsinfo
Status: newclosed

Closing as needsinfo.

comment:3 by Daniel Samuels, 3 years ago

I've just hit this same problem today, here's some example code:

views.py:

class ExampleView(TemplateView):
    template_name = 'example.html'

    def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
        context = super().get_context_data(**kwargs)

        context['language_code'] = translation.get_language()
        #        ^-- should be pt-BR, but is pt
        return context

test_views.py

def test_example_view(db, client):
    language_code = 'pt-BR'
    resp = client.get(reverse('example'), HTTP_ACCEPT_LANGUAGE=language_code)
    assert resp.context_data['language_code'] == language_code
    # ^-- AssertionError: pt-BR != pt

The code path that's going wrong is:

  • LocaleMiddleware.process_request calls translation.get_language_from_request
  • get_language_from_request calls parse_accept_lang_header which turns pt-BR into pt-br
  • get_language_from_request then calls get_supported_language_variant, passing pt-br as the lang_code
  • get_supported_language_variant then runs if code in supported_lang_codes, which is False (note that 'pt-BR' in supported_lang_codes == True)
  • get_supported_language_variant then returns the fallback lang_code pt
Last edited 3 years ago by Daniel Samuels (previous) (diff)

comment:4 by Daniel Samuels, 3 years ago

Resolution: needsinfo
Status: closednew

comment:5 by Daniel Samuels, 3 years ago

I've created a simple reproduction using the above example code as a basis. You can find it here: https://github.com/danielsamuels/django_23689

comment:6 by Zainab Amir, 3 years ago

Owner: changed from nobody to Zainab Amir
Status: newassigned

comment:7 by Mariusz Felisiak, 3 years ago

Cc: Claude Paroz added
Easy pickings: unset

comment:8 by Claude Paroz, 3 years ago

Triage Stage: UnreviewedAccepted

Thanks for the test project!

comment:9 by Zainab Amir, 3 years ago

Created a fix for it on my branch: https://github.com/django/django/compare/main...zainab-amir:ticket_23689
I will create a PR on django if no one has any suggestions or comments.

  1. The documentation already mentions that the header should be case sensitive, so I fixed that
  2. Also noticed that running django server with these settings throw an error as mentioned below. This is also fixed.
LANGUAGES = (
    ('EN-US', 'English (US)'),
    ('De', 'Deutsche'),
    ('ar', 'عربى'),
)

LANGUAGE_CODE = 'en-us'

ERROR:

ERRORS:
?: (translation.E004) You have provided a value for the LANGUAGE_CODE setting that is not in the LANGUAGES setting.

comment:10 by Zainab Amir, 3 years ago

Version: 1.74.0

comment:11 by Mariusz Felisiak, 3 years ago

I will create a PR on django if no one has any suggestions or comments.

You don't need to wait for an approval. PR is the right place for suggestions and comments.

Also noticed that running django server with these settings throw an error as mentioned below. This is also fixed.

I don't see anything to fix here 🤔

in reply to:  11 comment:12 by Zainab Amir, 3 years ago

Replying to Mariusz Felisiak:

Also noticed that running django server with these settings throw an error as mentioned below. This is also fixed.

I don't see anything to fix here 🤔

This is the ripple effect of fixing the header parsing to be case insensitive.

comment:13 by Daniel Samuels, 3 years ago

Cc: Daniel Samuels added

comment:14 by Zainab Amir, 3 years ago

Has patch: set

comment:15 by Mariusz Felisiak, 3 years ago

Triage Stage: AcceptedReady for checkin

comment:16 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

Resolution: fixed
Status: assignedclosed

In 901a1691:

Fixed #23689 -- Made parsing HTTP Accept-Language header case-insensitive.

Thank you Daniel Samuels for test project.

comment:17 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

In f741dd5:

[4.1.x] Fixed #23689 -- Made parsing HTTP Accept-Language header case-insensitive.

Thank you Daniel Samuels for test project.

Backport of 901a1691982cab76349d33e51b72c40120ec927a from main

Note: See TracTickets for help on using tickets.
Back to Top