-
Notifications
You must be signed in to change notification settings - Fork 5
/
djangopo.py
262 lines (192 loc) · 6.63 KB
/
djangopo.py
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
"""A tool to work with language trans files (.po) in a django project
Author: Vineet Naik <[email protected]>, <[email protected]>
This script is a utility for working with .po files in a django
project in which many languages are to be supported and so for any new
text introduced the translation files for all languages are to be
updated.
To get translated strings, it uses the mymemory api, more info about
the same can be obtained from here -
http://mymemory.translated.net/doc/spec.php
"""
import os
import re
from fnmatch import fnmatch
from itertools import groupby
from functools import wraps
import requests
import polib
# global, expected to be specified in sys.argv
basedir = None
# edit this before running the code
mymemory_email = None
_commands = set()
lang_code_pattern = re.compile(r'locale/([a-z_A-Z]+)/LC_MESSAGES')
def find_pofile_paths(basedir):
pofiles = []
for root, dirs, files in os.walk(basedir):
pofiles.extend(os.path.join(root, f) for f in files if fnmatch(f, '*.po'))
return pofiles
def lang_code_from_path(fpath):
m = lang_code_pattern.search(fpath)
if m is not None:
return m.groups()[0].lower().replace('_', '-')
else:
raise Exception('Not a locale path')
### Code for listing various types of po entries to stdout
def write(grouped_entries):
for lang, entries in grouped_entries:
if lang != 'en':
print
print('=== %s ===' % lang)
print
for entry in entries:
print entry
print
def groupby_lang(func):
@wraps(func)
def dec(*args, **kwargs):
f = lambda e: e.lang_code
return groupby(sorted(func(*args, **kwargs), key=f), f)
return dec
def entries(func):
@wraps(func)
def dec():
global basedir
pofiles = (polib.pofile(f) for f in find_pofile_paths(basedir))
return get_entries(pofiles, func);
return dec
def command(func):
global _commands
_commands.add((func.__name__, func.__doc__))
return func
class WrappedPOEntry(object):
def __init__(self, po_entry, fpath):
self._po_entry = po_entry
self.fpath = fpath
@property
def lang_code(self):
return lang_code_from_path(self.fpath)
def __getattr__(self, attr):
return getattr(self._po_entry, attr)
def set_trans(self, trans):
self._po_entry.msgstr = trans
def __unicode__(self):
values = {'flags': ','.join(self.flags),
'fpath': self.fpath,
'msgid': self.msgid,
'msgstr': self.msgstr if self.msgstr else '<???>'}
return '\n'.join(['[%(flags)s]',
'[File]: %(fpath)s',
'[Orig]: %(msgid)s',
'[Trans]: %(msgstr)s']) % values
def __str__(self):
return self.__unicode__().encode('utf-8')
@groupby_lang
def get_entries(pofiles, pred=None):
return (WrappedPOEntry(e, po.fpath)
for po in pofiles
for e in po
if pred is not None and pred(e))
def by_fext(entry, fext):
return any(os.path.splitext(o[0])[1] == fext for o in entry.occurrences)
@entries
def get_non_translated(entry):
return not entry.translated() and entry.obsolete != 1
@entries
def get_non_translated_non_fuzzy(entry):
return not entry.translated() and entry.obsolete != 1 and 'fuzzy' not in entry.flags
@entries
def get_obsolete(entry):
return entry.obsolete == 1
@entries
def get_py_text(entry):
return by_fext(entry, '.py')
@entries
def get_js_text(entry):
return by_fext(entry, '.js')
@entries
def get_html_text(entry):
return by_fext(entry, '.js')
@command
def list_non_translated():
write(get_non_translated())
@command
def list_non_translated_non_fuzzy():
write(get_non_translated_non_fuzzy())
@command
def list_obsolete():
write(get_obsolete())
### Code for getting translations for text from MyMemory
### http://mymemory.translated.net/doc/spec.php
class MyMemoryTransError(Exception):
pass
def mymemory_translate(msgid, to_lang, from_lang='en'):
assert mymemory_email is not None, 'Please provide an email to use mymemory api'
req = requests.get('http://api.mymemory.translated.net/get',
params={'q': msgid,
'langpair': '%s|%s' % (from_lang, to_lang),
'de': mymemory_email})
if req.status_code == 200:
return req.json['responseData']['translatedText']
else:
raise MyMemoryTransError('Request to translate %r failed with status code %d' % (msgid, req.status_code))
@command
def translate_non_translated_non_fuzzy():
entries = get_non_translated_non_fuzzy()
for lang, entries in entries:
if lang != 'en':
print
print('=== %s ===' % lang)
print
for entry in entries:
try:
trans = mymemory_translate(entry.msgid, to_lang=lang)
entry.set_trans(trans)
except (MyMemoryTransError, requests.exceptions.RequestException) as e:
print e
finally:
print entry
print
### Code for writing to the po files. To be used with care.
@command
def remove_fuzzy_flags():
pofiles = (polib.pofile(f) for f in find_pofile_paths(basedir))
for po in pofiles:
num_fuzzy = 0
for entry in po.fuzzy_entries():
num_fuzzy += 1
entry.flags.remove('fuzzy')
po.save()
if num_fuzzy > 0:
print '%d fuzzy flags removed from %s' % (num_fuzzy, po.fpath)
print 'DONE!'
@command
def translate_new_and_save():
pofiles = (polib.pofile(f) for f in find_pofile_paths(basedir))
for po in pofiles:
num = 0
lang = lang_code_from_path(po.fpath)
if lang == 'en':
continue
for entry in po.untranslated_entries():
try:
trans = mymemory_translate(entry.msgid, to_lang=lang)
except (MyMemoryTransError, requests.exceptions.RequestException) as e:
print e
else:
num += 1
entry.msgstr = trans
po.save()
if num > 0:
print '%d new translations added to %s' % (num, po.fpath)
print 'DONE!'
def remove_obsolete():
pass # how to do this using polib?
if __name__ == '__main__':
import sys
script, subcommand, basedir, email = sys.argv
mymemory_email = email
if locals().get(subcommand) is not None:
locals().get(subcommand)()
else:
print('Command %s not defined' % subcommand)