svghmi/i18n.py
changeset 3919 0ce4b50d1182
parent 3918 9f0ef23569cb
equal deleted inserted replaced
3918:9f0ef23569cb 3919:0ce4b50d1182
    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:
   367                     'Syntax error on %s:%d' % (infile, lno) + 'before:\n %s'%l)
   377                     'Syntax error on %s:%d' % (infile, lno) + 'before:\n %s'%l)
   368         # Add last entry
   378         # Add last entry
   369         if section == STR:
   379         if section == STR:
   370             self.add(msgctxt, msgid, msgstr, fuzzy)
   380             self.add(msgctxt, msgid, msgstr, fuzzy)
   371 
   381 
   372 
   382         return self.__messages
   373 
   383 
       
   384 
       
   385