Commit 7b09ab5e by Sarina Canelake

dark_lang: only allow released langs in accept header LOC-72, LOC-85

Only return languages we've actually released LOC-85
Perform fuzzy matching to greedily serve the best released language LOC-72
parent e4cd2982
...@@ -83,11 +83,17 @@ class DarkLangMiddleware(object): ...@@ -83,11 +83,17 @@ class DarkLangMiddleware(object):
self._clean_accept_headers(request) self._clean_accept_headers(request)
self._activate_preview_language(request) self._activate_preview_language(request)
def _is_released(self, lang_code): def _fuzzy_match(self, lang_code):
""" """Returns a fuzzy match for lang_code"""
``True`` iff one of the values in ``self.released_langs`` is a prefix of ``lang_code``. if lang_code in self.released_langs:
""" return lang_code
return any(lang_code.lower().startswith(released_lang.lower()) for released_lang in self.released_langs)
lang_prefix = lang_code.partition('-')[0]
for released_lang in self.released_langs:
released_prefix = released_lang.partition('-')[0]
if lang_prefix == released_prefix:
return released_lang
return None
def _format_accept_value(self, lang, priority=1.0): def _format_accept_value(self, lang, priority=1.0):
""" """
...@@ -104,12 +110,13 @@ class DarkLangMiddleware(object): ...@@ -104,12 +110,13 @@ class DarkLangMiddleware(object):
if accept is None or accept == '*': if accept is None or accept == '*':
return return
new_accept = ", ".join( new_accept = []
self._format_accept_value(lang, priority) for lang, priority in dark_parse_accept_lang_header(accept):
for lang, priority fuzzy_code = self._fuzzy_match(lang.lower())
in dark_parse_accept_lang_header(accept) if fuzzy_code:
if self._is_released(lang) new_accept.append(self._format_accept_value(fuzzy_code, priority))
)
new_accept = ", ".join(new_accept)
request.META['HTTP_ACCEPT_LANGUAGE'] = new_accept request.META['HTTP_ACCEPT_LANGUAGE'] = new_accept
......
...@@ -4,8 +4,10 @@ Tests of DarkLangMiddleware ...@@ -4,8 +4,10 @@ Tests of DarkLangMiddleware
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http import HttpRequest from django.http import HttpRequest
import ddt
from django.test import TestCase from django.test import TestCase
from mock import Mock from mock import Mock
import unittest
from dark_lang.middleware import DarkLangMiddleware from dark_lang.middleware import DarkLangMiddleware
from dark_lang.models import DarkLangConfig from dark_lang.models import DarkLangConfig
...@@ -23,6 +25,7 @@ def set_if_set(dct, key, value): ...@@ -23,6 +25,7 @@ def set_if_set(dct, key, value):
dct[key] = value dct[key] = value
@ddt.ddt
class DarkLangMiddlewareTests(TestCase): class DarkLangMiddlewareTests(TestCase):
""" """
Tests of DarkLangMiddleware Tests of DarkLangMiddleware
...@@ -82,6 +85,10 @@ class DarkLangMiddlewareTests(TestCase): ...@@ -82,6 +85,10 @@ class DarkLangMiddlewareTests(TestCase):
def test_wildcard_accept(self): def test_wildcard_accept(self):
self.assertAcceptEquals('*', self.process_request(accept='*')) self.assertAcceptEquals('*', self.process_request(accept='*'))
def test_malformed_accept(self):
self.assertAcceptEquals('', self.process_request(accept='xxxxxxxxxxxx'))
self.assertAcceptEquals('', self.process_request(accept='en;q=1.0, es-419:q-0.8'))
def test_released_accept(self): def test_released_accept(self):
self.assertAcceptEquals( self.assertAcceptEquals(
'rel;q=1.0', 'rel;q=1.0',
...@@ -123,14 +130,17 @@ class DarkLangMiddlewareTests(TestCase): ...@@ -123,14 +130,17 @@ class DarkLangMiddlewareTests(TestCase):
) )
def test_accept_released_territory(self): def test_accept_released_territory(self):
# We will munge 'rel-ter' to be 'rel', so the 'rel-ter'
# user will actually receive the released language 'rel'
# (Otherwise, the user will actually end up getting the server default)
self.assertAcceptEquals( self.assertAcceptEquals(
'rel-ter;q=1.0, rel;q=0.5', 'rel;q=1.0, rel;q=0.5',
self.process_request(accept='rel-ter;q=1.0, rel;q=0.5') self.process_request(accept='rel-ter;q=1.0, rel;q=0.5')
) )
def test_accept_mixed_case(self): def test_accept_mixed_case(self):
self.assertAcceptEquals( self.assertAcceptEquals(
'rel-TER;q=1.0, REL;q=0.5', 'rel;q=1.0, rel;q=0.5',
self.process_request(accept='rel-TER;q=1.0, REL;q=0.5') self.process_request(accept='rel-TER;q=1.0, REL;q=0.5')
) )
...@@ -140,11 +150,85 @@ class DarkLangMiddlewareTests(TestCase): ...@@ -140,11 +150,85 @@ class DarkLangMiddlewareTests(TestCase):
enabled=True enabled=True
).save() ).save()
# Since we have only released "rel-ter", the requested code "rel" will
# fuzzy match to "rel-ter", in addition to "rel-ter" exact matching "rel-ter"
self.assertAcceptEquals( self.assertAcceptEquals(
'rel-ter;q=1.0', 'rel-ter;q=1.0, rel-ter;q=0.5',
self.process_request(accept='rel-ter;q=1.0, rel;q=0.5') self.process_request(accept='rel-ter;q=1.0, rel;q=0.5')
) )
@ddt.data(
('es;q=1.0, pt;q=0.5', 'es-419;q=1.0'), # 'es' should get 'es-419', not English
('es-AR;q=1.0, pt;q=0.5', 'es-419;q=1.0'), # 'es-AR' should get 'es-419', not English
)
@ddt.unpack
def test_partial_match_es419(self, accept_header, expected):
# Release es-419
DarkLangConfig(
released_languages=('es-419, en'),
changed_by=self.user,
enabled=True
).save()
self.assertAcceptEquals(
expected,
self.process_request(accept=accept_header)
)
def test_partial_match_esar_es(self):
# If I release 'es', 'es-AR' should get 'es', not English
DarkLangConfig(
released_languages=('es, en'),
changed_by=self.user,
enabled=True
).save()
self.assertAcceptEquals(
'es;q=1.0',
self.process_request(accept='es-AR;q=1.0, pt;q=0.5')
)
@ddt.data(
# Test condition: If I release 'es-419, es, es-es'...
('es;q=1.0, pt;q=0.5', 'es;q=1.0'), # 1. es should get es
('es-419;q=1.0, pt;q=0.5', 'es-419;q=1.0'), # 2. es-419 should get es-419
('es-es;q=1.0, pt;q=0.5', 'es-es;q=1.0'), # 3. es-es should get es-es
)
@ddt.unpack
def test_exact_match_gets_priority(self, accept_header, expected):
# Release 'es-419, es, es-es'
DarkLangConfig(
released_languages=('es-419, es, es-es'),
changed_by=self.user,
enabled=True
).save()
self.assertAcceptEquals(
expected,
self.process_request(accept=accept_header)
)
@unittest.skip("This won't work until fallback is implemented for LA country codes. See LOC-86")
@ddt.data(
'es-AR', # Argentina
'es-PY', # Paraguay
)
def test_partial_match_es_la(self, latin_america_code):
# We need to figure out the best way to implement this. There are a ton of LA country
# codes that ought to fall back to 'es-419' rather than 'es-es'.
# http://unstats.un.org/unsd/methods/m49/m49regin.htm#americas
# If I release 'es, es-419'
# Latin American codes should get es-419
DarkLangConfig(
released_languages=('es, es-419'),
changed_by=self.user,
enabled=True
).save()
self.assertAcceptEquals(
'es-419;q=1.0',
self.process_request(accept='{};q=1.0, pt;q=0.5'.format(latin_america_code))
)
def assertSessionLangEquals(self, value, request): def assertSessionLangEquals(self, value, request):
""" """
Assert that the 'django_language' set in request.session is equal to value Assert that the 'django_language' set in request.session is equal to value
...@@ -224,6 +308,6 @@ class DarkLangMiddlewareTests(TestCase): ...@@ -224,6 +308,6 @@ class DarkLangMiddlewareTests(TestCase):
).save() ).save()
self.assertAcceptEquals( self.assertAcceptEquals(
'zh-CN;q=1.0, zh-TW;q=0.5, zh-HK;q=0.3', 'zh-cn;q=1.0, zh-tw;q=0.5, zh-hk;q=0.3',
self.process_request(accept='zh-Hans;q=1.0, zh-Hant-TW;q=0.5, zh-HK;q=0.3') self.process_request(accept='zh-Hans;q=1.0, zh-Hant-TW;q=0.5, zh-HK;q=0.3')
) )
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment