Skip to content

Commit

Permalink
Adding middleware and tests. Removing dependency on django-localeurl.…
Browse files Browse the repository at this point in the history
… Added the big list of locales (more than we support) and the subset of our supported locales.
  • Loading branch information
James Socol committed Apr 1, 2010
1 parent fff6989 commit d03b6f8
Show file tree
Hide file tree
Showing 13 changed files with 357 additions and 23 deletions.
2 changes: 1 addition & 1 deletion apps/search/helpers.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import urllib

from django.conf import settings
from django.core.urlresolvers import reverse

import jinja2
from jingo import register
from didyoumean import DidYouMean
from flatqs import flatten

from sumo.urlresolvers import reverse

@register.function
def spellcheck(string, locale='en-US'):
Expand Down
16 changes: 9 additions & 7 deletions apps/search/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from nose.tools import eq_

from django.core.urlresolvers import reverse

import test_utils

import jingo

from django import test

from sumo.urlresolvers import reverse


def setup():
jingo.load_helpers()
test.Client().get('/')


def render(s, context={}):
Expand All @@ -30,7 +32,7 @@ def test_suggestions():
request = test_utils.RequestFactory().get(url)
w = 'worng'
t = "{{ q|suggestions('en-US') }}"
exp = '<a href="/en/search?q=wrong"><strong>wrong</strong></a>'
exp = '<a href="/en-US/search?q=wrong"><strong>wrong</strong></a>'
eq_(exp, render(t, {'q': w, 'request': request}))


Expand All @@ -40,7 +42,7 @@ def test_suggestions_page2():
request = test_utils.RequestFactory().get(url)
w = 'worng'
t = "{{ q|suggestions('en-US') }}"
exp = '<a href="/en/search?q=wrong&amp;page=1"><strong>wrong</strong></a>'
exp = '<a href="/en-US/search?q=wrong&amp;page=1"><strong>wrong</strong></a>'
eq_(exp, render(t, {'q': w, 'request': request}))


Expand All @@ -50,7 +52,7 @@ def test_suggestions_categories():
request = test_utils.RequestFactory().get(url)
w = 'worng'
t = "{{ q|suggestions('en-US') }}"
exp = '<a href="/en/search?q=wrong&amp;category=1&amp;category=2"><strong>wrong</strong></a>'
exp = '<a href="/en-US/search?q=wrong&amp;category=1&amp;category=2"><strong>wrong</strong></a>'
eq_(exp, render(t, {'q': w, 'request': request}))


Expand All @@ -60,5 +62,5 @@ def test_suggestions_highlight():
request = test_utils.RequestFactory().get(url)
q = 'right worng'
t = "{{ q|suggestions('en-US') }}"
exp = '<a href="/en/search?q=right+wrong">right <strong>wrong</strong></a>'
exp = '<a href="/en-US/search?q=right+wrong">right <strong>wrong</strong></a>'
eq_(exp, render(t, {'q': q, 'request': request}))
3 changes: 2 additions & 1 deletion apps/search/tests/test_json.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from nose.tools import eq_

from django.core.urlresolvers import reverse
from django.test.client import Client

from sumo.urlresolvers import reverse


def test_json_format():
"""JSON without callback should return application/json"""
Expand Down
15 changes: 8 additions & 7 deletions apps/search/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def search(request):
# set up query variables
q = request.GET.get('q', '')

locale = request.GET.get('locale', request.LANGUAGE_CODE)
locale = request.GET.get('locale', request.locale)
language = request.GET.get('language', locale)

search_locale = (crc32(language),)
Expand All @@ -56,16 +56,17 @@ def search(request):
# => return empty form
if (not q or (request.GET.get('a', '0') == '1')):
return jingo.render(request, 'form.html',
{'locale': request.LANGUAGE_CODE,
{'locale': request.locale,
'advanced': request.GET.get('a'),
'request': request,
'w': where, 'search_form': search_form,
})

# get language name for display in template
for (l, l_name) in settings.LANGUAGES:
if l == language:
lang_name = l_name
if settings.LANGUAGES.get(language):
lang_name = settings.LANGUAGES[language]
else:
lang_name = ''

documents = []

Expand Down Expand Up @@ -254,7 +255,7 @@ def search(request):

return jingo.render(request, 'results.html',
{'num_results': len(documents), 'results': results, 'q': q,
'locale': request.LANGUAGE_CODE, 'pages': pages,
'locale': request.locale, 'pages': pages,
'w': where, 'refine_query': refine_query,
'search_form': search_form,
'lang_name': lang_name, })
Expand All @@ -267,7 +268,7 @@ class SearchForm(forms.Form):
tag = forms.CharField(label=_('Tags'))

language = forms.ChoiceField(label=_('Language'),
choices=settings.LANGUAGES)
choices=[(i, settings.LANGUAGES[i]) for i in settings.LANGUAGES])

categories = []
for cat in Category.objects.all():
Expand Down
3 changes: 2 additions & 1 deletion apps/sumo/helpers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import cgi
import urlparse

from django.core.urlresolvers import reverse
from django.utils.datastructures import MultiValueDict

import jinja2

from jingo import register, env
from flatqs import flatten

from sumo.urlresolvers import reverse


