Merge branch 'reflow'
[wolnelektury.git] / src / wolnelektury / management / commands / translation2po.py
1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
4 #
5 import os
6 import time
7 from optparse import make_option
8 from django.conf import settings
9 from django.core.management.base import BaseCommand
10 from django.db import models
11
12 import polib
13 from modeltranslation.translator import translator, NotRegistered
14
15 from wolnelektury.utils import makedirs
16
17
18 def metadata(language=''):
19     """get metadata for PO, given language code"""
20     t = time.strftime('%Y-%m-%d %H:%M%z')
21
22     return {
23         'Project-Id-Version': '1.0',
24         'Report-Msgid-Bugs-To': settings.CONTACT_EMAIL,
25         'POT-Creation-Date': '%s' % t,
26         'PO-Revision-Date': '%s' % t,
27         'Last-Translator': 'you <you@example.com>',
28         'Language-Team': '%s' % dict(settings.LANGUAGES).get(language, language),
29         'MIME-Version': '1.0',
30         'Content-Type': 'text/plain; charset=utf-8',
31         'Content-Transfer-Encoding': '8bit',
32         }
33
34
35 def make_po(language=''):
36     """Create new POFile object for language code"""
37     po = polib.POFile()
38     po.metadata = metadata(language)
39     return po
40
41
42 def get_languages(langs):
43     if not langs:
44         return settings.LANGUAGES
45     langs = langs.split(',')
46     lm = dict(settings.LANGUAGES)
47     return map(lambda l: (l, lm.get(l, l)), langs)
48
49
50 class Command(BaseCommand):
51     option_list = BaseCommand.option_list + (
52         make_option('-d', '--directory', help='Specify which directory should hold generated PO files',
53                     dest='directory', default=''),
54         make_option('-l', '--load', help='load locales back to source', action='store_true', dest='load',
55                     default=False),
56         make_option('-L', '--language', help='locales to load', dest='lang', default=None),
57         make_option('-n', '--poname', help='name of the po file [no extension]', dest='poname', default=None),
58         make_option('-k', '--keep-running', help='keep running even when missing an input file', dest='keep_running',
59                     default=False, action='store_true'),
60     )
61     help = 'Export models from app to po files'
62     args = 'app'
63
64     def get_models(self, app):
65         for mdname in dir(app.models):
66             if mdname[0] == '_':
67                 continue
68             md = getattr(app.models, mdname)
69             try:
70                 assert issubclass(md, models.Model)
71             except (AssertionError, TypeError):
72                 continue
73
74             try:
75                 opts = translator.get_options_for_model(md)
76             except NotRegistered:
77                 continue
78             else:
79                 yield (md, opts)
80
81     def handle(self, appname, **options):
82         if not options['poname']:
83             options['poname'] = appname
84         app = __import__(appname)
85
86         if options['load']:
87             objects = {}
88             modmod = {}
89             for md, opts in self.get_models(app):
90                 if md.__name__ not in objects:
91                     objects[md.__name__] = {}
92                     modmod['model'] = md
93
94             languages = get_languages(options['lang'])
95
96             for lng in zip(*languages)[0]:
97                 pofile = os.path.join(options['directory'], lng, options['poname'] + '.po')
98                 if not os.path.exists(pofile):
99                     if options['keep_running']:
100                         continue
101                     else:
102                         raise OSError('%s po file: %s not found' % (appname, pofile))
103                 po = polib.pofile(pofile)
104                 for entry in po:
105                     loc, _ignored = entry.occurrences[0]
106                     _appname, modelname, fieldname, pk = loc.split('/')
107                     try:
108                         obj = objects[modelname][pk]
109                     except KeyError:
110                         obj = modmod['model'].objects.get(pk=pk)
111                         objects[modelname][pk] = obj
112                     setattr(obj, fieldname, entry.msgstr)
113
114             for mod, objcs in objects.items():
115                 for o in objcs.values():
116                     o.save()
117
118         else:
119             pofiles = {}
120             for md, opts in self.get_models(app):
121                 for obj in md.objects.all().order_by('pk'):
122                     for fld, locflds in opts.local_fields.items():
123                         k = getattr(obj, '%s_%s' % (fld, settings.LANGUAGE_CODE)) or ''
124                         for locfld in locflds:
125                             cur_lang = locfld.language
126                             try:
127                                 po = pofiles[cur_lang]
128                             except KeyError:
129                                 po = make_po(cur_lang)
130                                 pofiles[cur_lang] = po
131                             v = locfld.value_from_object(obj) or ''
132                             entry = polib.POEntry(
133                                 msgid=k,
134                                 msgstr=v,
135                                 occurrences=[('%s/%s/%s/%s' % (appname, md.__name__, locfld.name, str(obj.pk)), 0)])
136                             po.append(entry)
137
138             directory = options['directory']
139             for lng, po in pofiles.items():
140                 makedirs(os.path.join(directory, lng))
141                 po.save(os.path.join(directory, lng, '%s.po' % options['poname']))