OSDN Git Service

Add auto-reload modified files option (#1611)
[winmerge-jp/winmerge-jp.git] / Translations / GetTranslationsStatus.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-;
3
4 # The MIT License
5
6 # Copyright (c) 2009-2022 Tim Gerundt <tim@gerundt.de>
7
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to deal
10 # in the Software without restriction, including without limitation the rights
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 # copies of the Software, and to permit persons to whom the Software is
13 # furnished to do so, subject to the following conditions:
14
15 # The above copyright notice and this permission notice shall be included in
16 # all copies or substantial portions of the Software.
17
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 # THE SOFTWARE.
25
26 # Python script to get the status of the translations
27
28 import os
29 import os.path
30 import string
31 import re
32 import time
33 import codecs
34 import math
35 import argparse
36
37 class TranslationsStatus(object):
38     def __init__(self):
39         self._projects = []
40     
41     @property
42     def projects(self):
43         return self._projects
44     
45     @property
46     def languages(self):
47         ''' Return a list with all languages '''
48         temp = []
49         for project in self._projects: #For all projects...
50             for language in project.languages: #For all languages...
51                 if language not in temp: #If language NOT in list...
52                     temp.append(language)
53         temp.sort()
54         return temp
55     
56     @property
57     def noneTemplateLanguages(self):
58         ''' Return a list with all NONE template languages '''
59         temp = []
60         for project in self._projects: #For all projects...
61             for language in project.noneTemplateLanguages: #For all NONE template languages...
62                 if language not in temp: #If language NOT in list...
63                     temp.append(language)
64         temp.sort()
65         return temp
66     
67     def clear(self):
68         self._projects = []
69     
70     def addProject(self, project):
71         self._projects.append(project)
72     
73     def writeToXmlFile(self, xmlpath):
74         xmlfile = codecs.open(xmlpath, 'w', 'utf-8')
75         xmlfile.write('<?xml version="1.0" encoding="UTF-8"?>\n')
76         xmlfile.write('<status>\n')
77         xmlfile.write('  <update>%s</update>\n' % (time.strftime('%Y-%m-%d')))
78         for project in self._projects: #For all projects...
79             xmlfile.write('  <translations project="%s">\n' % (project.name))
80             for language in project.languages: #For all (sorted) languages...
81                 status1 = project[language]
82                 if status1.template: #If a template file...
83                     xmlfile.write('    <translation template="1">\n')
84                     xmlfile.write('      <language>%s</language>\n' % (status1.language))
85                     xmlfile.write('      <file>%s</file>\n' % (status1.filename))
86                     xmlfile.write('      <update>%s</update>\n' % (status1.updatedate[0:10]))
87                     xmlfile.write('      <strings>\n')
88                     xmlfile.write('        <count>%u</count>\n' % (status1.count))
89                     xmlfile.write('        <translated>%u</translated>\n' % (status1.count))
90                     xmlfile.write('        <fuzzy>0</fuzzy>\n')
91                     xmlfile.write('        <untranslated>0</untranslated>\n')
92                     xmlfile.write('      </strings>\n')
93                     xmlfile.write('    </translation>\n')
94                 else: #If NOT a template file...
95                     xmlfile.write('    <translation>\n')
96                     xmlfile.write('      <language>%s</language>\n' % (status1.language))
97                     xmlfile.write('      <file>%s</file>\n' % (status1.filename))
98                     xmlfile.write('      <update>%s</update>\n' % (status1.updatedate[0:10]))
99                     xmlfile.write('      <strings>\n')
100                     xmlfile.write('        <count>%u</count>\n' % (status1.count))
101                     xmlfile.write('        <translated>%u</translated>\n' % (status1.translated))
102                     xmlfile.write('        <fuzzy>%u</fuzzy>\n' % (status1.fuzzy))
103                     xmlfile.write('        <untranslated>%u</untranslated>\n' % (status1.untranslated))
104                     xmlfile.write('      </strings>\n')
105                     if status1.translators: #If translators exists...
106                         xmlfile.write('      <translators>\n')
107                         for translator in status1.translators: #For all translators...
108                             if (translator.ismaintainer): #If maintainer...
109                                 xmlfile.write('        <translator maintainer="1">\n')
110                             else: #If NOT maintainer...
111                                 xmlfile.write('        <translator>\n')
112                             xmlfile.write('          <name>%s</name>\n' % (translator.name))
113                             if (translator.mail): #If mail address exists...
114                                 xmlfile.write('          <mail>%s</mail>\n' % (translator.mail))
115                             xmlfile.write('        </translator>\n')
116                         xmlfile.write('      </translators>\n')
117                     xmlfile.write('    </translation>\n')
118             xmlfile.write('  </translations>\n')
119         xmlfile.write('</status>\n')
120         xmlfile.close()
121     
122     def writeToHtmlFile(self, htmlpath):
123         htmlfile = codecs.open(htmlpath, 'w', 'utf-8')
124         
125         htmlfile.write('<!DOCTYPE html>\n')
126         htmlfile.write('<html lang="en">\n')
127         htmlfile.write('<head>\n')
128         htmlfile.write('  <title>Translations Status</title>\n')
129         htmlfile.write('  <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n')
130         htmlfile.write('  <style>\n')
131         htmlfile.write('  <!--\n')
132         htmlfile.write('    body {\n')
133         htmlfile.write('      font-family: Calibri,Helvetica,Arial,sans-serif;\n')
134         htmlfile.write('    }\n')
135         htmlfile.write('    h1, h2, h3, h4, h5, h6 {\n')
136         htmlfile.write('      font-family: Cambria,"Times New Roman",Times,serif;\n')
137         htmlfile.write('    }\n')
138         htmlfile.write('    .status {\n')
139         htmlfile.write('      border-collapse: collapse;\n')
140         htmlfile.write('      border: 1px solid #d2d2d2;\n')
141         htmlfile.write('    }\n')
142         htmlfile.write('    .status th, .status td {\n')
143         htmlfile.write('      padding: .3em;\n')
144         htmlfile.write('      border: 1px solid #d2d2d2;\n')
145         htmlfile.write('    }\n')
146         htmlfile.write('    .status th {\n')
147         htmlfile.write('      background: #f2f2f2;\n')
148         htmlfile.write('    }\n')
149         htmlfile.write('    .status tr:nth-child(odd) {\n')
150         htmlfile.write('      background: #f9f9f9;\n')
151         htmlfile.write('    }\n')
152         htmlfile.write('    .left { text-align: left; }\n')
153         htmlfile.write('    .center { text-align: center; }\n')
154         htmlfile.write('    .right { text-align: right; }\n')
155         htmlfile.write('\n')
156         htmlfile.write('    .translated { color: #2D802B; }\n')
157         htmlfile.write('    .fuzzy { color: #05359B; }\n')
158         htmlfile.write('    .untranslated { color: #D42323; }\n')
159         htmlfile.write('  -->\n')
160         htmlfile.write('  </style>\n')
161         htmlfile.write('</head>\n')
162         htmlfile.write('<body>\n')
163         htmlfile.write('<h1>Translations Status</h1>\n')
164         htmlfile.write('<p>Status from <strong>%s</strong>:</p>\n' % (time.strftime('%Y-%m-%d')))
165         for project in self._projects: #For all projects...
166             htmlfile.write('<h2>%s</h2>\n' % (project.name))
167             htmlfile.write('<table class="status">\n')
168             htmlfile.write('  <tr>\n')
169             htmlfile.write('    <th class="left">Language</th>\n')
170             htmlfile.write('    <th class="right">Total</th>\n')
171             htmlfile.write('    <th class="right translated">Translated</th>\n')
172             htmlfile.write('    <th class="right fuzzy">Fuzzy</th>\n')
173             htmlfile.write('    <th class="right untranslated">Untranslated</th>\n')
174             htmlfile.write('    <th class="right">Complete</th>\n')
175             htmlfile.write('    <th class="center">Last Update</th>\n')
176             htmlfile.write('  </tr>\n')
177             for language in project.languages: #For all (sorted) languages...
178                 status1 = project[language]
179                 htmlfile.write('  <tr>\n')
180                 htmlfile.write('    <td class="left">%s</td>\n' % (status1.language))
181                 if status1.template: #If a template file...
182                     if status1.count > 0: #If KNOWN status...
183                         htmlfile.write('    <td class="right">%u</td>\n' % (status1.count))
184                         htmlfile.write('    <td class="right translated">%u</td>\n' % (status1.count))
185                         htmlfile.write('    <td class="right fuzzy">0</td>\n')
186                         htmlfile.write('    <td class="right untranslated">0</td>\n')
187                         htmlfile.write('    <td class="right">100 %</td>\n')
188                     else: #If UNKNOWN status...
189                         htmlfile.write('    <td class="right">-</td>\n')
190                         htmlfile.write('    <td class="right translated">-</td>\n')
191                         htmlfile.write('    <td class="right fuzzy">-</td>\n')
192                         htmlfile.write('    <td class="right untranslated">-</td>\n')
193                         htmlfile.write('    <td class="right">-</td>\n')
194                     htmlfile.write('    <td class="center">%s</td>\n' % (status1.updatedate[0:10]))
195                 else: #If NOT a template file...
196                     if status1.count > 0: #If KNOWN status...
197                         htmlfile.write('    <td class="right">%u</td>\n' % (status1.count))
198                         htmlfile.write('    <td class="right translated">%u</td>\n' % (status1.translated))
199                         htmlfile.write('    <td class="right fuzzy">%u</td>\n' % (status1.fuzzy))
200                         htmlfile.write('    <td class="right untranslated">%u</td>\n' % (status1.untranslated))
201                         htmlfile.write('    <td class="right">%u %%</td>\n' % (status1.complete))
202                     else: #If UNKNOWN status...
203                         htmlfile.write('    <td class="right">-</td>\n')
204                         htmlfile.write('    <td class="right translated">-</td>\n')
205                         htmlfile.write('    <td class="right fuzzy">-</td>\n')
206                         htmlfile.write('    <td class="right untranslated">-</td>\n')
207                         htmlfile.write('    <td class="right">-</td>\n')
208                     htmlfile.write('    <td class="center">%s</td>\n' % (status1.updatedate[0:10]))
209                 htmlfile.write('  </tr>\n')
210             htmlfile.write('</table>\n')
211         
212         #Translators...
213         htmlfile.write('<h2>Translators</h2>\n')
214         htmlfile.write('<table class="status">\n')
215         htmlfile.write('  <tr>\n')
216         htmlfile.write('    <th class="left">Language</th>\n')
217         for project in self._projects: #For all projects...
218             htmlfile.write('    <th class="left">%s</th>\n' % project.name)
219         htmlfile.write('  </tr>\n')
220         for language in self.noneTemplateLanguages: #For all NONE template languages...
221             htmlfile.write('  <tr>\n')
222             htmlfile.write('    <td>%s</td>\n' % language)
223             for project in self._projects: #For all projects...
224                 status1 = project[language]
225                 if status1:
226                     htmlfile.write('    <td>')
227                     if status1.translators: #If translators exists...
228                         for translator in status1.translators: #For all translators...
229                             if (translator.ismaintainer): #If maintainer...
230                                 if (translator.mail): #If mail address exists...
231                                     htmlfile.write('<strong title="Maintainer"><a href="mailto:%s">%s</a></strong><br>' % (translator.mail, translator.name))
232                                 else: #If NO mail address exists...
233                                     htmlfile.write('<strong title="Maintainer">%s</strong><br>' % (translator.name))
234                             else: #If NOT maintainer...
235                                 if (translator.mail): #If mail address exists...
236                                     htmlfile.write('<a href="mailto:%s">%s</a><br>' % (translator.mail, translator.name))
237                                 else: #If NO mail address exists...
238                                     htmlfile.write('%s<br>' % (translator.name))
239                     htmlfile.write('</td>\n')
240                 else:
241                     htmlfile.write('    <td></td>\n')
242             htmlfile.write('  </tr>\n')
243         htmlfile.write('</table>\n')
244         
245         htmlfile.write('</body>\n')
246         htmlfile.write('</html>\n')
247         htmlfile.close()
248     
249     def writeToMdFile(self, mdpath):
250         mdfile = codecs.open(mdpath, 'w', 'utf-8')
251         
252         mdfile.write('# Translations Status\n\n')
253         mdfile.write('Status from **%s**:\n\n' % (time.strftime('%Y-%m-%d')))
254         for project in self._projects: #For all projects...
255             mdfile.write('## %s\n\n' % (project.name))
256             mdfile.write('| Language             | Total | Translated | Fuzzy | Untranslated | Complete | Last Update |\n')
257             mdfile.write('|:---------------------|------:|-----------:|------:|-------------:|---------:|:-----------:|\n')
258             for language in project.languages: #For all (sorted) languages...
259                 status1 = project[language]
260                 formatedlanguage = status1.language.ljust(20)
261                 formatedupdatedate = status1.updatedate[0:10].center(11)
262                 if status1.template: #If a template file...
263                     if status1.count > 0: #If KNOWN status...
264                         formatedcount = str(status1.count).rjust(5)
265                         formatedtranslated = str(status1.count).rjust(10)
266                         mdfile.write('| %s | %s | %s |     0 |            0 |    100 %% | %s |\n' % (formatedlanguage, formatedcount, formatedtranslated, formatedupdatedate))
267                     else: #If UNKNOWN status...
268                         mdfile.write('| %s |     - |          - |     - |            - |        - | %s |\n' % (formatedlanguage, formatedupdatedate))
269                 else: #If NOT a template file...
270                     if status1.count > 0: #If KNOWN status...
271                         formatedcount = str(status1.count).rjust(5)
272                         formatedtranslated = str(status1.translated).rjust(10)
273                         formatedfuzzy = str(status1.fuzzy).rjust(5)
274                         formateduntranslated = str(status1.untranslated).rjust(12)
275                         formatedcomplete = str(status1.complete).rjust(6)
276                         mdfile.write('| %s | %s | %s | %s | %s | %s %% | %s |\n' % (formatedlanguage, formatedcount, formatedtranslated, formatedfuzzy, formateduntranslated, formatedcomplete, formatedupdatedate))
277                     else: #If UNKNOWN status...
278                         mdfile.write('| %s |     - |          - |     - |            - |        - | %s |\n' % (formatedlanguage, formatedupdatedate))
279             mdfile.write('\n')
280         
281         #Translators...
282         mdfile.write('## Translators\n')
283         for project in self._projects: #For all projects...
284             mdfile.write('\n### %s\n' % project.name)
285             for language in self.noneTemplateLanguages: #For all NONE template languages...
286                 status1 = project[language]
287                 if status1:
288                     if status1.translators: #If translators exists...
289                         mdfile.write('\n * %s\n' % language)
290                         for translator in status1.translators: #For all translators...
291                             if (translator.ismaintainer): #If maintainer...
292                                 if (translator.mail): #If mail address exists...
293                                     mdfile.write('   - [%s](mailto:%s) *Maintainer*\n' % (translator.name, translator.mail.replace(" ", "%20")))
294                                 else: #If NO mail address exists...
295                                     mdfile.write('   - %s *Maintainer*\n' % (translator.name))
296                             else: #If NOT maintainer...
297                                 if (translator.mail): #If mail address exists...
298                                     mdfile.write('   - [%s](mailto:%s)\n' % (translator.name, translator.mail.replace(" ", "%20")))
299                                 else: #If NO mail address exists...
300                                     mdfile.write('   - %s\n' % (translator.name))
301         
302         mdfile.close()
303
304 class Project(object):
305     def __getitem__(self, key):
306         for status in self._status: #For all status...
307             if status.language == key:
308                 return status
309         return None
310     
311     @property
312     def name(self):
313         return self._name
314     
315     @property
316     def status(self):
317         return self._status
318     
319     @property
320     def languages(self):
321         ''' Return a list with all languages '''
322         temp = []
323         for status in self._status: #For all status...
324             temp.append(status.language)
325         temp.sort()
326         return temp
327     
328     @property
329     def noneTemplateLanguages(self):
330         ''' Return a list with all NONE template languages '''
331         temp = []
332         for status in self._status: #For all status...
333             if not status.template: #If NOT a template...
334                 temp.append(status.language)
335         temp.sort()
336         return temp
337
338 class Status(object):
339     @property
340     def filepath(self):
341         return self._filepath
342     
343     @property
344     def filename(self):
345         return os.path.basename(self._filepath)
346     
347     @property
348     def template(self):
349         return self._template
350     
351     @property
352     def charset(self):
353         return self._charset
354     
355     @property
356     def language(self):
357         if self._poeditlanguage: #If "X-Poedit-Language"...
358             return self._poeditlanguage
359         else: #If NOT "X-Poedit-Language"...
360             return os.path.splitext(self.filename)[0]
361     
362     @property
363     def count(self):
364         return self._count
365     
366     @property
367     def translated(self):
368         return self._translated
369     
370     @property
371     def untranslated(self):
372         return self._untranslated
373     
374     @property
375     def fuzzy(self):
376         return self._fuzzy
377     
378     @property
379     def complete(self):
380         return self._complete
381     
382     @property
383     def updatedate(self):
384         return self._updatedate
385     
386     @property
387     def translators(self):
388         return self._translators
389     
390     def calculateCompleteness(self):
391         if self._count > 0:
392             self._complete = math.floor(((self._translated + self._fuzzy) * 100 / self._count))
393         else:
394             self._complete = 0.0
395
396 class Translator(object):
397     def __init__(self, name, mail, ismaintainer):
398         self.name = name
399         self.mail = mail
400         self.ismaintainer = ismaintainer
401
402 class PoProject(Project):
403     def __init__(self, name, potfile, podir):
404         self._name = name
405         self._status = []
406         
407         #PO files...
408         for itemname in os.listdir(podir): #For all dir items...
409             fullitempath = os.path.abspath(os.path.join(podir, itemname))
410             if os.path.isfile(fullitempath): #If a file...
411                 filename = os.path.splitext(itemname)
412                 if str.lower(filename[1]) == '.po': #If a PO file...
413                     self._status.append(PoStatus(fullitempath, False))
414         
415         #POT file...
416         self._status.append(PoStatus(os.path.abspath(potfile), True))
417
418 class PoStatus(Status):
419     def __init__(self, filepath, template):
420         self._filepath = filepath
421         self._template = template
422         self._charset = self._getCharsetFromPoFile(filepath)
423         self._count = 0
424         self._translated = 0
425         self._untranslated = 0
426         self._fuzzy = 0
427         self._complete = 0.0
428         self._porevisiondate = ''
429         self._potcreationdate = ''
430         self._poeditlanguage = ''
431         self._translators = []
432         
433         if self._charset == '': #If NO charset found...
434            return
435         
436         if os.access(filepath, os.R_OK): #If PO(T) file can read...
437           reMsgId = re.compile('^msgid "(.*)"$', re.IGNORECASE)
438           reMsgStr = re.compile('^msgstr "(.*)"$', re.IGNORECASE)
439           reMsgContinued = re.compile('^"(.*)"$', re.IGNORECASE)
440           reTranslator = re.compile('^# \* (.*)$', re.IGNORECASE)
441           rePoRevisionDate = re.compile('PO-Revision-Date: ([0-9 :\+\-]+)', re.IGNORECASE)
442           rePotCreationDate = re.compile('POT-Creation-Date: ([0-9 :\+\-]+)', re.IGNORECASE)
443           rePoeditLanguage = re.compile('X-Poedit-Language: ([A-Z \(\)\-_]+)', re.IGNORECASE)
444           
445           iMsgStarted = 0
446           sMsgId = ''
447           sMsgStr = ''
448           bIsFuzzy = False
449           bIsMaintainer = False
450           
451           encoding = self._charset.lower()
452           pofile = codecs.open(filepath, 'r', encoding)
453           for line in pofile: #For all lines...
454               line = line.strip()
455               if line: #If NOT empty line...
456                   if line[0] != '#': #If NOT comment line...
457                       if reMsgId.findall(line): #If "msgid"...
458                           iMsgStarted = 1
459                           tmp = reMsgId.findall(line)
460                           sMsgId = tmp[0]
461                       elif reMsgStr.findall(line): #If "msgstr"...
462                           iMsgStarted = 2
463                           tmp = reMsgStr.findall(line)
464                           sMsgStr = tmp[0]
465                       elif reMsgContinued.findall(line): #If "msgid" or "msgstr" continued...
466                           tmp = reMsgContinued.findall(line)
467                           if iMsgStarted == 1:
468                               sMsgId = sMsgId + tmp[0]
469                           elif iMsgStarted == 2:
470                               sMsgStr = sMsgStr + tmp[0]
471                   else: #If comment line...
472                       iMsgStarted = -1
473                       if line.startswith('#,'): #If "Reference" line...
474                           if line.find('fuzzy') > -1: #If "fuzzy"...
475                               bIsFuzzy = True
476                       elif line.startswith('# Maintainer:'): #If maintainer list starts...
477                           bIsMaintainer = True
478                       elif line.startswith('# Translators:'): #If translators list starts...
479                           bIsMaintainer = False
480                       elif reTranslator.findall(line): #If translator/maintainer...
481                           translator = reTranslator.findall(line)
482                           if re.findall('\<(.*)\>', translator[0]): #If mail address exists...
483                               tmp = re.findall('(.*) \<(.*)\>', translator[0])
484                               sName = tmp[0][0]
485                               sMail = tmp[0][1]
486                           else: #If mail address NOT exists...
487                               sName = translator[0]
488                               sMail = ''
489                           self._translators.append(Translator(sName, sMail, bIsMaintainer))
490               else: #If empty line...
491                   iMsgStarted = 0
492               
493               if iMsgStarted == 0: #If NOT inside a translation...
494                   if sMsgId != '':
495                       self._count += 1
496                       if bIsFuzzy == False: #If NOT a fuzzy translation...
497                           if sMsgStr != '':
498                               self._translated += 1
499                           else:
500                               self._untranslated += 1
501                       else: #If a fuzzy translation...
502                           self._fuzzy += 1
503                   elif sMsgStr != '':
504                       tmp = rePoRevisionDate.findall(sMsgStr)
505                       if tmp: #If "PO-Revision-Date"...
506                           #TODO: Convert to date!
507                           self._porevisiondate = tmp[0]
508                       tmp = rePotCreationDate.findall(sMsgStr)
509                       if tmp: #If "POT-Creation-Date"...
510                           #TODO: Convert to date!
511                           self._potcreationdate = tmp[0]
512                       tmp = rePoeditLanguage.findall(sMsgStr)
513                       if tmp: #If "X-Poedit-Language"...
514                           self._poeditlanguage = tmp[0]
515                   sMsgId = ''
516                   sMsgStr = ''
517                   bIsFuzzy = False
518           pofile.close()
519           
520           if sMsgId != '': #If a translation remained...
521               self._count += 1
522               if bIsFuzzy == False: #If NOT a fuzzy translation...
523                   if sMsgStr != '':
524                       self._translated += 1
525                   else:
526                       self._untranslated += 1
527               else: #If a fuzzy translation...
528                   self._fuzzy += 1
529           
530           self.calculateCompleteness()
531     
532     @property
533     def updatedate(self):
534         if self._template: #if template...
535             return self._potcreationdate
536         else: #if NOT template...
537             return self._porevisiondate
538     
539     def _getCharsetFromPoFile(self, filepath):
540         charset = ''
541         if os.access(filepath, os.R_OK): #If PO(T) file can read...
542             reContentTypeCharset = re.compile('charset=([A-Z0-9\-]+)', re.IGNORECASE)
543             rePoeditSourceCharset = re.compile('X-Poedit-SourceCharset: ([A-Z0-9\-]+)', re.IGNORECASE)
544             
545             pofile = open(filepath, 'r', errors='ignore')
546             
547             for line in pofile: #For all lines...
548                 line = line.strip()
549                 
550                 tmp = reContentTypeCharset.findall(line)
551                 if tmp: #If "Content-Type-Charset"...
552                     charset = tmp[0]
553                     break
554                 tmp = rePoeditSourceCharset.findall(line)
555                 if tmp: #If "X-Poedit-SourceCharset"...
556                     charset = tmp[0]
557                     break
558             pofile.close()
559         return charset
560
561 class InnoSetupProject(Project):
562     def __init__(self, name, templatefile, translationsdir):
563         self._name = name
564         self._status = []
565         
566         #Translations files...
567         for itemname in os.listdir(translationsdir): #For all dir items...
568             fullitempath = os.path.abspath(os.path.join(translationsdir, itemname))
569             if os.path.isfile(fullitempath): #If a file...
570                 filename = os.path.splitext(itemname)
571                 if str.lower(filename[1]) == '.isl': #If a ISL file...
572                     if filename[0] != 'English': #If NOT the English file...
573                         self._status.append(InnoSetupStatus(fullitempath, False))
574         
575         #Template file...
576         self._status.append(InnoSetupStatus(os.path.abspath(templatefile), True))
577
578 class InnoSetupStatus(Status):
579     def __init__(self, filepath, template):
580         self._filepath = filepath
581         self._template = template
582         self._count = 0
583         self._translated = 0
584         self._untranslated = 0
585         self._fuzzy = 0
586         self._updatedate = ''
587         self._translators = []
588     
589     @property
590     def language(self):
591         if self._template: #if template...
592             return 'English'
593         else: #if NOT template...
594             filename = os.path.splitext(self.filename)
595             return filename[0].replace('_', '')
596
597 class ReadmeProject(Project):
598     def __init__(self, name, templatefile, translationsdir):
599         self._name = name
600         self._status = []
601         
602         #Translations files...
603         for itemname in os.listdir(translationsdir): #For all dir items...
604             fullitempath = os.path.abspath(os.path.join(translationsdir, itemname))
605             if os.path.isfile(fullitempath): #If a file...
606                 filename = os.path.splitext(itemname)
607                 if str.lower(filename[1]) == '.txt': #If a TXT file...
608                     self._status.append(ReadmeStatus(fullitempath, False))
609         
610         #Template file...
611         self._status.append(ReadmeStatus(os.path.abspath(templatefile), True))
612
613 class ReadmeStatus(Status):
614     def __init__(self, filepath, template):
615         self._filepath = filepath
616         self._template = template
617         self._count = 0
618         self._translated = 0
619         self._untranslated = 0
620         self._fuzzy = 0
621         self._updatedate = ''
622         self._translators = []
623     
624     @property
625     def language(self):
626         if self._template: #if template...
627             return 'English'
628         else: #if NOT template...
629             filename = os.path.splitext(self.filename)
630             return filename[0].replace('ReadMe-', '')
631
632 def main():
633     parser = argparse.ArgumentParser()
634     parser.add_argument('-f', '--format', nargs='*', default='md', type=str.lower, choices=['xml', 'html', 'md'])
635     args = parser.parse_args()
636
637     status = TranslationsStatus()
638     status.addProject(PoProject('WinMerge', 'WinMerge/English.pot', 'WinMerge'))
639     status.addProject(PoProject('ShellExtension', 'ShellExtension/English.pot', 'ShellExtension'))
640     status.addProject(InnoSetupProject('InnoSetup', 'InnoSetup/English.isl', 'InnoSetup'))
641     status.addProject(ReadmeProject('Docs/Readme', 'Docs/ReadMe.txt', 'Docs/Readme'))
642     if 'xml' in args.format:
643       status.writeToXmlFile('TranslationsStatus.xml')
644     if 'html' in args.format:
645       status.writeToHtmlFile('TranslationsStatus.html')
646     if 'md' in args.format:
647       status.writeToMdFile('TranslationsStatus.md')
648
649 # MAIN #
650 if __name__ == "__main__":
651     main()