@register.filter
def paginator(pager):
Expand Down
57 changes: 57 additions & 0 deletions apps/sumo/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Taken from zamboni.amo.middleware.
Tried to use localeurl but it choked on 'en-US' with capital letters.
"""

import urllib

from django.http import HttpResponsePermanentRedirect
from django.utils.encoding import smart_str

import l10n

from . import urlresolvers
from sumo.helpers import urlparams


class LocaleURLMiddleware(object):
"""
1. Search for the locale.
2. Save it in the request.
3. Strip them from the URL.
"""

def process_request(self, request):
prefixer = urlresolvers.Prefixer(request)
urlresolvers.set_url_prefix(prefixer)
full_path = prefixer.fix(prefixer.shortened_path)

if 'lang' in request.GET:
# Blank out the locale so that we can set a new one. Remove lang
# from the query params so we don't have an infinite loop.
prefixer.locale = ''
new_path = prefixer.fix(prefixer.shortened_path)
query = dict((smart_str(k), request.GET[k]) for k in request.GET)
query.pop('lang')
return HttpResponsePermanentRedirect(urlparams(new_path, **query))

if full_path != request.path:
query_string = request.META.get('QUERY_STRING', '')
if query_string:
full_path = '%s?%s' % (full_path, query_string)

full_path = urllib.quote(full_path.encode('utf-8'))
response = HttpResponsePermanentRedirect(full_path)

# Vary on Accept-Language if we changed the locale
old_locale = prefixer.locale
new_locale, _ = prefixer.split_path(full_path)
if old_locale != new_locale:
response['Vary'] = 'Accept-Language'

return response

request.path_info = '/' + prefixer.shortened_path
request.locale = prefixer.locale
l10n.activate(prefixer.locale)
31 changes: 31 additions & 0 deletions apps/sumo/tests/test_locale_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from django.test import Client, TestCase


class TestLocaleMiddleware(TestCase):
client = Client()

def test_default_redirect(self):
# User wants en-us, we send en-US
response = self.client.get('/search', follow=True,
HTTP_ACCEPT_LANGUAGE='en-us')
self.assertRedirects(response, '/en-US/search', status_code=301)

# User wants fr-FR, we send fr
response = self.client.get('/search', follow=True,
HTTP_ACCEPT_LANGUAGE='fr-fr')
self.assertRedirects(response, '/fr/search', status_code=301)

# User wants xx, we send en-US
response = self.client.get('/search', follow=True,
HTTP_ACCEPT_LANGUAGE='xx')
self.assertRedirects(response, '/en-US/search', status_code=301)

# User doesn't know what they want, we send en-US
response = self.client.get('/search', follow=True,
HTTP_ACCEPT_LANGUAGE='')
self.assertRedirects(response, '/en-US/search', status_code=301)

def test_specificity(self):
"""Requests for /fr-FR/search should end up on /fr/search"""
reponse = self.client.get('/fr-FR/search', follow=True)
self.assertRedirects(reponse, '/fr/search', status_code=301)
106 changes: 106 additions & 0 deletions apps/sumo/urlresolvers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from django.conf import settings
from django.core.urlresolvers import reverse as django_reverse
from django.utils.thread_support import currentThread
from django.utils.translation.trans_real import parse_accept_lang_header


# Thread-local storage for URL prefixes. Access with (get|set)_url_prefix.
_prefixes = {}


def set_url_prefix(prefix):
"""Set the ``prefix`` for the current thread."""
_prefixes[currentThread()] = prefix


def get_url_prefix():
"""Get the prefix for the current thread, or None."""
return _prefixes.get(currentThread())


def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
"""Wraps Django's reverse to prepend the correct locale."""
prefixer = get_url_prefix()

if prefixer:
prefix = prefix or '/'
url = django_reverse(viewname, urlconf, args, kwargs, prefix)
if prefixer:
return prefixer.fix(url)
else:
return url


class Prefixer(object):

def __init__(self, request):
self.request = request
split = self.split_path(request.path_info)
self.locale, self.shortened_path = split

def split_path(self, path_):
"""
Split the requested path into (locale, path).
locale will be empty if it isn't found.
"""
path = path_.lstrip('/')

# Use partitition instead of split since it always returns 3 parts
first, _, rest = path.partition('/')

if first.lower() in settings.LANGUAGES:
return first, rest
else:
supported = [x for x in settings.LANGUAGE_URL_MAP if
x.split('-')[0] ==
first.lower().split('-')[0]]
if len(supported):
return supported[0], rest
else:
return '', path

def get_language(self):
"""
Return a locale code we support on the site using the
user's Accept-Language header to determine which is best. This
mostly follows the RFCs but read bug 439568 for details.
"""
if 'lang' in self.request.GET:
lang = self.request.GET['lang'].lower()
if lang in settings.LANGUAGE_URL_MAP:
return settings.LANGUAGE_URL_MAP[lang]

if self.request.META.get('HTTP_ACCEPT_LANGUAGE'):
ranked_languages = parse_accept_lang_header(
self.request.META['HTTP_ACCEPT_LANGUAGE'])

# Do we support or remap their locale?
supported = [lang[0] for lang in ranked_languages if lang[0]
in settings.LANGUAGE_URL_MAP]

# Do we support a less specific locale? (xx-YY -> xx)
if not len(supported):
for lang in ranked_languages:
supported = [x for x in settings.LANGUAGE_URL_MAP if
lang[0].split('-', 1)[0] ==
x.split('-', 1)[0]]
if supported:
break

if len(supported):
return settings.LANGUAGE_URL_MAP[supported[0]]

return settings.LANGUAGE_CODE

def fix(self, path):
path = path.lstrip('/')
url_parts = [self.request.META['SCRIPT_NAME']]

if path.partition('/')[0] not in settings.SUPPORTED_NONLOCALES:
locale = self.locale if self.locale else self.get_language()
url_parts.append(locale)

url_parts.append(path)

return '/'.join(url_parts)
Empty file added lib/__init__.py
Empty file.
Loading

0 comments on commit d03b6f8

Please sign in to comment.