зеркало из https://github.com/mozilla/bedrock.git
This modifies how several parts of translation in bedrock work: 1. You can now provide a list of locales in `active_locales` in the template context that will be the list of available translations for the page. 2. You can now provide a list of locales in `add_active_locales` to add extra available translations for the page. 3. The `page()` and `LangFilesMixin` helpers are updated to support these new context items. 4. You can now create and use `en-US` locale-specific-templates.
This commit is contained in:
Родитель
ce96b4c0ec
Коммит
3bf8e8cca1
|
@ -18,6 +18,9 @@ urlpatterns = (
|
|||
page('eula/thunderbird-1.5', 'legal/eula/thunderbird-1.5-eula.html'),
|
||||
page('eula/thunderbird-2', 'legal/eula/thunderbird-2-eula.html'),
|
||||
page('firefox', 'legal/firefox.html'),
|
||||
# The "impressum" page is intended for Germany. Redirect to German (de) if
|
||||
# requested in any other locale. (Bug 1248393)
|
||||
page('impressum', 'legal/impressum.html', active_locales=['de']),
|
||||
|
||||
url(r'^terms/mozilla/$', LegalDocView.as_view(template_name='legal/terms/mozilla.html', legal_doc_name='Websites_ToU'),
|
||||
name='legal.terms.mozilla'),
|
||||
|
@ -44,6 +47,4 @@ urlpatterns = (
|
|||
name='legal.report-infringement'),
|
||||
|
||||
url('^fraud-report/$', views.fraud_report, name='legal.fraud-report'),
|
||||
url('^impressum/$', views.impressum, name='legal.impressum'),
|
||||
|
||||
)
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import re
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from lib import l10n_utils
|
||||
|
@ -83,13 +81,3 @@ def fraud_report(request):
|
|||
submitted = request.GET.get('submitted') == 'True'
|
||||
template_vars['form_submitted'] = submitted
|
||||
return l10n_utils.render(request, 'legal/fraud-report.html', template_vars)
|
||||
|
||||
|
||||
def impressum(request):
|
||||
# The "impressum" page is intended for Germany. Redirect to German (de) if
|
||||
# requested in any other locale. (Bug 1248393)
|
||||
if request.locale != 'de':
|
||||
return redirect(re.sub(r'^/%s/' % request.locale, '/de/',
|
||||
reverse('legal.impressum')), permanent=True)
|
||||
|
||||
return l10n_utils.render(request, 'legal/impressum.html', {'localized': True})
|
||||
|
|
|
@ -22,8 +22,7 @@ class TestLoadLegalDoc(TestCase):
|
|||
listdir_mock.return_value = ['en-US.md']
|
||||
doc = views.load_legal_doc('the_dude_is_legal', 'de')
|
||||
self.assertIsNone(doc['content'])
|
||||
self.assertFalse(doc['localized'])
|
||||
self.assertDictEqual(doc['translations'], {'en-US': 'English (US)'})
|
||||
self.assertEqual(doc['active_locales'], ['en-US'])
|
||||
|
||||
@patch('os.path.exists')
|
||||
@patch.object(views, 'listdir')
|
||||
|
@ -39,8 +38,7 @@ class TestLoadLegalDoc(TestCase):
|
|||
md_mock.markdownFromFile.assert_called_with(
|
||||
input=good_path, output=ANY, extensions=ANY)
|
||||
self.assertEqual(doc['content'], "You're not wrong Walter...")
|
||||
self.assertFalse(doc['localized'])
|
||||
self.assertDictEqual(doc['translations'], {'de': 'Deutsch', 'en-US': 'English (US)'})
|
||||
self.assertEqual(doc['active_locales'], ['de', 'en-US'])
|
||||
|
||||
@patch('os.path.exists')
|
||||
@patch.object(views, 'listdir')
|
||||
|
@ -56,8 +54,7 @@ class TestLoadLegalDoc(TestCase):
|
|||
md_mock.markdownFromFile.assert_called_with(
|
||||
input=good_path, output=ANY, extensions=ANY)
|
||||
self.assertEqual(doc['content'], "You're not wrong Walter...")
|
||||
self.assertTrue(doc['localized'])
|
||||
self.assertDictEqual(doc['translations'], {'de': 'Deutsch', 'en-US': 'English (US)'})
|
||||
self.assertEqual(doc['active_locales'], ['de', 'en-US'])
|
||||
|
||||
@patch('os.path.exists')
|
||||
@patch.object(views, 'listdir')
|
||||
|
@ -76,11 +73,7 @@ class TestLoadLegalDoc(TestCase):
|
|||
md_mock.markdownFromFile.assert_called_with(
|
||||
input=good_path, output=ANY, extensions=ANY)
|
||||
self.assertEqual(doc['content'], "You're not wrong Walter...")
|
||||
self.assertTrue(doc['localized'])
|
||||
self.assertDictEqual(doc['translations'], {
|
||||
'hi-IN': u'\u0939\u093f\u0928\u094d\u0926\u0940 (\u092d\u093e\u0930\u0924)',
|
||||
'en-US': 'English (US)',
|
||||
})
|
||||
self.assertEqual(doc['active_locales'], ['hi-IN', 'en-US'])
|
||||
|
||||
@patch('os.path.exists')
|
||||
@patch.object(views, 'listdir')
|
||||
|
@ -98,11 +91,7 @@ class TestLoadLegalDoc(TestCase):
|
|||
md_mock.markdownFromFile.assert_called_with(
|
||||
input=good_path, output=ANY, extensions=ANY)
|
||||
self.assertEqual(doc['content'], "You're not wrong Walter...")
|
||||
self.assertTrue(doc['localized'])
|
||||
self.assertDictEqual(doc['translations'], {
|
||||
'hi-IN': u'\u0939\u093f\u0928\u094d\u0926\u0940 (\u092d\u093e\u0930\u0924)',
|
||||
'en-US': 'English (US)',
|
||||
})
|
||||
self.assertEqual(doc['active_locales'], ['hi-IN', 'en-US'])
|
||||
|
||||
|
||||
class TestLegalDocView(TestCase):
|
||||
|
@ -125,8 +114,7 @@ class TestLegalDocView(TestCase):
|
|||
doc_value = "Donny, you're out of your element!"
|
||||
lld_mock.return_value = {
|
||||
'content': doc_value,
|
||||
'localized': True,
|
||||
'translations': {'de': 'Deutsch', 'en-US': 'English (US)'},
|
||||
'active_locales': ['de', 'en-US'],
|
||||
}
|
||||
good_resp = HttpResponse(doc_value)
|
||||
render_mock.return_value = good_resp
|
||||
|
@ -138,7 +126,6 @@ class TestLegalDocView(TestCase):
|
|||
eq_(resp['cache-control'], 'max-age={0!s}'.format(views.CACHE_TIMEOUT))
|
||||
eq_(resp.content, doc_value)
|
||||
eq_(render_mock.call_args[0][2]['doc'], doc_value)
|
||||
self.assertTrue(render_mock.call_args[0][2]['localized'])
|
||||
lld_mock.assert_called_with('the_dude_exists', 'de')
|
||||
|
||||
@patch.object(views, 'load_legal_doc')
|
||||
|
@ -148,8 +135,7 @@ class TestLegalDocView(TestCase):
|
|||
doc_value = "Donny, you're out of your element!"
|
||||
lld_mock.return_value = {
|
||||
'content': doc_value,
|
||||
'localized': True,
|
||||
'translations': {'es-ES': 'Espa\u00f1ol (de Espa\u00f1a)', 'en-US': 'English (US)'},
|
||||
'active_locales': ['es-ES', 'en-US'],
|
||||
}
|
||||
good_resp = HttpResponse(doc_value)
|
||||
render_mock.return_value = good_resp
|
||||
|
@ -168,8 +154,7 @@ class TestLegalDocView(TestCase):
|
|||
doc_value = "Donny, you're out of your element!"
|
||||
lld_mock.return_value = {
|
||||
'content': doc_value,
|
||||
'localized': True,
|
||||
'translations': {'es-ES': 'Espa\u00f1ol (de Espa\u00f1a)', 'en-US': 'English (US)'},
|
||||
'active_locales': ['es-ES', 'en-US'],
|
||||
}
|
||||
good_resp = HttpResponse(doc_value)
|
||||
render_mock.return_value = good_resp
|
||||
|
|
|
@ -15,7 +15,6 @@ from mdx_outline import OutlineExtension
|
|||
|
||||
from bedrock.settings import path as base_path
|
||||
from lib import l10n_utils
|
||||
from lib.l10n_utils.dotlang import get_translations_native_names
|
||||
|
||||
LEGAL_DOCS_PATH = base_path('vendor-local', 'src', 'legal-docs')
|
||||
CACHE_TIMEOUT = getattr(settings, 'LEGAL_DOCS_CACHE_TIMEOUT', 60 * 60)
|
||||
|
@ -47,9 +46,8 @@ def load_legal_doc(doc_name, locale):
|
|||
locales = [f.replace('.md', '') for f in listdir(source_dir) if f.endswith('.md')]
|
||||
# convert legal-docs locales to bedrock equivalents
|
||||
locales = [LEGAL_DOCS_LOCALES_TO_BEDROCK.get(l, l) for l in locales]
|
||||
# filter out non-production locales and convert to dict with names
|
||||
translations = get_translations_native_names(locales)
|
||||
localized = locale != settings.LANGUAGE_CODE
|
||||
# filter out non-production locales
|
||||
locales = [l for l in locales if l in settings.PROD_LANGUAGES]
|
||||
|
||||
# it's possible the legal-docs repo changed the filename to match our locale.
|
||||
# this makes it work for mapped locales even if the map becomes superfluous.
|
||||
|
@ -59,7 +57,6 @@ def load_legal_doc(doc_name, locale):
|
|||
|
||||
if not path.exists(source_file):
|
||||
source_file = path.join(LEGAL_DOCS_PATH, doc_name, 'en-US.md')
|
||||
localized = False
|
||||
|
||||
try:
|
||||
# Parse the Markdown file
|
||||
|
@ -69,14 +66,12 @@ def load_legal_doc(doc_name, locale):
|
|||
content = output.getvalue().decode('utf8')
|
||||
except IOError:
|
||||
content = None
|
||||
localized = False
|
||||
finally:
|
||||
output.close()
|
||||
|
||||
return {
|
||||
'content': content,
|
||||
'localized': localized,
|
||||
'translations': translations,
|
||||
'active_locales': locales,
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,8 +111,7 @@ class LegalDocView(TemplateView):
|
|||
|
||||
context = super(LegalDocView, self).get_context_data(**kwargs)
|
||||
context[self.legal_doc_context_name] = legal_doc['content']
|
||||
context['localized'] = legal_doc['localized']
|
||||
context['translations'] = legal_doc['translations']
|
||||
context['active_locales'] = legal_doc['active_locales']
|
||||
return context
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -51,8 +51,14 @@ def page(name, tmpl, decorators=None, url_name=None, **kwargs):
|
|||
be applied to the view.
|
||||
@param url_name: The value to use as the URL name, default is to coerce
|
||||
the template path into a name as described above.
|
||||
@param active_locales: A list of locale codes that should be active for this page
|
||||
regardless of the state of the lang files. Useful for pages with locale-
|
||||
specific templates or non-English text in the template. Ignores the lang
|
||||
file activation tags.
|
||||
@param add_active_locales: A list of locale codes that should be active for this page
|
||||
in addition to those from the lang files.
|
||||
@param kwargs: Any additional arguments are passed to l10n_utils.render
|
||||
after the request and the template name.
|
||||
as the context.
|
||||
"""
|
||||
pattern = r'^%s/$' % name if name else r'^$'
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ from bedrock.mozorg.tests import TestCase
|
|||
|
||||
@patch('bedrock.newsletter.forms.get_lang_choices',
|
||||
lambda *x: [['en', 'English'], ['fr', 'French'], ['pt', 'Portuguese']])
|
||||
@patch('lib.l10n_utils.template_is_active', lambda *x: True)
|
||||
@patch('lib.l10n_utils.translations_for_template',
|
||||
lambda *x: ['en-US', 'fr', 'pt-BR', 'af'])
|
||||
class TestNewsletterFooter(TestCase):
|
||||
def setUp(self):
|
||||
self.view_name = 'newsletter.subscribe'
|
||||
|
|
|
@ -8,11 +8,11 @@ from django.http import HttpResponse
|
|||
from django.test.client import RequestFactory
|
||||
|
||||
import basket
|
||||
from bedrock.base.urlresolvers import reverse
|
||||
from mock import ANY, DEFAULT, patch
|
||||
from nose.tools import eq_
|
||||
from pyquery import PyQuery as pq
|
||||
|
||||
from bedrock.base.urlresolvers import reverse
|
||||
from bedrock.mozorg.tests import TestCase
|
||||
from bedrock.newsletter.tests import newsletters
|
||||
from bedrock.newsletter.views import (
|
||||
|
@ -340,7 +340,9 @@ class TestExistingNewsletterView(TestCase):
|
|||
self.data['lang'] = 'en'
|
||||
self.data['country'] = 'us'
|
||||
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
with self.activate('en-US'):
|
||||
url = reverse('newsletter.existing.token', args=(self.token,))
|
||||
|
||||
with patch.multiple('basket',
|
||||
update_user=DEFAULT,
|
||||
subscribe=DEFAULT,
|
||||
|
|
|
@ -139,8 +139,7 @@ def privacy(request):
|
|||
|
||||
template_vars = {
|
||||
'doc': process_legal_doc(doc['content']),
|
||||
'localized': doc['localized'],
|
||||
'translations': doc['translations'],
|
||||
'active_locales': doc['active_locales'],
|
||||
}
|
||||
|
||||
return l10n_utils.render(request, 'privacy/index.html', template_vars)
|
||||
|
|
|
@ -452,6 +452,12 @@ When a request is made for a particular page, bedrock's rendering function
|
|||
automatically checks for a locale-specific template, and, if one exists, will
|
||||
render it instead of the originally specified (locale-agnostic) template.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Creating a locale-specific template for en-US was not possible when this
|
||||
feature was introduced, but it is now. So you can create your en-US-only
|
||||
template and the rest of the locales will continue to use the default.
|
||||
|
||||
.. IMPORTANT::
|
||||
|
||||
Note that the presence of an L10n template (e.g.
|
||||
|
@ -459,6 +465,57 @@ render it instead of the originally specified (locale-agnostic) template.
|
|||
a locale-specific template in bedrock.
|
||||
|
||||
|
||||
Specifying Active Locales in Views
|
||||
----------------------------------
|
||||
|
||||
Normally we rely on activation tags in our translation files (.lang files)
|
||||
to determine in which languages a page will be available. This will almost always
|
||||
be what we want for a page. But sometimes we need to explicitly state the locales
|
||||
available for a page. The `impressum` page for example is only available in German
|
||||
and the template itself has German hard-coded into it since we don't need it to be
|
||||
translated into any other languages. In cases like these we can send a list of locale
|
||||
codes with the template context and it will be the final list. This can be accomplished
|
||||
in a few ways depending on how the view is coded.
|
||||
|
||||
For a plain view function, you can simply pass a list of locale codes to `l10n_utils.render`
|
||||
in the context using the name `active_locales`. This will be the full list of available
|
||||
translations. Use `add_active_locales` if you want to add languages to the existing list:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def french_and_german_only(request):
|
||||
return l10n_utils.render(request, 'home.html', {'active_locales': ['de', 'fr'])
|
||||
|
||||
If you don't need a custom view and are just using the `page()` helper function in your `urls.py`
|
||||
file, then you can similarly pass in a list:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
page('about', 'about.html', active_locales=['en-US', 'es-ES']),
|
||||
|
||||
Or if your view is even more fancy and you're using a Class-Based-View that inherits from `LangFilesMixin`
|
||||
(which it must if you want it to be translated) then you can specify the list as part of the view Class
|
||||
definition:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyView(LangFilesMixin, View):
|
||||
active_locales = ['zh-CN', 'hi-IN']
|
||||
|
||||
Or in the `urls.py` when using a CBV:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
url(r'about/$', MyView.as_view(active_locales=['de', 'fr'])),
|
||||
|
||||
The main thing to keep in mind is that if you specify `active_locales` that will be the full list of
|
||||
localizations available for that page. If you'd like to add to the existing list of locales generated
|
||||
from the lang files then you can use the `add_active_locales` name in all of the same ways as
|
||||
`active_locales` above. It's a list of locale codes that will be added to the list already available.
|
||||
This is useful in situations where we would have needed the l10n team to create an empty .lang file with
|
||||
an active tag in it because we have a locale-specific-template with text in the language hard-coded into
|
||||
the template and therefore do not otherwise need a .lang file.
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ from django.utils.translation.trans_real import parse_accept_lang_header
|
|||
|
||||
from bedrock.base.urlresolvers import split_path
|
||||
|
||||
from .dotlang import get_lang_path
|
||||
from .gettext import template_is_active, translations_for_template
|
||||
from .dotlang import get_lang_path, get_translations_native_names
|
||||
from .gettext import translations_for_template
|
||||
|
||||
|
||||
def template_source_url(template):
|
||||
|
@ -43,7 +43,9 @@ def render(request, template, context=None, **kwargs):
|
|||
|
||||
if present, otherwise, it'll render the specified (en-US) template.
|
||||
"""
|
||||
context = {} if context is None else context
|
||||
# use copy() here to avoid modifying the dict in a view that will then
|
||||
# be different on the next call to the view.
|
||||
context = context.copy() if context else {}
|
||||
|
||||
# Make sure we have a single template
|
||||
if isinstance(template, list):
|
||||
|
@ -55,55 +57,31 @@ def render(request, template, context=None, **kwargs):
|
|||
context['langfile'] = get_lang_path(template)
|
||||
context['template_source_url'] = template_source_url(template)
|
||||
|
||||
# Get the available translation list of the current page
|
||||
context.setdefault('translations', {})
|
||||
context['translations'].update(translations_for_template(template))
|
||||
# if `locales` is given use it as the full list of active translations
|
||||
if 'active_locales' in context:
|
||||
translations = context['active_locales']
|
||||
del context['active_locales']
|
||||
else:
|
||||
translations = translations_for_template(template)
|
||||
# if `add_active_locales` is given then add it to the translations for the template
|
||||
if 'add_active_locales' in context:
|
||||
translations.extend(context['add_active_locales'])
|
||||
del context['add_active_locales']
|
||||
|
||||
# Look for localized template if not default lang.
|
||||
if hasattr(request, 'locale') and request.locale != settings.LANGUAGE_CODE:
|
||||
context['translations'] = get_translations_native_names(translations)
|
||||
|
||||
# Look for localized template
|
||||
if hasattr(request, 'locale'):
|
||||
# Redirect to one of the user's accept languages or the site's default
|
||||
# language (en-US) if the current locale not active
|
||||
if not template_is_active(template, get_locale(request)):
|
||||
# Use the default (en-US) template to render instead of redirecting
|
||||
# if the template is not localized yet but the content itself is
|
||||
# localized. This is useful especially for legal documents where the
|
||||
# content is translated in the external legal-docs repository.
|
||||
if context.get('localized', False):
|
||||
return django_render(request, template, context, **kwargs)
|
||||
|
||||
matched = None
|
||||
|
||||
# Look for the user's Accept-Language HTTP header to find another
|
||||
# locale we can offer
|
||||
for lang in get_accept_languages(request):
|
||||
if template_is_active(template, lang):
|
||||
matched = lang
|
||||
break
|
||||
|
||||
# Check for the fallback locales if the previous look-up doesn't
|
||||
# work. This is useful especially in the Spanish locale where es-ES
|
||||
# should be offered as the fallback of es, es-AR, es-CL and es-MX
|
||||
if not matched:
|
||||
for lang in get_accept_languages(request):
|
||||
lang = settings.FALLBACK_LOCALES.get(lang)
|
||||
if lang and template_is_active(template, lang):
|
||||
matched = lang
|
||||
break
|
||||
|
||||
# If all the attempts failed, just use en-US, the default locale of
|
||||
# the site
|
||||
if not matched:
|
||||
matched = settings.LANGUAGE_CODE
|
||||
|
||||
if request.locale not in translations:
|
||||
lang = get_best_translation(translations, get_accept_languages(request))
|
||||
response = HttpResponseRedirect('/' + '/'.join([
|
||||
matched,
|
||||
lang,
|
||||
split_path(request.get_full_path())[1]
|
||||
]))
|
||||
|
||||
# Add the Vary header to avoid wrong redirects due to a cache
|
||||
response['Vary'] = 'Accept-Language'
|
||||
|
||||
return response
|
||||
|
||||
# Render try #1: Look for l10n template in locale/{{ LANG }}/templates/
|
||||
|
@ -159,8 +137,43 @@ def get_accept_languages(request):
|
|||
return languages
|
||||
|
||||
|
||||
def get_best_translation(translations, accept_languages):
|
||||
# Look for the user's Accept-Language HTTP header to find another
|
||||
# locale we can offer
|
||||
for lang in accept_languages:
|
||||
if lang in translations:
|
||||
return lang
|
||||
|
||||
# Check for the fallback locales if the previous look-up doesn't
|
||||
# work. This is useful especially in the Spanish locale where es-ES
|
||||
# should be offered as the fallback of es, es-AR, es-CL and es-MX
|
||||
for lang in accept_languages:
|
||||
lang = settings.FALLBACK_LOCALES.get(lang)
|
||||
if lang in translations:
|
||||
return lang
|
||||
|
||||
# If all the attempts failed, just use en-US, the default locale of
|
||||
# the site
|
||||
if settings.LANGUAGE_CODE in translations:
|
||||
return settings.LANGUAGE_CODE
|
||||
|
||||
# fall back to just the first locale in the list
|
||||
return translations[0]
|
||||
|
||||
|
||||
class LangFilesMixin(object):
|
||||
"""Generic views mixin that uses l10n_utils to render responses."""
|
||||
active_locales = None
|
||||
add_active_locales = None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super(LangFilesMixin, self).get_context_data(**kwargs)
|
||||
if self.active_locales:
|
||||
ctx['active_locales'] = self.active_locales
|
||||
if self.add_active_locales:
|
||||
ctx['add_active_locales'] = self.add_active_locales
|
||||
|
||||
return ctx
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
return render(self.request, self.get_template_names(),
|
||||
|
|
|
@ -287,7 +287,7 @@ def get_translations_for_langfile(langfile):
|
|||
Return the list of available translations for the langfile.
|
||||
|
||||
:param langfile: the path to a lang file, retrieved with get_lang_path()
|
||||
:return: dict, like {'en-US': 'English (US)', 'fr': 'Français'}
|
||||
:return: list, like ['en-US', 'fr']
|
||||
"""
|
||||
|
||||
cache_key = 'translations:%s' % langfile
|
||||
|
@ -296,14 +296,13 @@ def get_translations_for_langfile(langfile):
|
|||
if translations:
|
||||
return translations
|
||||
|
||||
langs = []
|
||||
translations = []
|
||||
for lang in settings.PROD_LANGUAGES:
|
||||
if (lang in product_details.languages and
|
||||
(lang == settings.LANGUAGE_CODE or
|
||||
lang_file_is_active(langfile, lang))):
|
||||
langs.append(lang)
|
||||
translations.append(lang)
|
||||
|
||||
translations = get_translations_native_names(langs)
|
||||
cache.set(cache_key, translations, settings.DOTLANG_CACHE)
|
||||
return translations
|
||||
|
||||
|
@ -320,6 +319,7 @@ def get_translations_native_names(locales):
|
|||
translations = {}
|
||||
for locale in locales:
|
||||
if locale in settings.PROD_LANGUAGES:
|
||||
translations[locale] = product_details.languages[locale]['native']
|
||||
language = product_details.languages.get(locale)
|
||||
translations[locale] = language['native'] if language else locale
|
||||
|
||||
return translations
|
||||
|
|
|
@ -241,14 +241,14 @@ def translations_for_template(template_name):
|
|||
Return the list of available translations for the template.
|
||||
|
||||
:param template_name: name of the template passed to render.
|
||||
:return: dict, like {'en-US': 'English (US)', 'fr': 'Français'}
|
||||
:return: list, like ['en-US', 'fr']
|
||||
"""
|
||||
lang_files = [get_lang_path(template_name)]
|
||||
template = get_template(template_name)
|
||||
lang_files.extend(parse_template(template.template.filename))
|
||||
active_translations = {}
|
||||
active_translations = []
|
||||
for lf in lang_files:
|
||||
active_translations.update(get_translations_for_langfile(lf))
|
||||
active_translations.extend(get_translations_for_langfile(lf))
|
||||
|
||||
return active_translations
|
||||
|
||||
|
|
|
@ -21,11 +21,19 @@ jinja_env = Jinja2.get_default()
|
|||
class TestRender(TestCase):
|
||||
urls = 'lib.l10n_utils.tests.test_files.urls'
|
||||
|
||||
def _test(self, path, template, locale, accept_lang, status, destination=None):
|
||||
def _test(self, path, template, locale, accept_lang, status, destination=None,
|
||||
active_locales=None, add_active_locales=None):
|
||||
request = RequestFactory().get(path)
|
||||
request.META['HTTP_ACCEPT_LANGUAGE'] = accept_lang
|
||||
request.locale = locale
|
||||
response = l10n_utils.render(request, template)
|
||||
ctx = {}
|
||||
if active_locales:
|
||||
ctx['active_locales'] = active_locales
|
||||
|
||||
if add_active_locales:
|
||||
ctx['add_active_locales'] = add_active_locales
|
||||
|
||||
response = l10n_utils.render(request, template, ctx)
|
||||
|
||||
if status == 302:
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
@ -37,33 +45,53 @@ class TestRender(TestCase):
|
|||
def test_firefox(self):
|
||||
path = '/firefox/new/'
|
||||
template = 'firefox/new.html'
|
||||
locales = ['en-US', 'en-GB', 'fr', 'es-ES']
|
||||
|
||||
# Nothing to do with a valid locale
|
||||
self._test(path, template, 'en-US', 'en-us,en;q=0.5',
|
||||
200)
|
||||
200, active_locales=locales)
|
||||
# en-GB is activated on /firefox/new/
|
||||
with patch.object(l10n_utils, 'template_is_active') as active_mock:
|
||||
active_mock.return_value = True
|
||||
self._test(path, template, 'en-GB', 'en-gb,en;q=0.5',
|
||||
200)
|
||||
self._test(path, template, 'en-GB', 'en-gb,en;q=0.5',
|
||||
200, active_locales=locales)
|
||||
|
||||
active_mock.reset_mock()
|
||||
active_mock.side_effect = [False, True]
|
||||
# fr-FR should be treated as fr
|
||||
self._test(path, template, 'fr-FR', 'fr-fr',
|
||||
302, '/fr/firefox/new/')
|
||||
# fr-FR should be treated as fr
|
||||
self._test(path, template, 'fr-FR', 'fr-fr',
|
||||
302, '/fr/firefox/new/', active_locales=locales)
|
||||
|
||||
active_mock.reset_mock()
|
||||
active_mock.side_effect = [False, False, True]
|
||||
# Should fallback to the user's second preferred language
|
||||
self._test(path, template, 'zu', 'zu,fr;q=0.7,en;q=0.3',
|
||||
302, '/fr/firefox/new/')
|
||||
# Should fallback to the user's second preferred language
|
||||
self._test(path, template, 'zu', 'zu,fr;q=0.7,en;q=0.3',
|
||||
302, '/fr/firefox/new/', active_locales=locales)
|
||||
|
||||
active_mock.reset_mock()
|
||||
active_mock.side_effect = [False, False, False, False, True]
|
||||
# Should fallback to one of the site's fallback languages
|
||||
self._test(path, template, 'es-CL', 'es-CL,es;q=0.7,en;q=0.3',
|
||||
302, '/es-ES/firefox/new/')
|
||||
# Should fallback to one of the site's fallback languages
|
||||
self._test(path, template, 'es-CL', 'es-CL,es;q=0.7,en;q=0.3',
|
||||
302, '/es-ES/firefox/new/', active_locales=locales)
|
||||
|
||||
@patch.object(l10n_utils, 'translations_for_template')
|
||||
def test_add_active_locales(self, tft_mock):
|
||||
path = '/firefox/new/'
|
||||
template = 'firefox/new.html'
|
||||
locales = ['en-US', 'en-GB']
|
||||
tft_mock.return_value = ['fr', 'es-ES']
|
||||
# expect same results as above, but with locales from different sources
|
||||
|
||||
# Nothing to do with a valid locale
|
||||
self._test(path, template, 'en-US', 'en-us,en;q=0.5',
|
||||
200, add_active_locales=locales)
|
||||
# en-GB is activated on /firefox/new/
|
||||
self._test(path, template, 'en-GB', 'en-gb,en;q=0.5',
|
||||
200, add_active_locales=locales)
|
||||
|
||||
# fr-FR should be treated as fr
|
||||
self._test(path, template, 'fr-FR', 'fr-fr',
|
||||
302, '/fr/firefox/new/', add_active_locales=locales)
|
||||
|
||||
# Should fallback to the user's second preferred language
|
||||
self._test(path, template, 'zu', 'zu,fr;q=0.7,en;q=0.3',
|
||||
302, '/fr/firefox/new/', add_active_locales=locales)
|
||||
|
||||
# Should fallback to one of the site's fallback languages
|
||||
self._test(path, template, 'es-CL', 'es-CL,es;q=0.7,en;q=0.3',
|
||||
302, '/es-ES/firefox/new/', add_active_locales=locales)
|
||||
|
||||
|
||||
class TestGetAcceptLanguages(TestCase):
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
from django.conf import settings
|
||||
from django.core import mail
|
||||
from django.core.cache import cache
|
||||
from django.core.urlresolvers import clear_url_caches
|
||||
from django.http import HttpRequest
|
||||
from django.test.utils import override_settings
|
||||
|
@ -20,7 +19,7 @@ from pyquery import PyQuery as pq
|
|||
|
||||
from bedrock.mozorg.tests import TestCase
|
||||
from lib.l10n_utils import render
|
||||
from lib.l10n_utils.dotlang import (_, _lazy, FORMAT_IDENTIFIER_RE, lang_file_has_tag,
|
||||
from lib.l10n_utils.dotlang import (_, _lazy, cache, FORMAT_IDENTIFIER_RE, lang_file_has_tag,
|
||||
lang_file_is_active, parse, translate)
|
||||
from lib.l10n_utils.extract import extract_python
|
||||
|
||||
|
@ -39,6 +38,7 @@ jinja_env = Jinja2.get_default().env
|
|||
class TestLangFilesActivation(TestCase):
|
||||
def setUp(self):
|
||||
clear_url_caches()
|
||||
cache.clear()
|
||||
|
||||
def test_lang_file_is_active(self):
|
||||
"""
|
||||
|
|
|
@ -129,7 +129,6 @@ class TestNoLocale(TestCase):
|
|||
|
||||
|
||||
@patch.object(jinja_env.loader, 'searchpath', TEMPLATE_DIRS)
|
||||
@patch('lib.l10n_utils.template_is_active', Mock(return_value=True))
|
||||
@patch('lib.l10n_utils.django_render')
|
||||
class TestLocaleTemplates(TestCase):
|
||||
def setUp(self):
|
||||
|
@ -137,24 +136,44 @@ class TestLocaleTemplates(TestCase):
|
|||
|
||||
def test_enUS_render(self, django_render):
|
||||
"""
|
||||
en-US requests do not look for localized templates and should render the
|
||||
en-US requests without l10n or locale template should render the
|
||||
originally requested template.
|
||||
"""
|
||||
django_render.side_effect = [TemplateDoesNotExist, TemplateDoesNotExist, True]
|
||||
request = self.rf.get('/')
|
||||
request.locale = 'en-US'
|
||||
render(request, 'firefox/new.html', {'active_locales': ['en-US']})
|
||||
django_render.assert_called_with(request, 'firefox/new.html', ANY)
|
||||
|
||||
def test_bedrock_enUS_render(self, django_render):
|
||||
"""
|
||||
en-US requests with a locale-specific template should render the
|
||||
locale-specific template.
|
||||
"""
|
||||
django_render.side_effect = [TemplateDoesNotExist, True]
|
||||
request = self.rf.get('/')
|
||||
request.locale = 'en-US'
|
||||
render(request, 'firefox/new.html', {'active_locales': ['en-US']})
|
||||
django_render.assert_called_with(request, 'firefox/new.en-US.html', ANY)
|
||||
|
||||
def test_enUS_l10n_render(self, django_render):
|
||||
"""
|
||||
en-US requests with an l10n template should render the l10n template.
|
||||
"""
|
||||
request = self.rf.get('/')
|
||||
request.locale = 'en-US'
|
||||
render(request, 'firefox/new.html')
|
||||
django_render.assert_called_with(request, 'firefox/new.html', ANY)
|
||||
render(request, 'firefox/new.html', {'active_locales': ['en-US']})
|
||||
django_render.assert_called_with(request, 'en-US/templates/firefox/new.html', ANY)
|
||||
|
||||
def test_default_render(self, django_render):
|
||||
"""
|
||||
Non en-US requests without l10n or locale template should render the
|
||||
originally requested template.
|
||||
"""
|
||||
django_render.side_effect = [TemplateDoesNotExist, TemplateDoesNotExist,
|
||||
True]
|
||||
django_render.side_effect = [TemplateDoesNotExist, TemplateDoesNotExist, True]
|
||||
request = self.rf.get('/')
|
||||
request.locale = 'de'
|
||||
render(request, 'firefox/new.html')
|
||||
render(request, 'firefox/new.html', {'active_locales': ['de']})
|
||||
django_render.assert_called_with(request, 'firefox/new.html', ANY)
|
||||
|
||||
def test_bedrock_locale_render(self, django_render):
|
||||
|
@ -165,7 +184,7 @@ class TestLocaleTemplates(TestCase):
|
|||
django_render.side_effect = [TemplateDoesNotExist, True]
|
||||
request = self.rf.get('/')
|
||||
request.locale = 'es-ES'
|
||||
render(request, 'firefox/new.html')
|
||||
render(request, 'firefox/new.html', {'active_locales': ['es-ES']})
|
||||
django_render.assert_called_with(request, 'firefox/new.es-ES.html', ANY)
|
||||
|
||||
def test_l10n_render(self, django_render):
|
||||
|
@ -175,5 +194,5 @@ class TestLocaleTemplates(TestCase):
|
|||
"""
|
||||
request = self.rf.get('/')
|
||||
request.locale = 'es-ES'
|
||||
render(request, 'firefox/new.html')
|
||||
render(request, 'firefox/new.html', {'active_locales': ['es-ES']})
|
||||
django_render.assert_called_with(request, 'es-ES/templates/firefox/new.html', ANY)
|
||||
|
|
|
@ -900,7 +900,8 @@ URLS = flatten((
|
|||
|
||||
# bug 1248393
|
||||
url_test('/de/about/legal/impressum/', status_code=requests.codes.ok),
|
||||
url_test('/{en-US,fr,ja}/about/legal/impressum/', '/de/about/legal/impressum/'),
|
||||
url_test('/{en-US,fr,ja}/about/legal/impressum/', '/de/about/legal/impressum/',
|
||||
status_code=requests.codes.found),
|
||||
|
||||
# bug 960543
|
||||
url_test('/firefox/{2,3}.0/eula/random/stuff/', '/legal/eula/firefox-{2,3}/'),
|
||||
|
|
Загрузка…
Ссылка в новой задаче