Bug 920113: Use the specified lang file for activation.

If a template uses the "set_lang_files" template tag
we should check the first lang file in that list if
the lang file for the specific template name isn't
active.
This commit is contained in:
Paul McLanahan 2013-10-01 14:25:19 -04:00
Родитель fc11a9f485
Коммит 933609e6a7
13 изменённых файлов: 139 добавлений и 37 удалений

Просмотреть файл

@ -159,7 +159,7 @@ class TestUniversityAmbassadors(TestCase):
data=request_data)
@patch.object(l10n_utils, 'lang_file_is_active', lambda *x: True)
@patch.object(l10n_utils.dotlang, 'lang_file_is_active', lambda *x: True)
class TestContribute(TestCase):
def setUp(self):
self.client = Client()

Просмотреть файл

@ -1,17 +1,18 @@
from mock import patch
from django.test import Client
from funfactory.urlresolvers import reverse
from lib import l10n_utils
from bedrock.mozorg.tests import TestCase
from mock import patch
from nose.tools import eq_
from pyquery import PyQuery as pq
from bedrock.mozorg.tests import TestCase
@patch('bedrock.newsletter.utils.get_newsletter_languages', lambda *x: set(['en', 'fr', 'pt']))
@patch.object(l10n_utils, 'lang_file_is_active', lambda *x: True)
@patch('lib.l10n_utils.template_is_active', lambda *x: True)
class TestNewsletterFooter(TestCase):
def setUp(self):
self.view_name = 'firefox.fx'
self.view_name = 'newsletter.mozilla-and-you'
self.client = Client()
def test_country_selected(self):

Просмотреть файл

