Login

Django app/project/directory po messages collector and pofile bulder

Author:
sakkada
Posted:
February 9, 2016
Language:
Python
Version:
1.7
Score:
0 (after 0 ratings)

Some times if third party application is not translated competelly, developer have to extract and save all of i18n strings of third party application to current project, somewhere in root project directory (and configure additional LOCALE_DIRS entry) or in any project's applications (usually named "main" or same as project).
And after, create po file by makemessages, completely translate it and compile it to mo file.

This script allows you to extract all messages (msgid) from all po files and save it in python file in django format — _("Some i18n message"), and also compile complex po file, contained all msgids with related msgstrs of third party application.

Full example:
You have third party app, which is translated to russian, but not completely and names blog. It contains two locale dirs and have, for example, the following structure:

blog
..locale
....ru
......LC_MESSAGES
........django.mo
........django.po
..contrib
....comments
......locale
........ru
..........LC_MESSAGES
............django.mo
............django.po
......__init__.py
......models.py
..__init__.py
..models.py
..urls.py
..views.py

Each po file may contains any set of strings, which may be duplicated accross the application and any other po files, anyway result po file will be contain only unique msgids and msgstrs.
This application installed by pip into virtualenv lib directory and of cource you do not want change code here.
To translate this application in your project it is possible to copy all (unstanslated) strings to you project as ugettext entries (usually _("Some i18n string")), make po file, translate it and compile mo file.
So, after call this script (for example, let it be named pomessages.py), you will get two files, django.py which contains all i18n strings and django.po which contains all msgids (i18n strings) with related translations.

pomessages.py path/to/third-party-app/root locale [project-name:default is empty] [filename:default=django]

In this case:
pomessages.py path/to/blog ru blog django

After script will be finished, in blog directory will be added two files, django.po and django.mo.
django.py content:

...
# blog:admin (source:ru) directory translations
# ----------------------------------------------
_("Category") # blog:models.py:10, blog:views.py:15
_("Article") # blog:models.py:20

# blog:category/admin (source:ru) directory translations
# -------------------------------------------------------
_("Add Category") # blog:category/admin/templates/admin/create.html:10
_("Edit Category") # blog:category/admin/templates/admin/change.html:20
...

django.mo content:

...
#
msgid ""
msgstr ""
"Project-Id-Version: Blog\n"
"POT-Creation-Date: 2016-02-08 12:00+0000\n"
"PO-Revision-Date: 2016-02-08 18:00+0000\n"

#: path/to/file.py:10
msgid "Category"
msgstr "Категория"

#: path/to/file.py:20
msgid "Article"
msgstr ""
...

After you just need to place py file anywhere in your project and place po file to the following locale directory (or merge with existing po file if it exists), run:
django-admin makemessages -lru to fix po file (remake it), translate missing entries and run:
django-admin compilemessages, reload project and you will have translated third party application.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/usr/bin/python
import sys
import os
import polib
import json
import cStringIO
import collections

# processing arguments
# ---------------------
# pomessages.py app/root locale [project:default is empty] [filename:default=django]
# pomessages.py third/party/app/root ru projectname destinationfile

# path - root of translatable project
path = len(sys.argv) > 1 and sys.argv[1] or None
path = path and os.path.abspath(path) or None

# locale to extract from
locale = len(sys.argv) > 2 and sys.argv[2] or None

# project name
project = len(sys.argv) > 3 and sys.argv[3] or ''
project = project and '%s:' % project

# project name
filename = len(sys.argv) > 4 and sys.argv[4] or 'django'

# io - content of python file, po - po file
io = cStringIO.StringIO()
po = polib.POFile()


if not path or not locale:
    exit('Root path not specified.')

registry = collections.OrderedDict()
for root, dirs, files in os.walk(path):
    for file in files:
        if not file.endswith('.po'):
            continue

        realroot = os.path.abspath(os.path.join(root, '../../..')).replace('\\', '/')
        pofile = os.path.abspath(os.path.join(root, file)).replace('\\', '/')
        related = os.path.relpath(realroot, path).replace('\\', '/')
        curloc = os.path.relpath(pofile, realroot).replace('\\', '/').split('/')[1]

        if not curloc == locale:
            continue

        # get po file
        posource = polib.pofile(pofile)
        if not po.metadata and posource.metadata:
            po.metadata = posource.metadata

        # generate result
        # directory title
        _ = '\n\n# %s%s (source:%s) directory translations' % (project, related, curloc)
        io.write(_)
        io.write('\n# %s' % ('-' * (len(_)-3)))

        for elem in posource:
            # add entry to result python file
            io.write('\n_(%s) # %s' % (
                json.dumps(elem.msgid),
                ', '.join(['%s%s:%s' % (project, os.path.join(related, source).replace('\\', '/'), line,)
                           for source, line in elem.occurrences]),
            ))

            # add entry to po registry
            if (elem.msgid not in registry or
                elem.msgstr and not registry.get(elem.msgid).msgstr):
                registry.update(**{elem.msgid: elem,})

# write python polines file
with open(os.path.join(path, '%s.py' % filename), 'w') as handler:
    io.seek(0)
    handler.write(io.read())
    io.close()

# write po file
if len(registry):
    for elem in registry.values():
        po.append(elem)
    po.save(os.path.join(path, '%s.po' % filename))

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 3 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 1 week ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 11 months ago
  5. Help text hyperlinks by sa2812 11 months, 3 weeks ago

Comments

Please login first before commenting.