[extensions-web] i18n: added damned-lies compatible translation workflow support.



commit 5cd432e4eb380b4ee23ed48e989cd4cd3fdc01cb
Author: Yuri Konotopov <ykonotopov gnome org>
Date:   Tue Jan 22 07:45:55 2019 +0400

    i18n: added damned-lies compatible translation workflow support.
    
    1. Implemented damned-lies compatible translation workflow with single
    gettext template in the "po" directory"
    2. Implemented Mustache templates strings extraction in makemessages.
    3. Implemented Django-compatible compilation of translations from single
    gettext template to "django.po" and "djangojs.po"
    
    See-Also: https://gitlab.gnome.org/Infrastructure/extensions-web/merge_requests/9

 README.rst                                         |   1 +
 openshift/docker/Dockerfile                        |   7 +-
 requirements.txt                                   |   1 +
 sweettooth/core/__init__.py                        |   0
 sweettooth/core/management/__init__.py             |   0
 sweettooth/core/management/commands/__init__.py    |  25 ++
 .../core/management/commands/compilemessages.py    |  55 +++++
 .../core/management/commands/makemessages.py       | 125 ++++++++++
 sweettooth/locale/en/LC_MESSAGES/django.po         | 255 ---------------------
 sweettooth/locale/en/LC_MESSAGES/djangojs.po       |  42 ----
 sweettooth/settings.py                             |   3 +-
 11 files changed, 214 insertions(+), 300 deletions(-)
---
diff --git a/README.rst b/README.rst
index 6e37028..40d06dc 100644
--- a/README.rst
+++ b/README.rst
@@ -99,6 +99,7 @@ Once you've done that, proceed with the database migrations:
 ::
 
   $ python manage.py migrate
+  $ python manage.py compilemessages
   $ python mange.py createsuperuser --username=joe --email=joe email com
 
 After above steps your database should be initialized and almost ready to run.
diff --git a/openshift/docker/Dockerfile b/openshift/docker/Dockerfile
index 7fec13d..376b633 100644
--- a/openshift/docker/Dockerfile
+++ b/openshift/docker/Dockerfile
@@ -7,6 +7,10 @@ ENV PYTHONUNBUFFERED=1 \
        GPG_KEY=08E2400FF7FE8FEDE3ACB52818147B073BAD2B07
 
 RUN set -ex \
