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:
Родитель
a83fb98a93
Коммит
3b2a5855e7
|
@ -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 %}
|
Загрузка…
Ссылка в новой задаче