i18n: Move static/locale back to locale.

As of commit cff40c557b (#9300), these
files are no longer served directly to the browser.  Disentangle them
from the static asset pipeline so we can refactor it without worrying
about them.

This has the side effect of eliminating the accidental duplication of
translation data via hash-naming in our release tarballs.

This reverts commit b546391f0b (#1148).

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
This commit is contained in:
Anders Kaseorg 2019-07-02 13:38:09 -07:00 committed by Tim Abbott
parent d6f5655d0d
commit bbb56df6b0
72 changed files with 43 additions and 56 deletions

6
.gitignore vendored
View File

@ -34,6 +34,12 @@ package-lock.json
# Dockerfiles generated for CircleCI # Dockerfiles generated for CircleCI
/tools/circleci/images /tools/circleci/images
# Generated i18n data
/locale/en
/locale/language_options.json
/locale/language_name_map.json
/locale/*/mobile.json
# Static build # Static build
*.mo *.mo
npm-debug.log npm-debug.log

View File

@ -3,32 +3,32 @@ host = https://www.transifex.com
lang_map = zh-Hans: zh_Hans, zh-Hant: zh_Hant lang_map = zh-Hans: zh_Hans, zh-Hant: zh_Hant
[zulip.djangopo] [zulip.djangopo]
file_filter = static/locale/<lang>/LC_MESSAGES/django.po file_filter = locale/<lang>/LC_MESSAGES/django.po
source_file = static/locale/en/LC_MESSAGES/django.po source_file = locale/en/LC_MESSAGES/django.po
source_lang = en source_lang = en
type = PO type = PO
[zulip.translationsjson] [zulip.translationsjson]
file_filter = static/locale/<lang>/translations.json file_filter = locale/<lang>/translations.json
source_file = static/locale/en/translations.json source_file = locale/en/translations.json
source_lang = en source_lang = en
type = KEYVALUEJSON type = KEYVALUEJSON
[zulip.mobile] [zulip.mobile]
file_filter = static/locale/<lang>/mobile.json file_filter = locale/<lang>/mobile.json
source_file = static/locale/en/mobile.json source_file = locale/en/mobile.json
source_lang = en source_lang = en
type = KEYVALUEJSON type = KEYVALUEJSON
[zulip-test.djangopo] [zulip-test.djangopo]
file_filter = static/locale/<lang>/LC_MESSAGES/django.po file_filter = locale/<lang>/LC_MESSAGES/django.po
source_file = static/locale/en/LC_MESSAGES/django.po source_file = locale/en/LC_MESSAGES/django.po
source_lang = en source_lang = en
type = PO type = PO
[zulip-test.translationsjson] [zulip-test.translationsjson]
file_filter = static/locale/<lang>/translations.json file_filter = locale/<lang>/translations.json
source_file = static/locale/en/translations.json source_file = locale/en/translations.json
source_lang = en source_lang = en
type = KEYVALUEJSON type = KEYVALUEJSON

View File

@ -169,9 +169,7 @@ This is used to deploy essentially all configuration in production.
### Translation files ### Translation files
* `locale/` Backend (Django) translations data files. * `locale/` Backend (Django) and frontend translation data files.
* `static/locale/` Frontend translations data files.
----------------------------------------------------------------------- -----------------------------------------------------------------------

View File

@ -318,7 +318,7 @@ several files from the source data, which we manage similar to our
emoji, but without the caching (and thus without the emoji, but without the caching (and thus without the
garbage-collection). New translations data is downloaded from garbage-collection). New translations data is downloaded from
Transifex and then compiled to generate both the production locale Transifex and then compiled to generate both the production locale
files and also language data in `static/locale/language*.json` using files and also language data in `locale/language*.json` using
`manage.py compilemessages`, which extends the default Django `manage.py compilemessages`, which extends the default Django
implementation of that tool. implementation of that tool.

View File

@ -133,7 +133,7 @@ in our emails.
One can test whether you did the translating part right by running One can test whether you did the translating part right by running
`tools/inline-email-css && manage.py makemessages` and then searching `tools/inline-email-css && manage.py makemessages` and then searching
for the strings in `static/locale/en/LC_MESSAGES/django.po`; if there for the strings in `locale/en/LC_MESSAGES/django.po`; if there
are multiple copies or they contain CSS colors, you did it wrong. are multiple copies or they contain CSS colors, you did it wrong.
A final note for translating emails is that strings that are sent to A final note for translating emails is that strings that are sent to

View File

@ -110,9 +110,9 @@ sense of how everything fits together.
All the translation magic happens through resource files which hold All the translation magic happens through resource files which hold
the translated text. Backend resource files are located at the translated text. Backend resource files are located at
`static/locale/<lang_code>/LC_MESSAGES/django.po`, while frontend `locale/<lang_code>/LC_MESSAGES/django.po`, while frontend
resource files are located at resource files are located at
`static/locale/<lang_code>/translations.json` (and mobile at `locale/<lang_code>/translations.json` (and mobile at
`mobile.json`). `mobile.json`).
These files are uploaded to [Transifex][], where they can be translated. These files are uploaded to [Transifex][], where they can be translated.

View File

@ -6,12 +6,12 @@ from subprocess import check_output
from typing import Dict, List from typing import Dict, List
def get_json_filename(locale: str) -> str: def get_json_filename(locale: str) -> str:
return "static/locale/{}/mobile.json".format(locale) return "locale/{}/mobile.json".format(locale)
def get_locales() -> List[str]: def get_locales() -> List[str]:
tracked_files = check_output(['git', 'ls-files', 'static/locale']) tracked_files = check_output(['git', 'ls-files', 'locale'])
tracked_files = tracked_files.decode().split() tracked_files = tracked_files.decode().split()
regex = re.compile(r'static/locale/(\w+)/LC_MESSAGES/django.po') regex = re.compile(r'locale/(\w+)/LC_MESSAGES/django.po')
locales = ['en'] locales = ['en']
for tracked_file in tracked_files: for tracked_file in tracked_files:
matched = regex.search(tracked_file) matched = regex.search(tracked_file)
@ -37,7 +37,7 @@ for locale in get_locales():
translation_stats.update({locale: stats}) translation_stats.update({locale: stats})
locale_paths.append(path) locale_paths.append(path)
stats_path = os.path.join('static', 'locale', 'mobile_info.json') stats_path = os.path.join('locale', 'mobile_info.json')
with open(stats_path, 'w') as f: with open(stats_path, 'w') as f:
json.dump(translation_stats, f, indent=2, sort_keys=True) json.dump(translation_stats, f, indent=2, sort_keys=True)
f.write('\n') f.write('\n')

6
static/.gitignore vendored
View File

@ -16,11 +16,5 @@
# From tools/setup/generate-custom-icon-webfont # From tools/setup/generate-custom-icon-webfont
/generated/icons/ /generated/icons/
# Generated i18n data
/locale/en
/locale/language_options.json
/locale/language_name_map.json
/locale/*/mobile.json
# Legacy emoji data directory # Legacy emoji data directory
/third/emoji-data /third/emoji-data

View File

@ -114,7 +114,7 @@ echo "$version" > version
cd "$TMPDIR" cd "$TMPDIR"
tar --append -f "$TARBALL" "$prefix/prod-static" "$prefix/build_id" "$prefix/version" "$prefix/zulip-git-version" "$prefix/staticfiles.json" "$prefix/templates/zerver/emails/compiled" "$prefix/webpack-stats-production.json" tar --append -f "$TARBALL" "$prefix/prod-static" "$prefix/build_id" "$prefix/version" "$prefix/zulip-git-version" "$prefix/locale" "$prefix/staticfiles.json" "$prefix/templates/zerver/emails/compiled" "$prefix/webpack-stats-production.json"
rm -rf "$prefix" rm -rf "$prefix"

View File

@ -33,12 +33,12 @@ if __name__ == '__main__':
subprocess.call(['./manage.py', 'makemessages', '--locale', 'en'], subprocess.call(['./manage.py', 'makemessages', '--locale', 'en'],
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
with open('static/locale/en/translations.json') as f: with open('locale/en/translations.json') as f:
data = json.load(f) data = json.load(f)
frontend = check_capitalization(list(data.keys())) frontend = check_capitalization(list(data.keys()))
frontend_errors, frontend_ignored, banned_errors_front = frontend frontend_errors, frontend_ignored, banned_errors_front = frontend
with open('static/locale/en/LC_MESSAGES/django.po') as f: with open('locale/en/LC_MESSAGES/django.po') as f:
rows = [r for r in DJANGO_PO_REGEX.findall(f.read()) if r] rows = [r for r in DJANGO_PO_REGEX.findall(f.read()) if r]
backend = check_capitalization(rows) backend = check_capitalization(rows)
backend_errors, backend_ignored, banned_errors_back = backend backend_errors, backend_ignored, banned_errors_back = backend

View File

@ -33,7 +33,7 @@ if __name__ == '__main__':
subprocess.call(['./manage.py', 'makemessages', '--locale', 'en'], subprocess.call(['./manage.py', 'makemessages', '--locale', 'en'],
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
with open('static/locale/en/translations.json') as f: with open('locale/en/translations.json') as f:
data = json.load(f) data = json.load(f)
found = find_handlebars(list(data.keys())) found = find_handlebars(list(data.keys()))

View File

@ -19,7 +19,7 @@ project_slug = 'zulip-test' # HACK hardcode
tools_dir = os.path.dirname(os.path.abspath(__file__)) tools_dir = os.path.dirname(os.path.abspath(__file__))
root_dir = os.path.dirname(tools_dir) root_dir = os.path.dirname(tools_dir)
# Choose any translation file for processing all strings. # Choose any translation file for processing all strings.
po = polib.pofile(os.path.join(root_dir, 'static', 'locale', 'de', 'LC_MESSAGES', 'django.po')) po = polib.pofile(os.path.join(root_dir, 'locale', 'de', 'LC_MESSAGES', 'django.po'))
for entry in po: for entry in po:
tag = entry.comment tag = entry.comment
if tag: if tag:

View File

@ -561,8 +561,8 @@ def main(options):
# Consider updating generated translations data: both `.mo` # Consider updating generated translations data: both `.mo`
# files and `language-options.json`. # files and `language-options.json`.
paths = ['zerver/management/commands/compilemessages.py'] paths = ['zerver/management/commands/compilemessages.py']
paths += glob.glob('static/locale/*/LC_MESSAGES/*.po') paths += glob.glob('locale/*/LC_MESSAGES/*.po')
paths += glob.glob('static/locale/*/translations.json') paths += glob.glob('locale/*/translations.json')
if file_or_package_hash_updated(paths, "last_compilemessages_hash", options.is_force): if file_or_package_hash_updated(paths, "last_compilemessages_hash", options.is_force):
run(["./manage.py", "compilemessages"]) run(["./manage.py", "compilemessages"])

View File

@ -9,7 +9,7 @@ EXCLUDED_FILES = [
# Transifex syncs translation.json files without trailing # Transifex syncs translation.json files without trailing
# newlines; there's nothing other than trailing newlines we'd be # newlines; there's nothing other than trailing newlines we'd be
# checking for in these anyway. # checking for in these anyway.
"static/locale", "locale",
] ]
PUPPET_CHECK_RULES_TO_EXCLUDE = [ PUPPET_CHECK_RULES_TO_EXCLUDE = [

View File

@ -100,10 +100,6 @@ os.makedirs('prod-static', exist_ok=True)
shutil.move(os.path.join(settings.STATIC_ROOT, 'source-map'), 'prod-static/source-map') shutil.move(os.path.join(settings.STATIC_ROOT, 'source-map'), 'prod-static/source-map')
# Move language_options.json to the production release
run(['cp', '-aT', 'static/locale', os.path.join(settings.STATIC_ROOT, 'locale')],
stdout=fp, stderr=fp)
# Generate /team page markdown for authors # Generate /team page markdown for authors
authors_cmd = ['./tools/update-authors-json'] authors_cmd = ['./tools/update-authors-json']
if os.environ.get("TRAVIS"): if os.environ.get("TRAVIS"):

View File

@ -26,7 +26,7 @@ def with_language(string: str, language: str) -> str:
@lru_cache() @lru_cache()
def get_language_list() -> List[Dict[str, Any]]: def get_language_list() -> List[Dict[str, Any]]:
path = os.path.join(settings.STATIC_ROOT, 'locale', 'language_name_map.json') path = os.path.join(settings.DEPLOY_ROOT, 'locale', 'language_name_map.json')
with open(path, 'r') as reader: with open(path, 'r') as reader:
languages = ujson.load(reader) languages = ujson.load(reader)
return languages['name_map'] return languages['name_map']
@ -88,7 +88,7 @@ def get_language_translation_data(language: str) -> Dict[str, str]:
language = 'zh_Hant' language = 'zh_Hant'
elif language == 'id-id': elif language == 'id-id':
language = 'id_ID' language = 'id_ID'
path = os.path.join(settings.STATIC_ROOT, 'locale', language, 'translations.json') path = os.path.join(settings.DEPLOY_ROOT, 'locale', language, 'translations.json')
try: try:
with open(path, 'r') as reader: with open(path, 'r') as reader:
return ujson.load(reader) return ujson.load(reader)

View File

@ -27,13 +27,6 @@ class Command(compilemessages.Command):
help='Stop execution in case of errors.') help='Stop execution in case of errors.')
def handle(self, *args: Any, **options: Any) -> None: def handle(self, *args: Any, **options: Any) -> None:
if settings.PRODUCTION:
# HACK: When using upgrade-zulip-from-git, we're in a
# production environment where STATIC_ROOT will include
# past versions; this ensures we only process the current
# version
settings.STATIC_ROOT = os.path.join(settings.DEPLOY_ROOT, "static")
settings.LOCALE_PATHS = (os.path.join(settings.DEPLOY_ROOT, 'static/locale'),)
super().handle(*args, **options) super().handle(*args, **options)
self.strict = options['strict'] self.strict = options['strict']
self.extract_language_options() self.extract_language_options()
@ -41,9 +34,9 @@ class Command(compilemessages.Command):
def create_language_name_map(self) -> None: def create_language_name_map(self) -> None:
join = os.path.join join = os.path.join
static_root = settings.STATIC_ROOT deploy_root = settings.DEPLOY_ROOT
path = join(static_root, 'locale', 'language_options.json') path = join(deploy_root, 'locale', 'language_options.json')
output_path = join(static_root, 'locale', 'language_name_map.json') output_path = join(deploy_root, 'locale', 'language_name_map.json')
with open(path, 'r') as reader: with open(path, 'r') as reader:
languages = ujson.load(reader) languages = ujson.load(reader)
@ -80,9 +73,9 @@ class Command(compilemessages.Command):
raise Exception("Unknown language %s" % (locale,)) raise Exception("Unknown language %s" % (locale,))
def get_locales(self) -> List[str]: def get_locales(self) -> List[str]:
tracked_files = check_output(['git', 'ls-files', 'static/locale']) tracked_files = check_output(['git', 'ls-files', 'locale'])
tracked_files = tracked_files.decode().split() tracked_files = tracked_files.decode().split()
regex = re.compile(r'static/locale/(\w+)/LC_MESSAGES/django.po') regex = re.compile(r'locale/(\w+)/LC_MESSAGES/django.po')
locales = ['en'] locales = ['en']
for tracked_file in tracked_files: for tracked_file in tracked_files:
matched = regex.search(tracked_file) matched = regex.search(tracked_file)
@ -92,7 +85,7 @@ class Command(compilemessages.Command):
return locales return locales
def extract_language_options(self) -> None: def extract_language_options(self) -> None:
locale_path = "{}/locale".format(settings.STATIC_ROOT) locale_path = "{}/locale".format(settings.DEPLOY_ROOT)
output_path = "{}/language_options.json".format(locale_path) output_path = "{}/language_options.json".format(locale_path)
data = {'languages': []} # type: Dict[str, List[Dict[str, Any]]] data = {'languages': []} # type: Dict[str, List[Dict[str, Any]]]

View File

@ -78,7 +78,7 @@ class Command(makemessages.Command):
default='static/templates', default='static/templates',
help='Name of the Handlebars template directory') help='Name of the Handlebars template directory')
parser.add_argument('--frontend-output', type=str, parser.add_argument('--frontend-output', type=str,
default='static/locale', default='locale',
help='Name of the frontend messages output directory') help='Name of the frontend messages output directory')
parser.add_argument('--frontend-namespace', type=str, parser.add_argument('--frontend-namespace', type=str,
default='translations.json', default='translations.json',

View File

@ -889,7 +889,7 @@ else:
# If changing this, you need to also the hack modifications to this in # If changing this, you need to also the hack modifications to this in
# our compilemessages management command. # our compilemessages management command.
LOCALE_PATHS = (os.path.join(STATIC_ROOT, 'locale'),) LOCALE_PATHS = (os.path.join(DEPLOY_ROOT, 'locale'),)
# We want all temporary uploaded files to be stored on disk. # We want all temporary uploaded files to be stored on disk.
FILE_UPLOAD_MAX_MEMORY_SIZE = 0 FILE_UPLOAD_MAX_MEMORY_SIZE = 0