SVGHMI: i18n: now loads PO filesand match translation against catalog. Refactored a bit to move i18n related code in i18n.py svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Thu, 21 Jan 2021 05:04:23 +0100
branchsvghmi
changeset 3113 18133b90196e
parent 3112 bd20f9112014
child 3114 fb1e320836e8
SVGHMI: i18n: now loads PO filesand match translation against catalog. Refactored a bit to move i18n related code in i18n.py
svghmi/i18n.py
svghmi/svghmi.py
--- a/svghmi/i18n.py	Tue Jan 19 11:57:13 2021 +0100
+++ b/svghmi/i18n.py	Thu Jan 21 05:04:23 2021 +0100
@@ -7,9 +7,11 @@
 # See COPYING file for copyrights details.
 
 from __future__ import absolute_import
+import os
 import sys
 import subprocess
 import time
+import ast
 import wx
 
 def open_pofile(pofile):
@@ -36,6 +38,66 @@
     else:
         subprocess.Popen([poedit_path,pofile])
 
+def EtreeToMessages(msgs):
+    """ Converts XML tree from 'extract_i18n' templates into a list of tuples """
+    messages = []
+
+    for msg in msgs:
+        messages.append((
+            "\n".join([line.text.encode("utf-8") for line in msg]),
+            msg.get("label"), msg.get("id")))
+
+    return messages
+
+def SaveCatalog(fname, messages):
+    """ Save messages given as list of tupple (msg,label,id) in POT file """
+    w = POTWriter()
+    w.ImportMessages(messages)
+
+    with open(fname, 'w') as POT_file:
+        w.write(POT_file)
+    
+def ReadTranslations(dirpath):
+    """ Read all PO files from a directory and return a list of (lang, translation_dict) tuples """
+
+    po_files = [fname for fname in os.listdir(dirpath) if fname.endswith(".po")]
+
+    translations = []
+    for po_fname in po_files:
+        r = POReader()
+        with open(os.path.join(dirpath, po_fname), 'r') as PO_file:
+            r.read(PO_file)
+            translations.append((po_fname[:-3], r.get_messages()))
+    return translations
+
+def MatchTranslations(translations, messages, errcallback):
+    """ 
+    Matches translations against original message catalog, 
+    warn about inconsistancies, 
+    returns list of langs, and a list of (msgid, [translations]) tuples 
+    """
+    translated_messages = []
+    for msgid,label,svgid in messages:
+        translated_message = []
+        for lang,translation in translations:
+            msg = translation.pop(msgid, None)
+            if msg is None:
+                errcallback(_('{}: Missing translation for "{}" (label:{}, id:{})').format(lang,msgid,label,svgid))
+            translated_message.append(msg)
+        translated_messages.append((msgid,translated_message))
+    langs = []
+    for lang,translation in translations:
+        langs.append(lang)
+        for msgid, msg in translation.iteritems():
+            errcallback(_('{}: Unused translation "{}":"{}"').format(lang,msgid,msg))
+
+    return langs,translated_messages
+
+        
+def TranslationToEtree(langs,translated_messages):
+    pass
+    
+
 locpfx = '#:svghmi.svg:'
 
 pot_header = '''\
@@ -51,8 +113,8 @@
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
 "Language-Team: LANGUAGE <LL@li.org>\\n"
 "MIME-Version: 1.0\\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
 "Generated-By: SVGHMI 1.0\\n"
 
 '''
@@ -111,11 +173,10 @@
 
     def ImportMessages(self, msgs):
         for msg in msgs:
-            self.addentry("\n".join([line.text.encode("utf-8") for line in msg]), msg.get("label"), msg.get("id"))
+            self.addentry(*msg)
 
     def addentry(self, msg, label, svgid):
         entry = (label, svgid)
-        print(entry)
         self.__messages.setdefault(msg, set()).add(entry)
 
     def write(self, fp):
@@ -153,7 +214,10 @@
     def __init__(self):
         self.__messages = {}
 
-    def add(msgid, msgstr, fuzzy):
+    def get_messages(self):
+        return self.__messages
+
+    def add(self, msgid, msgstr, fuzzy):
         "Add a non-fuzzy translation to the dictionary."
         if not fuzzy and msgstr:
             self.__messages[msgid] = msgstr
--- a/svghmi/svghmi.py	Tue Jan 19 11:57:13 2021 +0100
+++ b/svghmi/svghmi.py	Thu Jan 21 05:04:23 2021 +0100
@@ -30,7 +30,7 @@
 import targets
 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
 from XSLTransform import XSLTransform
-from svghmi.i18n import POTWriter, POReader, open_pofile
+from svghmi.i18n import EtreeToMessages, SaveCatalog, ReadTranslations, MatchTranslations, TranslationToEtree
 
 HMI_TYPES_DESC = {
     "HMI_NODE":{},
@@ -530,18 +530,18 @@
         return res
 
     def GetTranslations(self, _context, msgs):
-
-        w = POTWriter()
-        w.ImportMessages(msgs)
-
-        with open(self._getPOTpath(), 'w') as POT_file:
-            w.write(POT_file)
-
-        # XXX scan existing PO files 
-        # XXX read PO files
-
-        r = POReader()
-        return None # XXX return all langs from all POs
+        messages = EtreeToMessages(msgs)
+
+        SaveCatalog(self._getPOTpath(), messages)
+
+        translations = ReadTranslations(self.CTNPath())
+            
+        langs,translated_messages = MatchTranslations(translations, messages, 
+            errcallback=self.GetCTRoot().logger.write_warning)
+
+        print(langs,translated_messages)
+
+        return TranslationToEtree(langs,translated_messages)
 
     def CTNGenerate_C(self, buildpath, locations):
 
@@ -699,7 +699,7 @@
         if dialog.ShowModal() == wx.ID_OK:
             POFile = dialog.GetPath()
             if os.path.isfile(POFile):
-                if os.path.dirname(POFile) == project_path:
+                if os.path.relpath(POFile, project_path) == os.path.basename(POFile):
                     self._StartPOEdit(POFile)
                 else:
                     self.GetCTRoot().logger.write_error(_("PO file misplaced: %s is not in %s\n") % (POFile,project_path))