Skip to content
This repository has been archived by the owner on Mar 15, 2024. It is now read-only.

Commit

Permalink
Refactor URL prefix rewriting
Browse files Browse the repository at this point in the history
  • Loading branch information
solarissmoke committed Aug 23, 2019
1 parent 5a7f0e0 commit d3bfd00
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 212 deletions.
56 changes: 56 additions & 0 deletions donate/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import functools

import django
from django.utils.translation.trans_real import accept_language_re


# WARNING: this is not necessarily a good idea, but is the only way to override
# Django's default behaviour of requiring language codes to be lowercased.
# We have to modify the core Django method, because we have no way to replace
# all the core functionality that relies on this - e.g., url resolvers that
# the Django admin and third party apps use.
# This is pretty ugly, and ideally this should be fixed upstream.


def language_code_to_iso_3166(language):
"""Turn a language name (en-us) into an ISO 3166 format (en-US)."""
language, _, country = language.lower().partition('-')
if country:
return language + '-' + country.upper()
return language


def to_language(locale):
"""Turn a locale name (en_US) into a language name (en-US)."""
return locale.replace('_', '-')


@functools.lru_cache(maxsize=1000)
def parse_accept_lang_header(lang_string):
"""
Parse the lang_string, which is the body of an HTTP Accept-Language
header, and return a tuple of (lang, q-value), ordered by 'q' values.
Return an empty tuple if there are any format errors in lang_string.
"""
result = []
pieces = accept_language_re.split(lang_string.lower())
if pieces[-1]:
return ()
for i in range(0, len(pieces) - 1, 3):
first, lang, priority = pieces[i:i + 3]
if first:
return ()
if priority:
priority = float(priority)
else:
priority = 1.0
result.append((language_code_to_iso_3166(lang), priority))
result.sort(key=lambda k: k[1], reverse=True)
return tuple(result)


# Replace some functions in django.utils.translation.trans_real with our own
# versions that support a language in the form en-US instead of en-us.
django.utils.translation.trans_real.to_language = to_language
django.utils.translation.trans_real.parse_accept_lang_header = parse_accept_lang_header
63 changes: 0 additions & 63 deletions donate/core/middleware.py

This file was deleted.

21 changes: 0 additions & 21 deletions donate/core/tests/test_middleware.py

This file was deleted.

2 changes: 1 addition & 1 deletion donate/core/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_get_initial_currency_uses_currency_arg(self):

