Commit 5ca2e13e authored by Julien Palard's avatar Julien Palard
Browse files

Use django-modeltranslation.

parent 3c370adc
......@@ -26,20 +26,13 @@ celery -A hkis worker
## Translations
Templates are translated using django `makemessages` and `compilemessages` commands:
```
$ django-admin makemessages -a
# Edit the .po files
$ django-admin compilemessages
```
To translate exercises you'll need to fetch them via the API first:
```
$ pip install requests
$ mkdir -p locale/fr/LC_MESSAGES/
$ python scripts/fetch.py --endpoint https://www.hackinscience.org/api/exercises/
$ python scripts/pygettext.py -o locale/fr/LC_MESSAGES/django.pot to_translate.py
$ cd locale/fr/LC_MESSAGES
$ if [ -f django.po ] ; then msgmerge -U django.po django.pot; else cp django.pot django.po; fi
# msgfmt django.po -o django.mo
```
Exercises (title and wording) are translated via the admin (or the
API), we use django-modeltranslation.
......@@ -39,6 +39,7 @@ INSTALLED_APPS = [
"website",
"moulinette",
"registration",
"modeltranslation",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
......@@ -161,7 +162,7 @@ INTERNAL_IPS = {"127.0.0.1"}
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = "en-us"
LANGUAGES = [("fr", "Français"), ("en", "English")]
LANGUAGES = [("en", "English"), ("fr", "Français")]
TIME_ZONE = "Europe/Paris"
......
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-07-22 09:00+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: scripts/pygettext.py:23
#, python-format
msgid ""
"pygettext -- Python equivalent of xgettext(1)\n"
"\n"
"Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the\n"
"internationalization of C programs. Most of these tools are independent of\n"
"the programming language and can be used from within Python programs.\n"
"Martin von Loewis' work[1] helps considerably in this regard.\n"
"\n"
"There's one problem though; xgettext is the program that scans source code\n"
"looking for message strings, but it groks only C (or C++). Python\n"
"introduces a few wrinkles, such as dual quoting characters, triple quoted\n"
"strings, and raw strings. xgettext understands none of this.\n"
"\n"
"Enter pygettext, which uses Python's standard tokenize module to scan\n"
"Python source code, generating .pot files identical to what GNU xgettext[2]\n"
"generates for C and C++ code. From there, the standard GNU tools can be\n"
"used.\n"
"\n"
"A word about marking Python strings as candidates for translation. GNU\n"
"xgettext recognizes the following keywords: gettext, dgettext, dcgettext,\n"
"and gettext_noop. But those can be a lot of text to include all over your\n"
"code. C and C++ have a trick: they use the C preprocessor. Most\n"
"internationalized C source includes a #define for gettext() to _() so that\n"
"what has to be written in the source is much less. Thus these are both\n"
"translatable strings:\n"
"\n"
" gettext(\"Translatable String\")\n"
" _(\"Translatable String\")\n"
"\n"
"Python of course has no preprocessor so this doesn't work so well. Thus,\n"
"pygettext searches only for _() by default, but see the -k/--keyword flag\n"
"below for how to augment this.\n"
"\n"
" [1] http://www.python.org/workshops/1997-10/proceedings/loewis.html\n"
" [2] http://www.gnu.org/software/gettext/gettext.html\n"
"\n"
"NOTE: pygettext attempts to be option and feature compatible with GNU\n"
"xgettext where ever possible. However some options are still missing or are\n"
"not fully implemented. Also, xgettext's use of command line switches with\n"
"option arguments is broken, and in these cases, pygettext just defines\n"
"additional switches.\n"
"\n"
"Usage: pygettext [options] inputfile ...\n"
"\n"
"Options:\n"
"\n"
" -a\n"
" --extract-all\n"
" Extract all strings.\n"
"\n"
" -d name\n"
" --default-domain=name\n"
" Rename the default output file from messages.pot to name.pot.\n"
"\n"
" -E\n"
" --escape\n"
" Replace non-ASCII characters with octal escape sequences.\n"
"\n"
" -D\n"
" --docstrings\n"
" Extract module, class, method, and function docstrings. These do\n"
" not need to be wrapped in _() markers, and in fact cannot be for\n"
" Python to consider them docstrings. (See also the -X option).\n"
"\n"
" -h\n"
" --help\n"
" Print this help message and exit.\n"
"\n"
" -k word\n"
" --keyword=word\n"
" Keywords to look for in addition to the default set, which are:\n"
" %(DEFAULTKEYWORDS)s\n"
"\n"
" You can have multiple -k flags on the command line.\n"
"\n"
" -K\n"
" --no-default-keywords\n"
" Disable the default set of keywords (see above). Any keywords\n"
" explicitly added with the -k/--keyword option are still recognized.\n"
"\n"
" --no-location\n"
" Do not write filename/lineno location comments.\n"
"\n"
" -n\n"
" --add-location\n"
" Write filename/lineno location comments indicating where each\n"
" extracted string is found in the source. These lines appear before\n"
" each msgid. The style of comments is controlled by the -S/--style\n"
" option. This is the default.\n"
"\n"
" -o filename\n"
" --output=filename\n"
" Rename the default output file from messages.pot to filename. If\n"
" filename is `-' then the output is sent to standard out.\n"
"\n"
" -p dir\n"
" --output-dir=dir\n"
" Output files will be placed in directory dir.\n"
"\n"
" -S stylename\n"
" --style stylename\n"
" Specify which style to use for location comments. Two styles are\n"
" supported:\n"
"\n"
" Solaris # File: filename, line: line-number\n"
" GNU #: filename:line\n"
"\n"
" The style name is case insensitive. GNU style is the default.\n"
"\n"
" -v\n"
" --verbose\n"
" Print the names of the files being processed.\n"
"\n"
" -V\n"
" --version\n"
" Print the version of pygettext and exit.\n"
"\n"
" -w columns\n"
" --width=columns\n"
" Set width of output to columns.\n"
"\n"
" -x filename\n"
" --exclude-file=filename\n"
" Specify a file that contains a list of strings that are not be\n"
" extracted from the input files. Each string to be excluded must\n"
" appear on a line by itself in the file.\n"
"\n"
" -X filename\n"
" --no-docstrings=filename\n"
" Specify a file that contains a list of files (one per line) that\n"
" should not have their docstrings extracted. This is only useful in\n"
" conjunction with the -D option above.\n"
"\n"
"If `inputfile' is -, standard input is read.\n"
msgstr ""
#: scripts/pygettext.py:179
#, python-format
msgid ""
"# SOME DESCRIPTIVE TITLE.\n"
"# Copyright (C) YEAR ORGANIZATION\n"
"# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n"
"#\n"
"msgid \"\"\n"
"msgstr \"\"\n"
"\"Project-Id-Version: PACKAGE VERSION\\n\"\n"
"\"POT-Creation-Date: %(time)s\\n\"\n"
"\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n"
"\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n"
"\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n"
"\"MIME-Version: 1.0\\n\"\n"
"\"Content-Type: text/plain; charset=%(charset)s\\n\"\n"
"\"Content-Transfer-Encoding: %(encoding)s\\n\"\n"
"\"Generated-By: pygettext.py %(version)s\\n\"\n"
"\n"
msgstr ""
#: scripts/pygettext.py:391
#, python-format
msgid "*** %(file)s:%(lineno)s: Seen unexpected token \"%(token)s\""
msgstr ""
#: scripts/pygettext.py:443
#, python-format
msgid "# File: %(filename)s, line: %(lineno)d"
msgstr ""
#: scripts/pygettext.py:450
#, python-format
msgid " %(filename)s:%(lineno)d"
msgstr ""
#: scripts/pygettext.py:527
#, python-format
msgid "Invalid value for --style: %s"
msgstr ""
#: scripts/pygettext.py:535
#, python-format
msgid "pygettext.py (xgettext for Python) %s"
msgstr ""
#: scripts/pygettext.py:541
#, python-format
msgid "--width argument must be an integer: %s"
msgstr ""
#: scripts/pygettext.py:568
#, python-format
msgid "Can't read --exclude-file: %s"
msgstr ""
#: scripts/pygettext.py:587
msgid "Reading standard input"
msgstr ""
#: scripts/pygettext.py:592
#, python-format
msgid "Working on %s"
msgstr ""
#: scripts/pygettext.py:629
#, python-format
msgid "*** Seen unexpected token \"%(token)s\""
msgstr ""
#: scripts/pygettext.py:630
msgid "morethanonestring"
msgstr ""
#: templates/admin/base_site.html:3
msgid "Django site admin"
msgstr ""
......@@ -13,6 +13,7 @@ django-extensions
django-filter
django-model-utils
django-registration-redux
django-modeltranslation
djangorestframework
gunicorn
......
......@@ -26,8 +26,9 @@ django-debug-toolbar==2.2 # via -r requirements.in
django-extensions==3.0.3 # via -r requirements.in
django-filter==2.3.0 # via -r requirements.in
django-model-utils==4.0.0 # via -r requirements.in
django-modeltranslation==0.15.2 # via -r requirements.in
django-registration-redux==2.8 # via -r requirements.in
django==3.0.8 # via -r requirements.in, channels, django-ace, django-debug-toolbar, django-filter, django-model-utils, djangorestframework
django==3.0.8 # via -r requirements.in, channels, django-ace, django-debug-toolbar, django-filter, django-model-utils, django-modeltranslation, djangorestframework
djangorestframework==3.11.0 # via -r requirements.in
future==0.18.2 # via celery
gunicorn==20.0.4 # via -r requirements.in
......@@ -49,7 +50,7 @@ pyparsing==2.4.7 # via packaging
pytz==2020.1 # via celery, django
redis==3.5.3 # via celery
service-identity==18.1.0 # via twisted
six==1.15.0 # via automat, bleach, cryptography, django-extensions, packaging, pyopenssl
six==1.15.0 # via automat, bleach, cryptography, django-extensions, django-modeltranslation, packaging, pyopenssl
sqlparse==0.3.1 # via django, django-debug-toolbar
twisted[tls]==20.3.0 # via daphne
txaio==20.4.1 # via autobahn
......
......@@ -41,12 +41,9 @@ def main():
if "results" not in exercises:
print(exercises)
exit(1)
translatables = []
for exercise in exercises["results"]:
path = Path("exercises") / exercise["slug"]
path.mkdir(exist_ok=True, parents=True)
translatables.append(exercise["wording"])
translatables.append(exercise["title"])
for file in "check.py", "solution.py", "wording.md", "initial_solution.py":
(path / (file)).write_text(
fix_newline_at_end_of_file(exercise[file.split(".")[0]])
......@@ -54,9 +51,6 @@ def main():
del exercise[file.split(".")[0]]
(path / "meta").write_text(json.dumps(exercise, indent=4))
next_exercise_page = exercises["next"]
with open("to_translate.py", "w", encoding="UTF-8") as to_translate:
for string in translatables:
to_translate.write(f"_({string!r})\n")
if __name__ == "__main__":
......
This diff is collapsed.
......@@ -6,6 +6,8 @@ from django.urls import reverse
from django.utils.safestring import mark_safe
from django_ace import AceWidget
from modeltranslation.admin import TranslationAdmin
from website.models import Answer, Exercise, Snippet
......@@ -53,7 +55,7 @@ class AnswerExerciseForm(forms.ModelForm):
}
class ExerciseAdmin(admin.ModelAdmin):
class ExerciseAdmin(TranslationAdmin):
def get_queryset(self, request):
return super().get_queryset(request).with_weekly_stats()
......
# Generated by Django 3.0.8 on 2020-09-27 14:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('website', '0015_exercise_author'),
]
operations = [
migrations.AddField(
model_name='exercise',
name='title_en',
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name='exercise',
name='title_fr',
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name='exercise',
name='wording_en',
field=models.TextField(null=True),
),
migrations.AddField(
model_name='exercise',
name='wording_fr',
field=models.TextField(null=True),
),
]
from modeltranslation.translator import translator, TranslationOptions
from .models import Exercise
class ExerciseTranslationOptions(TranslationOptions):
fields = ("title", "wording")
translator.register(Exercise, ExerciseTranslationOptions)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment