25 |
25 |
26 def open_pofile(pofile): |
26 def open_pofile(pofile): |
27 """ Opens PO file with POEdit """ |
27 """ Opens PO file with POEdit """ |
28 |
28 |
29 if sys.platform.startswith('win'): |
29 if sys.platform.startswith('win'): |
30 from six.moves import winreg |
30 import winreg |
31 poedit_cmd = None |
31 poedit_cmd = None |
32 try: |
32 try: |
33 poedit_cmd = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, |
33 poedit_cmd = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, |
34 'SOFTWARE\\Classes\\poedit\\shell\\open\\command') |
34 'SOFTWARE\\Classes\\poedit\\shell\\open\\command') |
35 cmd = re.findall(cmd_parser, poedit_cmd) |
35 cmd = re.findall(cmd_parser, poedit_cmd) |
87 def ReadTranslations(dirpath): |
87 def ReadTranslations(dirpath): |
88 """ Read all PO files from a directory and return a list of (langcode, translation_dict) tuples """ |
88 """ Read all PO files from a directory and return a list of (langcode, translation_dict) tuples """ |
89 |
89 |
90 translations = [] |
90 translations = [] |
91 for translation_name, po_path in GetPoFiles(dirpath): |
91 for translation_name, po_path in GetPoFiles(dirpath): |
92 r = POReader() |
92 messages = POReader().read(po_path) |
93 r.read(po_path) |
93 translations.append((translation_name, messages)) |
94 translations.append((translation_name, r.get_messages())) |
|
95 return translations |
94 return translations |
96 |
95 |
97 def MatchTranslations(translations, messages, errcallback): |
96 def MatchTranslations(translations, messages, errcallback): |
98 """ |
97 """ |
99 Matches translations against original message catalog, |
98 Matches translations against original message catalog, |
100 warn about inconsistancies, |
99 warn about inconsistancies, |
101 returns list of langs, and a list of (msgid, [translations]) tuples |
100 returns list of langs, and a list of (msgid, [translations]) tuples |
102 """ |
101 """ |
103 translated_messages = [] |
102 translated_messages = [] |
104 broken_lang = set() |
103 broken_lang = set() |
|
104 incomplete_lang = set() |
105 for msgid,label,svgid in messages: |
105 for msgid,label,svgid in messages: |
106 translated_message = [] |
106 translated_message = [] |
107 for langcode,translation in translations: |
107 for langcode,translation in translations: |
108 msg = translation.pop(msgid, None) |
108 msg = translation.pop(msgid, None) |
109 if msg is None: |
109 if msg is None: |
|
110 # Missing translation (msgid not in .po) |
110 broken_lang.add(langcode) |
111 broken_lang.add(langcode) |
111 errcallback(_('{}: Missing translation for "{}" (label:{}, id:{})\n').format(langcode,msgid,label,svgid)) |
112 msg = msgid |
112 translated_message.append(msgid) |
113 elif msg == b"": |
113 else: |
114 # Empty translation (msgid is in .po) |
114 translated_message.append(msg) |
115 incomplete_lang.add(langcode) |
|
116 msg = msgid |
|
117 |
|
118 translated_message.append(msg) |
115 translated_messages.append((msgid,translated_message)) |
119 translated_messages.append((msgid,translated_message)) |
116 langs = [] |
120 langs = [] |
117 for langcode,translation in translations: |
121 for langcode,translation in translations: |
118 try: |
122 try: |
119 l,c = langcode.split("_") |
123 l,c = langcode.split("_") |
127 langname = langcode |
131 langname = langcode |
128 |
132 |
129 langs.append((langname,langcode)) |
133 langs.append((langname,langcode)) |
130 |
134 |
131 broken = False |
135 broken = False |
132 for msgid, msg in translation.items(): |
136 if translation: |
133 broken = True |
137 broken = True |
134 errcallback(_('{}: Unused translation "{}":"{}"\n').format(langcode,msgid,msg)) |
|
135 if broken or langcode in broken_lang: |
138 if broken or langcode in broken_lang: |
136 errcallback(_('Translation for {} is outdated, please edit {}.po, click "Catalog -> Update from POT File..." and select messages.pot.\n').format(langcode,langcode)) |
139 errcallback(( |
|
140 _('Translation for {} is outdated and incomplete.') |
|
141 if langcode in incomplete_lang else |
|
142 _('Translation for {} is outdated.')).format(langcode) + |
|
143 " "+_('Edit {}.po, click "Catalog -> Update from POT File..." and select messages.pot.\n').format(langcode)) |
|
144 elif langcode in incomplete_lang: |
|
145 errcallback(_('Translation for {} is incomplete. Edit {}.po to complete it.\n').format(langcode,langcode)) |
137 |
146 |
138 |
147 |
139 return langs,translated_messages |
148 return langs,translated_messages |
140 |
149 |
141 |
150 |
263 |
272 |
264 class POReader: |
273 class POReader: |
265 def __init__(self): |
274 def __init__(self): |
266 self.__messages = {} |
275 self.__messages = {} |
267 |
276 |
268 def get_messages(self): |
|
269 return self.__messages |
|
270 |
|
271 def add(self, ctxt, msgid, msgstr, fuzzy): |
277 def add(self, ctxt, msgid, msgstr, fuzzy): |
272 "Add a non-fuzzy translation to the dictionary." |
278 "Add eventually empty translation to the dictionary." |
273 if not fuzzy and msgstr and msgid: |
279 if msgid: |
274 if ctxt is None: |
280 self.__messages[msgid if ctxt is None else (b"%b\x04%b" % (ctxt, id))] = msgstr |
275 self.__messages[msgid] = msgstr |
|
276 else: |
|
277 self.__messages[b"%b\x04%b" % (ctxt, id)] = str |
|
278 |
281 |
279 def read(self, infile): |
282 def read(self, infile): |
280 ID = 1 |
283 ID = 1 |
281 STR = 2 |
284 STR = 2 |
282 CTXT = 3 |
285 CTXT = 3 |
283 |
286 |
284 |
287 self.__messages = {} |
285 with open(infile, 'rb') as f: |
288 |
286 lines = f.readlines() |
289 try: |
|
290 with open(infile, 'rb') as f: |
|
291 lines = f.readlines() |
|
292 except Exception as e: |
|
293 raise UserAddressedException( |
|
294 'Cannot open PO translation file :%s' % str(e)) |
287 |
295 |
288 section = msgctxt = None |
296 section = msgctxt = None |
289 fuzzy = 0 |
297 fuzzy = 0 |
290 |
298 |
291 # Start off assuming Latin-1, so everything decodes without failure, |
299 # Start off assuming Latin-1, so everything decodes without failure, |
310 continue |
318 continue |
311 # Now we are in a msgid or msgctxt section, output previous section |
319 # Now we are in a msgid or msgctxt section, output previous section |
312 if l.startswith('msgctxt'): |
320 if l.startswith('msgctxt'): |
313 if section == STR: |
321 if section == STR: |
314 self.add(msgctxt, msgid, msgstr, fuzzy) |
322 self.add(msgctxt, msgid, msgstr, fuzzy) |
|
323 fuzzy = 0 |
315 section = CTXT |
324 section = CTXT |
316 l = l[7:] |
325 l = l[7:] |
317 msgctxt = b'' |
326 msgctxt = b'' |
318 elif l.startswith('msgid') and not l.startswith('msgid_plural'): |
327 elif l.startswith('msgid') and not l.startswith('msgid_plural'): |
319 if section == STR: |
328 if section == STR: |
320 self.add(msgctxt, msgid, msgstr, fuzzy) |
329 self.add(msgctxt, msgid, msgstr, fuzzy) |
|
330 fuzzy = 0 |
321 if not msgid: |
331 if not msgid: |
322 # See whether there is an encoding declaration |
332 # See whether there is an encoding declaration |
323 p = HeaderParser() |
333 p = HeaderParser() |
324 charset = p.parsestr(msgstr.decode(encoding)).get_content_charset() |
334 charset = p.parsestr(msgstr.decode(encoding)).get_content_charset() |
325 if charset: |
335 if charset: |