Generate and use /static/js/i18n/{locale}.js instead of using a dynamic view (#10372)

Generate and use /static/js/i18n/{locale}.js instead of using a dynamic view
This commit is contained in:
Mathieu Pillard 2019-01-15 11:49:31 +01:00 коммит произвёл GitHub
Родитель a83fb98a93
Коммит 3b2a5855e7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 88 добавлений и 111 удалений

73
.gitignore поставляемый
Просмотреть файл

@ -1,46 +1,47 @@
.env
*-all.css
*-all.js
*-min.css
*-min.js
*.css.tmp
*.egg-info
settings_local.py*
settings_local_*.py
local_settings.py
shellng_local.py
*.js.tmp
*.less.css
*.mo
*.po~
*.py[co]
*.signed.zip
*.styl.css
.*.sw?
pip-log.txt
.cache
.DS_Store
.env
.ipython
.nose*
.npm/
.pytest_cache/*
.tox/
.vscode
build*.py
docker*.yml
docker/artifacts/*
docs/_gh-pages
docs/api/_build
build*.py
.DS_Store
*.js.tmp
*.css.tmp
*-min.css
*-all.css
*-min.js
*-all.js
*.less.css
*.styl.css
.nose*
tmp/*
.pytest_cache/*
local_settings.py
logs/*
MANIFEST
node_modules
.npm/
static/css/node_lib/*
static/js/node_lib/*
*.signed.zip
*.po~
*.mo
pip-log.txt
settings_local.py*
settings_local_*.py
shellng_local.py
site-static/*
storage/shared_storage/*
storage/guarded-addons/*
src/olympia/discovery/strings.jinja2
static/css/node_lib/*
static/js/i18n/*.js
static/js/node_lib/*
storage/files/*
storage/git-storage/*
.tox/
MANIFEST
docker/artifacts/*
logs/*
storage/guarded-addons/*
storage/shared_storage/*
supervisord.pid
.ipython
docker*.yml
.cache
.vscode
src/olympia/discovery/strings.jinja2
tmp/*

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

@ -98,6 +98,7 @@ RUN DJANGO_SETTINGS_MODULE='settings_local' locale/compile-mo.sh locale
RUN npm install \
&& make -f Makefile-docker copy_node_js \
&& DJANGO_SETTINGS_MODULE='settings_local' python manage.py compress_assets \
&& DJANGO_SETTINGS_MODULE='settings_local' python manage.py generate_jsi18n_files \
&& DJANGO_SETTINGS_MODULE='settings_local' python manage.py collectstatic --noinput
RUN rm -f settings_local.py settings_local.pyc

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

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
import os
from django.conf import settings
from django.core.management.base import BaseCommand
from django.http import HttpRequest
from django.utils import translation
from django.views.i18n import javascript_catalog
class Command(BaseCommand):
help = 'Generate static jsi18n files for each locale we support'
def handle(self, *args, **options):
fake_request = HttpRequest()
for lang in settings.AMO_LANGUAGES:
filename = os.path.join(
settings.STATICFILES_DIRS[0], 'js', 'i18n', '%s.js' % lang)
with translation.override(lang):
response = javascript_catalog(fake_request)
with open(filename, 'w') as f:
f.write(response.content)

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

@ -109,3 +109,22 @@ def test_compress_assets_correctly_fetches_static_images(settings, tmpdir):
data = fobj.read()
assert 'background-image:url(' in data
assert 'img/icons/stars.png' in data
@pytest.mark.needs_locales_compilation
def test_generate_jsi18n_files():
dirname = os.path.join(settings.STATICFILES_DIRS[0], 'js', 'i18n')
assert os.path.exists(dirname)
filename = os.path.join(dirname, 'fr.js')
call_command('generate_jsi18n_files')
# Regardless of whether or not the file existed before, it needs to exist
# now.
assert os.path.exists(filename), filename
# Spot-check: Look for a string we know should be in the french file
# (Translation for "Error").
filename = os.path.join(
settings.STATICFILES_DIRS[0], 'js', 'i18n', 'fr.js')
with open(filename) as f:
content = f.read()
assert u'Erreur' in content

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

@ -321,25 +321,15 @@ class TestOtherStuff(TestCase):
assert set_remote_addr_mock.call_args_list[0] == (('1.1.1.1',), {})
assert set_remote_addr_mock.call_args_list[1] == ((None,), {})
@patch.object(settings, 'CDN_HOST', 'https://cdn.example.com')
def test_jsi18n_caching_and_cdn(self):
# The jsi18n catalog should be cached for a long time.
# Get the url from a real page so it includes the build id.
client = test.Client()
doc = pq(client.get('/', follow=True).content)
js_url = '%s%s' % (settings.CDN_HOST, reverse('jsi18n'))
url_with_build = doc('script[src^="%s"]' % js_url).attr('src')
response = client.get(url_with_build.replace(settings.CDN_HOST, ''),
follow=False)
self.assertCloseToNow(response['Expires'],
now=datetime.now() + timedelta(days=365))
@pytest.mark.needs_locales_compilation
def test_jsi18n(self):
"""Test that the jsi18n library has an actual catalog of translations
rather than just identity functions."""
response = self.client.get(reverse('jsi18n'))
self.assertCloseToNow(response['Expires'],
now=datetime.now() + timedelta(days=365))
en = self.client.get(reverse('jsi18n')).content
with self.activate('fr'):

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

@ -72,7 +72,7 @@
<span></span>
</div>
{% block site_js %}
<script src="{{ CDN_HOST }}{{ cache_buster(url('jsi18n')) }}"></script>
<script src="{{ static('js/i18n/%s.js'|format(LANG)) }}"></script>
{{ js('common') }}
{% endblock %}
</body>

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

@ -160,7 +160,7 @@
</div>
{# js #}
{% block site_js %}
<script src="{{ CDN_HOST }}{{ cache_buster(url('jsi18n')) }}"></script>
<script src="{{ static('js/i18n/%s.js'|format(LANG)) }}"></script>
{{ js('common') }}
{% endblock %}
{% block js %}{% endblock %}

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

@ -205,7 +205,7 @@
{% endblock %}
{# js #}
{% block site_js %}
<script src="{{ CDN_HOST }}{{ cache_buster(url('jsi18n')) }}"></script>
<script src="{{ static('js/i18n/%s.js'|format(LANG)) }}"></script>
{{ js('impala') }}
{% endblock %}
{% block js %}{% endblock %}

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

@ -75,6 +75,8 @@ urlpatterns = [
# Javascript translations.
# Should always be called with a cache-busting querystring.
# Still served for the moment, but superseded by a fully static version we
# generate at deploy-time through our generate_jsi18n_files command.
url(r'^jsi18n\.js$', cache_page(60 * 60 * 24 * 365)(javascript_catalog),
{'domain': 'djangojs', 'packages': []}, name='jsi18n'),

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

@ -1,58 +0,0 @@
{% extends "zadmin/base.html" %}
{% block title %}{{ page_title('Hera') }}{% endblock %}
{% block content %}
<h2>Hera</h2>
<div class="primary featured">
{% if form %}
<form method="post" class="featured-inner object-lead user-input">
{% csrf_token %}
<fieldset>
<p class="instruct">Want to get a message to Zeus out on the front
lines? You've come to the right place. The text area below accepts
a new-line separated list of simple expressions (wildcards are
supported) which will be flushed from the front end cache.
</p>
<ul>
<li>
<label for="id_prefix">Prefix</label>
{{ form.flushprefix }}
{{ form.flushprefix.errors }}
<p class="note">A prefix to prepend to all your patterns. I suggest <strong>{{ settings.SITE_URL }}</strong></p>
</li>
<li>
{{ form.flushlist }}
{{ form.flushlist.errors }}
<p class="note">A new-line separated list of simple expressions (wildcards allowed) which will be flushed from
the front end cache. Examples: <strong>/*addon/1865*</strong> or <strong>/de/jsi18n*</strong></p>
</li>
</ul>
</fieldset>
<div class="fm-control">
<button type="submit">Go go go</button>
</div>
</form>
{% endif %}
</div>
<div class="secondary">
<h3>Stats</h3>
{% for i in boxes %}
<h4>{{ i.location }}</h4>
{% if i.stats %}
{% if i.stats.num_lookups > 0 %}
{# Can't jinja2 detect a division by zero and just do something intelligent? #}
{% set hit_percentage = 100 * i.stats.num_hits / i.stats.num_lookups %}
{% else %}
{% set hit_percentage = 0 %}
{% endif %}
<ul>
<li>Using {{ i.stats.bytes_used|filesizeformat(binary=True) }} ({{ i.stats.percent_used }}% of total space) for {{ i.stats.entries|numberfmt }} objects.</li>
<li>We've had {{ i.stats.num_hits|numberfmt }} hits out of {{ i.stats.num_lookups|numberfmt }} lookups ({{ hit_percentage|round(2) }}%).</li>
</ul>
{% else %}
<p>Not Available</p>
{% endif %}
{% endfor %}
</div>
{% endblock %}

0
static/js/i18n/.keep Normal file
Просмотреть файл