@ -44,7 +44,7 @@ PROD_LANGUAGES = ('ach', 'af', 'ak', 'an', 'ar', 'as', 'ast', 'az', 'be', 'bg',
DEV_LANGUAGES = list(DEV_LANGUAGES) + ['en-US']
FEED_CACHE = 3900
DOTLANG_CACHE = 60
DOTLANG_CACHE = 600
DOTLANG_FILES = ['main', 'download_button', 'newsletter']

Просмотреть файл

@ -8,6 +8,7 @@ from hashlib import md5
from django.conf import settings
from django.test import Client, RequestFactory
from django.test.utils import override_settings
from django.utils.http import parse_http_date
from funfactory.urlresolvers import reverse
@ -147,10 +148,10 @@ class TabzillaRedirectTests(TestCase):
resp = self._process_request('/')
eq_(resp['location'], '/en-US/')
@override_settings(DEV=False)
@patch('bedrock.tabzilla.middleware.settings.CDN_BASE_URL', '//example.com')
@patch('bedrock.tabzilla.middleware.settings.TEMPLATE_DEBUG', False)
@patch('lib.l10n_utils.settings.DEV', False)
@patch('lib.l10n_utils.lang_file_is_active')
@patch('lib.l10n_utils.template_is_active')
def test_redirect_to_cdn_inactive_locale(self, lang_mock):
"""
The view should redirect to the CDN when the locale is not active.
@ -159,10 +160,10 @@ class TabzillaRedirectTests(TestCase):
resp = self.client.get('/de/tabzilla/tabzilla.js')
eq_(resp['location'], 'http://example.com/en-US/tabzilla/tabzilla.js')
@override_settings(DEV=False)
@patch('bedrock.tabzilla.middleware.settings.CDN_BASE_URL', '//example.com')
@patch('bedrock.tabzilla.middleware.settings.TEMPLATE_DEBUG', False)
@patch('lib.l10n_utils.settings.DEV', False)
@patch('lib.l10n_utils.lang_file_is_active')
@patch('lib.l10n_utils.template_is_active')
def test_no_redirect_to_cdn_active_locale(self, lang_mock):
"""
The view should NOT redirect to the CDN when the locale is active.
@ -171,10 +172,10 @@ class TabzillaRedirectTests(TestCase):
resp = self.client.get('/de/tabzilla/tabzilla.js')
ok_(resp.status_code == 200)
@override_settings(DEV=False)
@patch('bedrock.tabzilla.middleware.settings.CDN_BASE_URL', '')
@patch('bedrock.tabzilla.middleware.settings.TEMPLATE_DEBUG', False)
@patch('lib.l10n_utils.settings.DEV', False)
@patch('lib.l10n_utils.lang_file_is_active')
@patch('lib.l10n_utils.template_is_active')
def test_no_redirect_to_cdn_no_cdn(self, lang_mock):
"""
The view should not redirect to the CDN when the CDN setting is empty.
@ -183,10 +184,10 @@ class TabzillaRedirectTests(TestCase):
resp = self.client.get('/de/tabzilla/tabzilla.js')
eq_(resp['location'], 'http://testserver/en-US/tabzilla/tabzilla.js')
@override_settings(DEV=False)
@patch('bedrock.tabzilla.middleware.settings.CDN_BASE_URL', '//example.com')
@patch('bedrock.tabzilla.middleware.settings.TEMPLATE_DEBUG', True)
@patch('lib.l10n_utils.settings.DEV', False)
@patch('lib.l10n_utils.lang_file_is_active')
@patch('lib.l10n_utils.template_is_active')
def test_no_redirect_to_cdn_template_debug(self, lang_mock):
"""
The view should not redirect to the CDN when TEMPLATE_DEBUG is True.

Просмотреть файл

@ -9,7 +9,8 @@ from django.template import TemplateDoesNotExist
from funfactory.urlresolvers import split_path
from dotlang import get_lang_path, get_translations, lang_file_is_active
from .dotlang import get_lang_path, get_translations
from .gettext import template_is_active
def render(request, template, context=None, **kwargs):
@ -42,8 +43,7 @@ def render(request, template, context=None, **kwargs):
if hasattr(request, 'locale') and request.locale != settings.LANGUAGE_CODE:
# redirect to default lang if locale not active
if not (settings.DEV or
lang_file_is_active(context['langfile'], request.locale)):
if not template_is_active(template, get_locale(request)):
return HttpResponseRedirect('/' + '/'.join([
settings.LANGUAGE_CODE,
split_path(request.get_full_path())[1]

Просмотреть файл

@ -278,7 +278,7 @@ def get_translations(langfile):
for lang in settings.PROD_LANGUAGES:
if (lang in product_details.languages and
(lang == settings.LANGUAGE_CODE or
lang_file_is_active(langfile, lang))):
lang_file_is_active(langfile, lang))):
translations[lang] = product_details.languages[lang]['native']
cache.set(cache_key, translations, settings.DOTLANG_CACHE)

Просмотреть файл

@ -11,9 +11,11 @@ from os.path import join
from tokenize import generate_tokens, NAME, NEWLINE, OP, untokenize
from django.conf import settings
from django.core.cache import cache
from django.template.loader import get_template
from jinja2 import Environment
from dotlang import parse as parse_lang, get_lang_path
from dotlang import parse as parse_lang, get_lang_path, lang_file_is_active
REGEX_URL = re.compile(r'.* (\S+/\S+\.[^:]+).*')
@ -159,6 +161,35 @@ def parse_template(path):
return []
def template_is_active(path, lang):
"""Given a template path, determine if it should be active for a locale.
It is active if either the template's lang file, or the lang file
specified in the "set_lang_files" template tag has the active tag.
:param path: relative path to the template.
:param lang: language code
:return: boolean
"""
if settings.DEV:
return True
cache_key = 'template_active:{lang}:{path}'.format(lang=lang, path=path)
is_active = cache.get(cache_key)
if is_active is None:
# try the quicker and more efficient check first
is_active = lang_file_is_active(get_lang_path(path), lang)
if not is_active:
template = get_template(path)
lang_files = parse_template(template.filename)
is_active = lang_files and lang_file_is_active(lang_files[0], lang)
cache.set(cache_key, is_active, settings.DOTLANG_CACHE)
return is_active
def langfiles_for_path(path):
"""
Find and return any extra lang files specified in templates or python

Просмотреть файл

@ -12,20 +12,20 @@ from django.core.cache import cache
from django.core.urlresolvers import clear_url_caches
from django.http import HttpRequest
from django.test.client import Client
from django.test.utils import override_settings
from jingo import env
from jinja2 import FileSystemLoader
from mock import patch
from nose.tools import assert_not_equal, eq_, ok_
from product_details import product_details
from pyquery import PyQuery as pq
from tower import extract_tower_python
from lib.l10n_utils import render
from lib.l10n_utils.dotlang import (_, FORMAT_IDENTIFIER_RE, lang_file_has_tag,
lang_file_is_active, parse, translate,
_lazy)
from product_details import product_details
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,
lang_file_is_active, parse, translate)
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_files')
@ -41,7 +41,7 @@ class TestLangFilesActivation(TestCase):
clear_url_caches()
self.client = Client()
@patch('lib.l10n_utils.settings.DEV', False)
@override_settings(DEV=False)
def test_lang_file_is_active(self):
"""
`lang_file_is_active` should return true if lang file has the
@ -67,16 +67,19 @@ class TestLangFilesActivation(TestCase):
ok_(not lang_file_has_tag('main', 'de', 'tag_after_non_tag_lines'))
ok_(not lang_file_has_tag('main', 'de', 'no_such_tag'))
@patch('lib.l10n_utils.settings.DEV', False)
@override_settings(DEV=False)
def test_active_locale_not_redirected(self):
""" Active lang file should render correctly. """
""" Active lang file should render correctly.
Also the template has an inactive lang file manually set,
but that should not cause it to be inactive.
"""
response = self.client.get('/de/active-de-lang-file/')
eq_(response.status_code, 200)
doc = pq(response.content)
eq_(doc('h1').text(), 'Die Lage von Mozilla')
@patch('lib.l10n_utils.settings.DEV', False)
@patch.object(settings, 'LANGUAGE_CODE', 'en-US')
@override_settings(DEV=False, LANGUAGE_CODE='en-US')
def test_inactive_locale_redirected(self):
""" Inactive locale should redirect to en-US. """
response = self.client.get('/de/inactive-de-lang-file/')
@ -87,7 +90,7 @@ class TestLangFilesActivation(TestCase):
doc = pq(response.content)
eq_(doc('h1').text(), 'The State of Mozilla')
@patch('lib.l10n_utils.settings.DEV', True)
@override_settings(DEV=True)
def test_inactive_locale_not_redirected_dev_true(self):
"""
Inactive lang file should not redirect in DEV mode.
@ -97,6 +100,14 @@ class TestLangFilesActivation(TestCase):
doc = pq(response.content)
eq_(doc('h1').text(), 'Die Lage von Mozilla')
@override_settings(DEV=False)
def test_active_alternate_lang_file(self):
"""Template with active alternate lang file should activate from it."""
response = self.client.get('/de/state-of-mozilla/')
eq_(response.status_code, 200)
doc = pq(response.content)
eq_(doc('h1').text(), 'Die Lage von Mozilla')
class TestDotlang(TestCase):
def setUp(self):

Просмотреть файл

@ -2,6 +2,8 @@
# 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/. #}
{% set_lang_files "inactive_de_lang_file" %}
<html>
<body>
<h1>{{ _('The State of Mozilla') }}</h1>

Просмотреть файл

@ -0,0 +1,20 @@
{# This Source Code Form is subject to the terms of the Mozilla Public
# 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/. #}
{% set_lang_files "active_de_lang_file" "inactive_de_lang_file" %}
<html>
<body>
<h1>{{ _('The State of Mozilla') }}</h1>
<p>
{% trans %}
Mozillas vision of the Internet is a place where anyone can
access information, a place where everyone can hack and tinker;
one that has openness, freedom and transparency; where users have
control over their personal data and where all minds have the
freedom to create and to consume without walls or tight restrictions.
{% endtrans %}
</p>
</body>
</html>

Просмотреть файл

@ -2,7 +2,8 @@
# 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/.
from django.conf.urls.defaults import *
from django.conf.urls import patterns
from bedrock.mozorg.util import page
@ -11,4 +12,5 @@ urlpatterns = patterns('',
page('active-de-lang-file', 'active_de_lang_file.html'),
page('inactive-de-lang-file', 'inactive_de_lang_file.html'),
page('some-lang-files', 'some_lang_files.html'),
page('state-of-mozilla', 'state_of_mozilla.html'),
)

Просмотреть файл

@ -13,8 +13,8 @@ from mock import ANY, MagicMock, Mock, patch
from nose.tools import eq_, ok_
from lib.l10n_utils.gettext import (_append_to_lang_file, langfiles_for_path,
parse_python, parse_template, po_msgs,
pot_to_langfiles)
parse_python, parse_template,
po_msgs, pot_to_langfiles, template_is_active)
from lib.l10n_utils.tests import TempFileMixin
from bedrock.mozorg.tests import TestCase
@ -29,6 +29,40 @@ TRUE_MOCK = Mock()
TRUE_MOCK.return_value = True
class TestTemplateIsActive(TestCase):
@override_settings(DEV=False)
@patch('lib.l10n_utils.gettext.parse_template')
@patch('lib.l10n_utils.gettext.lang_file_is_active')
@patch('django.core.cache.cache.get')
@patch('django.core.cache.cache.set')
def test_cache_hit(self, cache_set_mock, cache_get_mock, lang_active_mock,
parse_template_mock):
"""Should not call other methods on cache hit."""
cache_get_mock.return_value = True
self.assertTrue(template_is_active('the/dude', 'de'))
cache_get_mock.assert_called_once_with('template_active:de:the/dude')
self.assertFalse(lang_active_mock.called)
self.assertFalse(parse_template_mock.called)
self.assertFalse(cache_set_mock.called)
@override_settings(DEV=False)
@patch('lib.l10n_utils.gettext.parse_template')
@patch('lib.l10n_utils.gettext.lang_file_is_active')
@patch('django.core.cache.cache.get')
@patch('django.core.cache.cache.set')
def test_cache_miss(self, cache_set_mock, cache_get_mock, lang_active_mock,
parse_template_mock):
"""Should check the files and set the cache on cache miss."""
cache_get_mock.return_value = None
lang_active_mock.return_value = True
self.assertTrue(template_is_active('the/dude', 'de'))
cache_key = 'template_active:de:the/dude'
cache_get_mock.assert_called_once_with(cache_key)
self.assertTrue(lang_active_mock.called)
self.assertFalse(parse_template_mock.called)
cache_set_mock.assert_called_once_with(cache_key, True, settings.DOTLANG_CACHE)
class TestPOFiles(TestCase):
good_messages = [
[u'Said angrily, loudly, and repeatedly.',

Просмотреть файл

@ -78,7 +78,7 @@ class TestTemplateLangFiles(TestCase):
template = env.get_template('some_lang_files.html')
# make a dummy object capable of having arbitrary attrs assigned
request = type('request', (), {})()
template.render({'request':request})
template.render({'request': request})
eq_(request.langfiles, ['dude', 'walter',
'main', 'download_button', 'newsletter'])
@ -123,8 +123,8 @@ class TestTemplateLangFiles(TestCase):
The template-specific lang file should come before the defaults.
"""
self.client.get('/de/active-de-lang-file/')
translate.assert_called_with(ANY, ['active_de_lang_file', 'main',
'download_button', 'newsletter'])
translate.assert_called_with(ANY, ['inactive_de_lang_file', 'active_de_lang_file',
'main', 'download_button', 'newsletter'])
class TestNoLocale(TestCase):