def test_get_initial_currency_uses_locale(self):
request = RequestFactory().get('/')
request.LANGUAGE_CODE = 'es-mx'
request.LANGUAGE_CODE = 'es-MX'
self.assertEqual(
DonationPage().get_initial_currency(request),
'mxn'
Expand Down
55 changes: 35 additions & 20 deletions donate/core/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from django.test import RequestFactory, TestCase
from django.utils import translation
from django.utils.translation.trans_real import (
to_language as django_to_language,
parse_accept_lang_header as django_parse_accept_lang_header
)
from django.test import TestCase
from django.urls import reverse

from ..utils import ISO3166LocalePrefixPattern, get_language_from_request, language_code_to_iso_3166
from .. import language_code_to_iso_3166, parse_accept_lang_header, to_language


class UtilsTestCase(TestCase):
Expand All @@ -11,22 +16,32 @@ def test_get_language_code_to_iso_3166(self):
self.assertEqual(language_code_to_iso_3166('en-us'), 'en-US')
self.assertEqual(language_code_to_iso_3166('fr'), 'fr')

def test_ISO3166LocalePrefixPattern(self):
def test_to_language(self):
self.assertEqual(to_language('en_US'), 'en-US')

def test_parse_accept_lang_header_returns_iso_3166_language(self):
self.assertEqual(
parse_accept_lang_header('en-GB,en;q=0.5'),
(('en-GB', 1.0), ('en', 0.5)),
)


class UtilsIntegrationTestCase(TestCase):

"""
Test that our overrides to Django translation functions work.
"""
def test_to_language(self):
self.assertEqual(django_to_language('en_US'), 'en-US')

def test_parse_accept_lang_header_returns_iso_3166_language(self):
self.assertEqual(
django_parse_accept_lang_header('en-GB,en;q=0.5'),
(('en-GB', 1.0), ('en', 0.5)),
)

def test_reverse_produces_correct_url_prefix(self):
translation.activate('en-GB')
url = reverse('payments:completed')
self.assertTrue(url.startswith('/en-GB/'))
translation.deactivate()
pattern = ISO3166LocalePrefixPattern(prefix_default_language=True)
# Pattern should not match lowercase URLs
self.assertIsNone(pattern.match('en-us/'))
# It should match this URL
self.assertTrue(pattern.match('en-US/'))

def test_get_language_from_request_returns_iso_3166_language(self):
request = RequestFactory().get('/')
request.META['HTTP_ACCEPT_LANGUAGE'] = 'en-GB,en;q=0.5'
language = get_language_from_request(request)
self.assertEqual(language, 'en-GB')

def test_get_language_from_request_fallback_language(self):
request = RequestFactory().get('/')
request.META['HTTP_ACCEPT_LANGUAGE'] = 'en-FO,en;q=0.5'
language = get_language_from_request(request)
self.assertEqual(language, 'en-US')
81 changes: 0 additions & 81 deletions donate/core/utils.py

This file was deleted.

46 changes: 23 additions & 23 deletions donate/payments/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -1006,26 +1006,26 @@
'de': 'eur',
'dsb': 'eur',
'el': 'eur',
'en-au': 'aud',
'en-ca': 'cad',
'en-gb': 'gbp',
'en-in': 'inr',
'en-nz': 'nzd',
'en-us': 'usd',
'en-za': 'zar',
'en-AU': 'aud',
'en-CA': 'cad',
'en-GB': 'gbp',
'en-IN': 'inr',
'en-NZ': 'nzd',
'en-US': 'usd',
'en-ZA': 'zar',
'es': 'eur',
'es-ar': 'ars',
'es-cl': 'clp',
'es-el': 'eur',
'es-mx': 'mxn',
'es-xl': 'mxn',
'es-AR': 'ars',
'es-CL': 'clp',
'es-EL': 'eur',
'es-MX': 'mxn',
'es-XL': 'mxn',
'et': 'eur',
'fi': 'eur',
'fr': 'eur',
'fy-nl': 'eur',
'gu-in': 'inr',
'fy-NL': 'eur',
'gu-IN': 'inr',
'he': 'ils',
'hi-in': 'inr',
'hi-IN': 'inr',
'hr': 'hrk',
'hsb': 'eur',
'hu': 'huf',
Expand All @@ -1040,24 +1040,24 @@
'ml': 'inr',
'mr': 'inr',
'ms': 'myr',
'nb-no': 'nok',
'nb-NO': 'nok',
'nl': 'eur',
'nn-no': 'nok',
'pa-in': 'inr',
'nn-NO': 'nok',
'pa-IN': 'inr',
'pl': 'pln',
'pt-br': 'brl',
'pt-pt': 'eur',
'pt-BR': 'brl',
'pt-PT': 'eur',
'ro': 'ron',
'ru': 'rub',
'sk': 'eur',
'sl': 'eur',
'sq': 'all',
'sv-se': 'sek',
'sv-SE': 'sek',
'ta': 'inr',
'te': 'inr',
'th': 'thb',
'tr': 'try',
'uk': 'uah',
'zh-cn': 'cny',
'zh-tw': 'twd'
'zh-CN': 'cny',
'zh-TW': 'twd'
}
2 changes: 1 addition & 1 deletion donate/payments/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_freeze_transaction_details_stringifies_decimal_amount(self):
})

def test_get_default_currency_matches_exact_locale(self):
self.assertEqual(get_default_currency('nb-no'), 'nok')
self.assertEqual(get_default_currency('nb-NO'), 'nok')

def test_get_default_currency_falls_back_to_base_language(self):
self.assertEqual(get_default_currency('es-GG'), 'eur')
Expand Down
2 changes: 1 addition & 1 deletion donate/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'donate.core.middleware.LocaleMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
Expand Down
Loading

0 comments on commit d3bfd00

Please sign in to comment.