зеркало из https://github.com/mozilla/bedrock.git
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:
Родитель
fc11a9f485
Коммит
933609e6a7
|
@ -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 %}
|
||||
Mozilla‘s 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):
|
||||
|
|
Загрузка…
Ссылка в новой задаче