+       && apt-get update \
+       && apt-get install --no-install-recommends --no-install-suggests -y \
+               gettext \
+       && rm -r /var/lib/apt/lists/* \
        && pip install Sphinx \
        && wget -O xapian-core.tar.xz 
"https://oligarchy.co.uk/xapian/$XAPIAN_VERSION/xapian-core-$XAPIAN_VERSION.tar.xz"; \
        && wget -O xapian-core.tar.xz.asc 
"https://oligarchy.co.uk/xapian/$XAPIAN_VERSION/xapian-core-$XAPIAN_VERSION.tar.xz.asc"; \
@@ -68,4 +72,5 @@ RUN set -ex \
        && chown www-data:root /extensions-web/wsgi.ini \
        && pip install -r requirements.txt \
        && pip install mysqlclient \
-       && pip install uWSGI
+       && pip install uWSGI \
+       && EGO_SECRET_KEY=- python manage.py compilemessages
diff --git a/requirements.txt b/requirements.txt
index dcdf708..bd47ef1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,7 @@ django-contrib-comments == 1.9.0
 django-registration == 2.5.2
 Pygments >= 1.4
 pillow >= 2.0.0
+polib >= 1.1.0
 chardet >= 2.2.1
 dj-database-url
 dj-email-url
diff --git a/sweettooth/core/__init__.py b/sweettooth/core/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/sweettooth/core/management/__init__.py b/sweettooth/core/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/sweettooth/core/management/commands/__init__.py b/sweettooth/core/management/commands/__init__.py
new file mode 100644
index 0000000..073c63b
--- /dev/null
+++ b/sweettooth/core/management/commands/__init__.py
@@ -0,0 +1,25 @@
+"""
+    GNOME Shell extensions repository
+    Copyright (C) 2019  Yuri Konotopov <ykonotopov gnome org>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+"""
+
+from django.conf import settings
+from pathlib import Path
+
+class MessagesCommand:
+    metadata_domain_prefix = 'extensions-web-domain-'
+    po_path = Path(settings.BASE_DIR).joinpath('po')
+    linguas_path = Path(po_path).joinpath('LINGUAS')
+    locale_path = Path(settings.SITE_ROOT).joinpath("locale")
+
+    def check_po_directory(self):
+        Path(self.po_path).mkdir(parents=True, exist_ok=True)
+        Path(self.linguas_path).touch(exist_ok=True)
+
+    def create_locale_directory(self):
+        self.locale_path.mkdir(parents=True, exist_ok=True)
diff --git a/sweettooth/core/management/commands/compilemessages.py 
b/sweettooth/core/management/commands/compilemessages.py
new file mode 100644
index 0000000..e65fba4
--- /dev/null
+++ b/sweettooth/core/management/commands/compilemessages.py
@@ -0,0 +1,55 @@
+"""
+    GNOME Shell extensions repository
+    Copyright (C) 2019  Yuri Konotopov <ykonotopov gnome org>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+"""
+
+from django.core.management.commands.compilemessages import Command as CompileMessagesCommand
+from sweettooth.core.management.commands import MessagesCommand
+from pathlib import Path
+import polib
+
+class Command(CompileMessagesCommand, MessagesCommand):
+    def handle(self, *args, **options):
+        self.check_po_directory()
+        self.create_locale_directory()
+        self.copy_translations()
+
+        super().handle(*args, **options)
+
+    def copy_translations(self):
+        with open(self.linguas_path, 'r') as file:
+            for line in file:
+                lang = line.strip()
+
+                po_path = Path(self.po_path, lang + '.po')
+                if not lang or not po_path.is_file():
+                    continue
+
+                self.stdout.write('processing language %s\n' % lang)
+                source = polib.pofile(po_path)
+                django = polib.POFile()
+                djangojs = polib.POFile()
+
+                django.metadata = source.metadata
+                djangojs.metadata = source.metadata
+
+                for entry in source:
+                    for occurrence, line in entry.occurrences:
+                        if occurrence.startswith(self.metadata_domain_prefix):
+                            domain = occurrence[len(self.metadata_domain_prefix):]
+
+                            if domain == 'django':
+                                django.append(entry)
+                            elif domain == 'djangojs':
+                                djangojs.append(entry)
+
+                lang_path = self.locale_path.joinpath(lang, 'LC_MESSAGES')
+                lang_path.mkdir(parents=True, exist_ok=True)
+
+                django.save(str(lang_path.joinpath("django.po")))
+                djangojs.save(str(lang_path.joinpath("djangojs.po")))
diff --git a/sweettooth/core/management/commands/makemessages.py 
b/sweettooth/core/management/commands/makemessages.py
new file mode 100644
index 0000000..0b92a04
--- /dev/null
+++ b/sweettooth/core/management/commands/makemessages.py
@@ -0,0 +1,125 @@
+"""
+    GNOME Shell extensions repository
+    Copyright (C) 2019  Yuri Konotopov <ykonotopov gnome org>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+"""
+
+from datetime import datetime
+from django.conf import settings
+from django.core.management.commands.makemessages import Command as MakeMessagesCommand
+from html.parser import HTMLParser
+from pathlib import Path
+import polib
+import pytz
+
+from sweettooth.core.management.commands import MessagesCommand
+
+class GettextParser(HTMLParser):
+    in_gettext = False
+    po = None
+    file = None
+
+    def __init__(self, po):
+        super().__init__(convert_charrefs=True)
+        self.po = po
+
+    def set_file(self, file):
+        self.file = file
+
+    def handle_starttag(self, tag, attrs):
+        if tag.lower() == 'x-gettext':
+            self.in_gettext = True
+        else:
+            self.in_gettext = False
+
+    def handle_endtag(self, tag):
+        self.in_gettext = False
+
+    def handle_data(self, data):
+        if self.in_gettext:
+            (line, offset) = self.getpos()
+            self.po.append(polib.POEntry(msgid=data, occurrences=[(self.file, line)]))
+
+
+class Command(MakeMessagesCommand, MessagesCommand):
+    def handle(self, *args, **options):
+        options['keep_pot'] = True
+
+        self.check_po_directory()
+        self.create_locale_directory()
+
+        for domain in ('django', 'djangojs'):
+            self.locale_paths = []
+            options['domain'] = domain
+            super().handle(*args, **options)
+
+        self.create_po_template()
+
+        for domain in ('django', 'djangojs'):
+            self.domain = domain
+            self.remove_potfiles()
+
+    def build_potfiles(self):
+        potfiles = super().build_potfiles()
+        if self.domain == 'djangojs':
+            if len(potfiles) > 1:
+                # We do not support multiple locale dirs
+                raise NotImplementedError("Multiple locale dirs are not supported")
+
+            po = polib.pofile(potfiles[0], check_for_duplicates = True)
+
+            self.search_mustache_texts(po)
+
+            po.sort()
+            po.save(potfiles[0])
+
+        return potfiles
+
+    def search_mustache_texts(self, po):
+        extensions = self.extensions
+        self.extensions = {'.mst'}
+
+        parser = GettextParser(po)
+        file_list = self.find_files(Path(settings.SITE_ROOT).joinpath('static', 'js'))
+        for translatable_file in file_list:
+            with open(translatable_file.path, 'r') as file:
+                parser.set_file(translatable_file.path.replace(settings.BASE_DIR, '').lstrip('/'))
+                parser.feed(file.read())
+                parser.reset()
+
+        self.extensions = extensions
+
+    def create_po_template(self):
+        django = polib.pofile(str(Path(self.locale_paths[0]).joinpath('django.pot')), check_for_duplicates = 
True)
+        djangojs = polib.pofile(str(Path(self.locale_paths[0]).joinpath('djangojs.pot')), 
check_for_duplicates = True)
+
+        self.add_po_domain(django, 'django')
+        self.add_po_domain(djangojs, 'djangojs')
+
+        for entry in djangojs:
+            django.append(entry)
+
+        django.header = "\nGNOME Shell extensions repository\n"
+        django.header += "\n"
+        django.header += "DO NOT EDIT!\n"
+        django.header += "This file is auto generated with manage.py makemessages."
+        django.header += "\n"
+
+        django.metadata = {
+            'Project-Id-Version': '1.0',
+            'Report-Msgid-Bugs-To': 'ykonotopov gnome org',
+            'POT-Creation-Date': datetime.now(pytz.utc).strftime('%Y-%m-%d %H:%M%z'),
+            'MIME-Version': '1.0',
+            'Content-Type': 'text/plain; charset=utf-8',
+            'Content-Transfer-Encoding': '8bit',
+        }
+        django.sort()
+        django.save(Path(self.po_path).joinpath("extensions-web.pot"))
+
+    def add_po_domain(self, po, domain):
+        for entry in po:
+            entry.occurrences.append((self.metadata_domain_prefix + domain, 1))
diff --git a/sweettooth/settings.py b/sweettooth/settings.py
index 44c7c3f..6a2f809 100644
--- a/sweettooth/settings.py
+++ b/sweettooth/settings.py
@@ -48,6 +48,7 @@ INSTALLED_APPS = (
 
     'sweettooth.extensions',
     'sweettooth.auth',
+    'sweettooth.core',
     'sweettooth.review',
     'sweettooth.errorreports',
     'sweettooth.templates',
@@ -110,8 +111,6 @@ DATABASES = {
 
 LANGUAGE_CODE = 'en-us'
 
-LOCALE_PATHS = [os.path.join(BASE_DIR, 'sweettooth', 'locale')]
-
 TIME_ZONE = 'UTC'
 
 USE_I18N = True


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]