]> git.lyx.org Git - lyx.git/blob - po/lyx_pot.py
Update ru.po
[lyx.git] / po / lyx_pot.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # file lyx_pot.py
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
7 #
8 # \author Bo Peng
9 #
10 # Full author contact details are available in file CREDITS
11
12 # Usage: use
13 #     lyx_pot.py -h
14 # to get usage message
15
16 # This script will extract translatable strings from input files and write
17 # to output in gettext .pot format.
18 #
19 from __future__ import print_function
20
21 import sys, os, re, getopt
22 import io
23
24 def relativePath(path, base):
25     '''return relative path from top source dir'''
26     # full pathname of path
27     path1 = os.path.normpath(os.path.realpath(path)).split(os.sep)
28     path2 = os.path.normpath(os.path.realpath(base)).split(os.sep)
29     if path1[:len(path2)] != path2:
30         print("Path %s is not under top source directory" % path)
31     path3 = os.path.join(*path1[len(path2):]);
32     # replace all \ by / such that we get the same comments on Windows and *nix
33     path3 = path3.replace('\\', '/')
34     return path3
35
36
37 def writeString(outfile, infile, basefile, lineno, string):
38     string = string.replace('\\', '\\\\').replace('"', '')
39     if string == "":
40         return
41     print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
42         (relativePath(infile, basefile), lineno, string), file=outfile)
43
44
45 def ui_l10n(input_files, output, base):
46     '''Generate pot file from lib/ui/*'''
47     output = io.open(output, 'w', encoding='utf_8', newline='\n')
48     Submenu = re.compile(r'^[^#]*Submenu\s+"([^"]*)"', re.IGNORECASE)
49     Popupmenu = re.compile(r'^[^#]*PopupMenu\s+"[^"]+"\s+"([^"]*)"', re.IGNORECASE)
50     IconPalette = re.compile(r'^[^#]*IconPalette\s+"[^"]+"\s+"([^"]*)"', re.IGNORECASE)
51     Toolbar = re.compile(r'^[^#]*Toolbar\s+"[^"]+"\s+"([^"]*)"', re.IGNORECASE)
52     Item = re.compile(r'[^#]*Item\s+"([^"]*)"', re.IGNORECASE)
53     TableInsert = re.compile(r'[^#]*TableInsert\s+"([^"]*)"', re.IGNORECASE)
54     for src in input_files:
55         input = io.open(src, encoding='utf_8')
56         for lineno, line in enumerate(input.readlines()):
57             if Submenu.match(line):
58                 (string,) = Submenu.match(line).groups()
59                 string = string.replace('_', ' ')
60             elif Popupmenu.match(line):
61                 (string,) = Popupmenu.match(line).groups()
62             elif IconPalette.match(line):
63                 (string,) = IconPalette.match(line).groups()
64             elif Toolbar.match(line):
65                 (string,) = Toolbar.match(line).groups()
66             elif Item.match(line):
67                 (string,) = Item.match(line).groups()
68             elif TableInsert.match(line):
69                 (string,) = TableInsert.match(line).groups()
70             else:
71                 continue
72             string = string.replace('"', '')
73             if string != "":
74                 print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
75                     (relativePath(src, base), lineno+1, string), file=output)
76         input.close()
77     output.close()
78
79
80 def layouts_l10n(input_files, output, base, layouttranslations):
81     '''Generate pot file from lib/layouts/*.{layout,inc,module} and lib/citeengines/*.citeengine'''
82     ClassDescription = re.compile(r'^\s*#\s*\\Declare(LaTeX|DocBook)Class.*\{(.*)\}$', re.IGNORECASE)
83     ClassCategory = re.compile(r'^\s*#\s*\\DeclareCategory\{(.*)\}$', re.IGNORECASE)
84     Style = re.compile(r'^\s*Style\s+(.*\S)\s*$', re.IGNORECASE)
85     # match LabelString, EndLabelString, LabelStringAppendix and maybe others but no comments
86     LabelString = re.compile(r'^[^#]*LabelString\S*\s+(.*\S)\s*$', re.IGNORECASE)
87     MenuString = re.compile(r'^[^#]*MenuString\S*\s+(.*\S)\s*$', re.IGNORECASE)
88     OutlinerName = re.compile(r'^[^#]*OutlinerName\s+(\S+|\"[^\"]*\")\s+\"([^\"]*)\"', re.IGNORECASE)
89     Tooltip = re.compile(r'^\s*Tooltip\S*\s+(.*\S)\s*$', re.IGNORECASE)
90     GuiName = re.compile(r'^\s*GuiName\s+(.*\S)\s*$', re.IGNORECASE)
91     ListName = re.compile(r'^\s*ListName\s+(.*\S)\s*$', re.IGNORECASE)
92     CategoryName = re.compile(r'^\s*Category\s+(.*\S)\s*$', re.IGNORECASE)
93     NameRE = re.compile(r'^\s*#\s*\\DeclareLyXModule.*{(.*)}$', re.IGNORECASE)
94     CiteNameRE = re.compile(r'^\s*#\s*\\DeclareLyXCiteEngine.*\{(.*)\}$', re.IGNORECASE)
95     InsetLayout = re.compile(r'^InsetLayout\s+\"?(.*)\"?\s*$', re.IGNORECASE)
96     FlexCheck = re.compile(r'^Flex:(.*)', re.IGNORECASE)
97     CaptionCheck = re.compile(r'^Caption:(.*)', re.IGNORECASE)
98     DescBegin = re.compile(r'^\s*#\s*DescriptionBegin\s*$', re.IGNORECASE)
99     DescEnd = re.compile(r'^\s*#\s*DescriptionEnd\s*$', re.IGNORECASE)
100     Category = re.compile(r'^\s*#\s*Category:\s+(.*\S)\s*$', re.IGNORECASE)
101     I18nPreamble = re.compile(r'^\s*((Lang)|(Babel))Preamble\s*$', re.IGNORECASE)
102     EndI18nPreamble = re.compile(r'^\s*End((Lang)|(Babel))Preamble\s*$', re.IGNORECASE)
103     I18nString = re.compile(r'_\(([^\)]+)\)')
104     CounterFormat = re.compile(r'^\s*PrettyFormat\s+"?(.*)"?\s*$', re.IGNORECASE)
105     CiteFormat = re.compile(r'^\s*CiteFormat', re.IGNORECASE)
106     # Note: preceding and trailing space in the val below matters
107     KeyVal = re.compile(r'^\s*_\w+\s(.*\S)*$')
108     Float = re.compile(r'^\s*Float\s*$', re.IGNORECASE)
109     UsesFloatPkg = re.compile(r'^\s*UsesFloatPkg\s+(.*\S)\s*$', re.IGNORECASE)
110     IsPredefined = re.compile(r'^\s*IsPredefined\s+(.*\S)\s*$', re.IGNORECASE)
111     End = re.compile(r'^\s*End', re.IGNORECASE)
112     Comment = re.compile(r'^(.*)#')
113     Translation = re.compile(r'^\s*Translation\s+(.*\S)\s*$', re.IGNORECASE)
114     KeyValPair = re.compile(r'\s*"(.*)"\s+"(.*)"')
115
116     oldlanguages = []
117     languages = []
118     keyset = set()
119     oldtrans = dict()
120     if layouttranslations:
121         linguas_file = os.path.join(base, 'po/LINGUAS')
122         for line in open(linguas_file).readlines():
123             res = Comment.search(line)
124             if res:
125                 line = res.group(1)
126             if line.strip() != '':
127                 languages.extend(line.split())
128
129         # read old translations if available
130         try:
131             input = io.open(output, encoding='utf_8')
132             lang = ''
133             for line in input.readlines():
134                 res = Comment.search(line)
135                 if res:
136                     line = res.group(1)
137                 if line.strip() == '':
138                     continue
139                 res = Translation.search(line)
140                 if res:
141                     lang = res.group(1)
142                     if lang not in languages:
143                         oldlanguages.append(lang)
144                         languages.append(lang)
145                     oldtrans[lang] = dict()
146                     continue
147                 res = End.search(line)
148                 if res:
149                     lang = ''
150                     continue
151                 res = KeyValPair.search(line)
152                 if res and lang != '':
153                     key = res.group(1)
154                     val = res.group(2)
155                     key = key.replace('\\"', '"').replace('\\\\', '\\')
156                     val = val.replace('\\"', '"').replace('\\\\', '\\')
157                     oldtrans[lang][key] = val
158                     keyset.add(key)
159                     continue
160                 print("Error: Unable to handle line:")
161                 print(line)
162         except IOError:
163             print("Warning: Unable to open %s for reading." % output)
164             print("         Old translations will be lost.")
165
166         # walon is not a known document language
167         # FIXME: Do not hardcode, read from lib/languages!
168         if 'wa' in languages:
169             languages.remove('wa')
170
171     if layouttranslations:
172         out = io.open(output, 'w', encoding='utf_8')
173     else:
174         out = io.open(output, 'w', encoding='utf_8', newline='\n')
175     for src in input_files:
176         readingDescription = False
177         readingI18nPreamble = False
178         readingFloat = False
179         readingCiteFormats = False
180         isPredefined = False
181         usesFloatPkg = True
182         listname = ''
183         floatname = ''
184         descStartLine = -1
185         descLines = []
186         lineno = 0
187         for line in io.open(src, encoding='utf_8').readlines():
188             lineno += 1
189             res = ClassDescription.search(line)
190             if res != None:
191                 string = res.group(2)
192                 if not layouttranslations:
193                     writeString(out, src, base, lineno + 1, string)
194                 continue
195             res = ClassCategory.search(line)
196             if res != None:
197                 string = res.group(1)
198                 if not layouttranslations:
199                     writeString(out, src, base, lineno + 1, string)
200                 continue
201             if readingDescription:
202                 res = DescEnd.search(line)
203                 if res != None:
204                     readingDescription = False
205                     desc = " ".join(descLines)
206                     if not layouttranslations:
207                         writeString(out, src, base, lineno + 1, desc)
208                     continue
209                 descLines.append(line[1:].strip())
210                 continue
211             res = DescBegin.search(line)
212             if res != None:
213                 readingDescription = True
214                 descStartLine = lineno
215                 continue
216             if readingI18nPreamble:
217                 res = EndI18nPreamble.search(line)
218                 if res != None:
219                     readingI18nPreamble = False
220                     continue
221                 res = I18nString.search(line)
222                 if res != None:
223                     string = res.group(1)
224                     if layouttranslations:
225                         keyset.add(string)
226                     else:
227                         writeString(out, src, base, lineno, string)
228                 continue
229             res = I18nPreamble.search(line)
230             if res != None:
231                 readingI18nPreamble = True
232                 continue
233             res = NameRE.search(line)
234             if res != None:
235                 string = res.group(1)
236                 if not layouttranslations:
237                     writeString(out, src, base, lineno + 1, string)
238                 continue
239             res = CiteNameRE.search(line)
240             if res != None:
241                 string = res.group(1)
242                 if not layouttranslations:
243                     writeString(out, src, base, lineno + 1, string)
244                 continue
245             res = Style.search(line)
246             if res != None:
247                 string = res.group(1)
248                 string = string.replace('_', ' ')
249                 # Style means something else inside a float definition
250                 if not readingFloat:
251                     if not layouttranslations:
252                         writeString(out, src, base, lineno, string)
253                 continue
254             res = LabelString.search(line)
255             if res != None:
256                 string = res.group(1)
257                 if not layouttranslations:
258                     writeString(out, src, base, lineno, string)
259                 continue
260             res = MenuString.search(line)
261             if res != None:
262                 string = res.group(1)
263                 if not layouttranslations:
264                     writeString(out, src, base, lineno, string)
265                 continue
266             res = OutlinerName.search(line)
267             if res != None:
268                 string = res.group(2)
269                 if not layouttranslations:
270                     writeString(out, src, base, lineno, string)
271                 continue
272             res = Tooltip.search(line)
273             if res != None:
274                 string = res.group(1)
275                 if not layouttranslations:
276                     writeString(out, src, base, lineno, string)
277                 continue
278             res = GuiName.search(line)
279             if res != None:
280                 string = res.group(1)
281                 if layouttranslations:
282                     # gui name must only be added for floats
283                     if readingFloat:
284                         floatname = string
285                 else:
286                     writeString(out, src, base, lineno, string)
287                 continue
288             res = CategoryName.search(line)
289             if res != None:
290                 string = res.group(1)
291                 if not layouttranslations:
292                     writeString(out, src, base, lineno, string)
293                 continue
294             res = ListName.search(line)
295             if res != None:
296                 string = res.group(1)
297                 if layouttranslations:
298                     listname = string.strip('"')
299                 else:
300                     writeString(out, src, base, lineno, string)
301                 continue
302             res = InsetLayout.search(line)
303             if res != None:
304                 string = res.group(1)
305                 string = string.replace('_', ' ')
306                 #Flex:xxx is not used in translation
307                 #if not layouttranslations:
308                 #    writeString(out, src, base, lineno, string)
309                 m = FlexCheck.search(string)
310                 if m:
311                     if not layouttranslations:
312                         writeString(out, src, base, lineno, m.group(1))
313                 m = CaptionCheck.search(string)
314                 if m:
315                     if not layouttranslations:
316                         writeString(out, src, base, lineno, m.group(1))
317                 continue
318             res = Category.search(line)
319             if res != None:
320                 string = res.group(1)
321                 if not layouttranslations:
322                     writeString(out, src, base, lineno, string)
323                 continue
324             res = CounterFormat.search(line)
325             if res != None:
326                 string = res.group(1)
327                 if not layouttranslations:
328                     writeString(out, src, base, lineno, string)
329                 continue
330             res = Float.search(line)
331             if res != None:
332                 readingFloat = True
333                 continue
334             res = IsPredefined.search(line)
335             if res != None:
336                 string = res.group(1).lower()
337                 if string == 'true':
338                     isPredefined = True
339                 else:
340                     isPredefined = False
341                 continue
342             res = UsesFloatPkg.search(line)
343             if res != None:
344                 string = res.group(1).lower()
345                 if string == 'true':
346                     usesFloatPkg = True
347                 else:
348                     usesFloatPkg = False
349                 continue
350             res = CiteFormat.search(line)
351             if res != None:
352                 readingCiteFormats = True
353                 continue
354             res = End.search(line)
355             if res != None:
356                 # We have four combinations of the flags usesFloatPkg and isPredefined:
357                 #     usesFloatPkg and     isPredefined: might use standard babel translations
358                 #     usesFloatPkg and not isPredefined: does not use standard babel translations
359                 # not usesFloatPkg and     isPredefined: uses standard babel translations
360                 # not usesFloatPkg and not isPredefined: not supported by LyX
361                 # The third combination is even true for MarginFigure, MarginTable (both from
362                 # tufte-book.layout) and Planotable, Plate (both from aguplus.inc).
363                 if layouttranslations and readingFloat and usesFloatPkg:
364                     if floatname != '':
365                         keyset.add(floatname)
366                     if listname != '':
367                         keyset.add(listname)
368                 isPredefined = False
369                 usesFloatPkg = True
370                 listname = ''
371                 floatname = ''
372                 readingCiteFormats = False
373                 readingFloat = False
374                 continue
375             if readingCiteFormats:
376                 res = KeyVal.search(line)
377                 if res != None:
378                     val = res.group(1)
379                     if not layouttranslations:
380                         writeString(out, src, base, lineno, val)
381
382     if layouttranslations:
383         # Extract translations of layout files
384         import polib
385
386         # Sort languages and key to minimize the diff between different runs
387         # with changed translations
388         languages.sort()
389         keys = []
390         for key in keyset:
391             keys.append(key)
392         keys.sort()
393
394         ContextRe = re.compile(r'(.*)(\[\[.*\]\])')
395
396         print(u'''# This file has been automatically generated by po/lyx_pot.py.
397 # PLEASE MODIFY ONLY THE LAGUAGES HAVING NO .po FILE! If you want to regenerate
398 # this file from the translations, run `make ../lib/layouttranslations' in po.
399 # Python polib library is needed for building the output file.
400 #
401 # This file should remain fixed during minor LyX releases.
402 # For more comments see README.localization file.''', file=out)
403         for lang in languages:
404             print(u'\nTranslation %s' % lang, file=out)
405             if lang in list(oldtrans.keys()):
406                 trans = oldtrans[lang]
407             else:
408                 trans = dict()
409             if not lang in oldlanguages:
410                 poname = os.path.join(base, 'po/' + lang + '.po')
411                 po = polib.pofile(poname)
412                 # Iterate through po entries and not keys for speed reasons.
413                 # FIXME: The code is still too slow
414                 for entry in po:
415                     if not entry.translated():
416                         continue
417                     if entry.msgid in keys:
418                         key = entry.msgid
419                         val = entry.msgstr
420                         # some translators keep untranslated entries
421                         if val != key:
422                             trans[key] = val
423             for key in keys:
424                 if key in list(trans.keys()):
425                     val = trans[key].replace('\\', '\\\\').replace('"', '\\"')
426                     res = ContextRe.search(val)
427                     if res != None:
428                         val = res.group(1)
429                     key = key.replace('\\', '\\\\').replace('"', '\\"')
430                     print(u'\t"%s" "%s"' % (key, val), file=out)
431                 # also print untranslated entries to help translators
432                 elif not lang in oldlanguages:
433                     key = key.replace('\\', '\\\\').replace('"', '\\"')
434                     res = ContextRe.search(key)
435                     if res != None:
436                         val = res.group(1)
437                     else:
438                         val = key
439                     print(u'\t"%s" "%s"' % (key, val), file=out)
440             print(u'End', file=out)
441
442     out.close()
443
444
445 def qt4_l10n(input_files, output, base):
446     '''Generate pot file from src/frontends/qt4/ui/*.ui'''
447     output = io.open(output, 'w', encoding='utf_8', newline='\n')
448     pat = re.compile(r'\s*<string>(.*)</string>')
449     prop = re.compile(r'\s*<property.*name.*=.*shortcut')
450     for src in input_files:
451         input = io.open(src, encoding='utf_8')
452         skipNextLine = False
453         for lineno, line in enumerate(input.readlines()):
454             # skip the line after <property name=shortcut>
455             if skipNextLine:
456                 skipNextLine = False
457                 continue
458             if prop.match(line):
459                 skipNextLine = True
460                 continue
461             # get lines that match <string>...</string>
462             if pat.match(line):
463                 (string,) = pat.match(line).groups()
464                 string = string.replace('&amp;', '&').replace('&quot;', '"')
465                 string = string.replace('&lt;', '<').replace('&gt;', '>')
466                 string = string.replace('\\', '\\\\').replace('"', r'\"')
467                 string = string.replace('&#x0a;', r'\n')
468                 print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
469                     (relativePath(src, base), lineno+1, string), file=output)
470         input.close()
471     output.close()
472
473
474 def languages_l10n(input_files, output, base):
475     '''Generate pot file from lib/languages'''
476     out = io.open(output, 'w', encoding='utf_8', newline='\n')
477     GuiName = re.compile(r'^[^#]*GuiName\s+(.*)', re.IGNORECASE)
478
479     for src in input_files:
480         descStartLine = -1
481         descLines = []
482         lineno = 0
483         for line in io.open(src, encoding='utf_8').readlines():
484             lineno += 1
485             res = GuiName.search(line)
486             if res != None:
487                 string = res.group(1)
488                 writeString(out, src, base, lineno, string)
489                 continue
490
491     out.close()
492
493
494 def latexfonts_l10n(input_files, output, base):
495     '''Generate pot file from lib/latexfonts'''
496     out = io.open(output, 'w', encoding='utf_8', newline='\n')
497     GuiName = re.compile(r'^[^#]*GuiName\s+(.*)', re.IGNORECASE)
498
499     for src in input_files:
500         descStartLine = -1
501         descLines = []
502         lineno = 0
503         for line in io.open(src, encoding='utf_8').readlines():
504             lineno += 1
505             res = GuiName.search(line)
506             if res != None:
507                 string = res.group(1)
508                 writeString(out, src, base, lineno, string)
509                 continue
510
511     out.close()
512
513
514 def external_l10n(input_files, output, base):
515     '''Generate pot file from lib/xtemplates'''
516     output = io.open(output, 'w', encoding='utf_8', newline='\n')
517     Template = re.compile(r'^Template\s+(.*)', re.IGNORECASE)
518     GuiName = re.compile(r'\s*GuiName\s+(.*)', re.IGNORECASE)
519     HelpTextStart = re.compile(r'\s*HelpText\s', re.IGNORECASE)
520     HelpTextSection = re.compile(r'\s*(\S.*)\s*$')
521     HelpTextEnd = re.compile(r'\s*HelpTextEnd\s', re.IGNORECASE)
522     i = -1
523     for src in input_files:
524         input = io.open(src, encoding='utf_8')
525         inHelp = False
526         hadHelp = False
527         prev_help_string = ''
528         for lineno, line in enumerate(input.readlines()):
529             if Template.match(line):
530                 (string,) = Template.match(line).groups()
531             elif GuiName.match(line):
532                 (string,) = GuiName.match(line).groups()
533             elif inHelp:
534                 if HelpTextEnd.match(line):
535                     if hadHelp:
536                         print(u'\nmsgstr ""\n', file=output)
537                     inHelp = False
538                     hadHelp = False
539                     prev_help_string = ''
540                 elif HelpTextSection.match(line):
541                     (help_string,) = HelpTextSection.match(line).groups()
542                     help_string = help_string.replace('"', '')
543                     help_string =  help_string.replace('\\', '_backsl_')
544                     help_string =  help_string.replace('_backsl_', '\\\\')
545                     if help_string != "" and prev_help_string == '':
546                         print(u'#: %s:%d\nmsgid ""\n"%s\\n"' % \
547                             (relativePath(src, base), lineno+1, help_string), file=output)
548                         hadHelp = True
549                     elif help_string != "":
550                         print(u'"%s\\n"' % help_string, file=output)
551                     prev_help_string = help_string
552                 else:
553                     # Empty line
554                     print(u'"\\n"', file=output)
555                     prev_help_string = 'xxxx'
556             elif HelpTextStart.match(line):
557                 inHelp = True
558                 prev_help_string = ''
559             else:
560                 continue
561             string = string.replace('"', '')
562             if string != "" and not inHelp:
563                 print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
564                     (relativePath(src, base), lineno+1, string), file=output)
565         input.close()
566     output.close()
567
568
569 def formats_l10n(input_files, output, base):
570     '''Generate pot file from configure.py'''
571     output = io.open(output, 'w', encoding='utf_8', newline='\n')
572     GuiName = re.compile(r'.*\\Format\s+\S+\s+\S+\s+"([^"]*)"\s+(\S*)\s+.*', re.IGNORECASE)
573     GuiName2 = re.compile(r'.*\\Format\s+\S+\s+\S+\s+([^"]\S+)\s+(\S*)\s+.*', re.IGNORECASE)
574     input = io.open(input_files[0], encoding='utf_8')
575     for lineno, line in enumerate(input.readlines()):
576         label = ""
577         labelsc = ""
578         if GuiName.match(line):
579             label = GuiName.match(line).group(1)
580             shortcut = GuiName.match(line).group(2).replace('"', '')
581         elif GuiName2.match(line):
582             label = GuiName2.match(line).group(1)
583             shortcut = GuiName2.match(line).group(2).replace('"', '')
584         else:
585             continue
586         label = label.replace('\\', '\\\\').replace('"', '')
587         if shortcut != "":
588             labelsc = label + "|" + shortcut
589         if label != "":
590             print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
591                 (relativePath(input_files[0], base), lineno+1, label), file=output)
592         if labelsc != "":
593             print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
594                 (relativePath(input_files[0], base), lineno+1, labelsc), file=output)
595     input.close()
596     output.close()
597
598
599 def encodings_l10n(input_files, output, base):
600     '''Generate pot file from lib/encodings'''
601     output = io.open(output, 'w', encoding='utf_8', newline='\n')
602     # assuming only one encodings file
603     #                 Encoding utf8      utf8    "Unicode (utf8)" UTF-8    variable inputenc
604     reg = re.compile('Encoding [\w-]+\s+[\w-]+\s+"([\w \-\(\)^"]*)"\s+["\w-]+\s+(fixed|variable|variableunsafe)\s+\w+.*')
605     input = io.open(input_files[0], encoding='utf_8')
606     for lineno, line in enumerate(input.readlines()):
607         if not line.startswith('Encoding'):
608             continue
609         if reg.match(line):
610             guiname = reg.match(line).groups()[0]
611             if guiname != "":
612                 print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
613                     (relativePath(input_files[0], base), lineno+1, guiname), file=output)
614         else:
615             print("Error: Unable to handle line:")
616             print(line)
617             # No need to abort if the parsing fails
618             # sys.exit(1)
619     input.close()
620     output.close()
621
622
623
624 Usage = '''
625 lyx_pot.py [-b|--base top_src_dir] [-o|--output output_file] [-h|--help] [-s|src_file filename] -t|--type input_type input_files
626
627 where
628     --base:
629         path to the top source directory. default to '.'
630     --output:
631         output pot file, default to './lyx.pot'
632     --src_file
633         filename that contains a list of input files in each line
634     --input_type can be
635         ui: lib/ui/*
636         layouts: lib/layouts/*
637         layouttranslations: create lib/layouttranslations from po/*.po and lib/layouts/*
638         qt4: qt4 ui files
639         languages: file lib/languages
640         latexfonts: file lib/latexfonts
641         encodings: file lib/encodings
642         external: external templates files
643         formats: formats predefined in lib/configure.py
644 '''
645
646 if __name__ == '__main__':
647     input_type = None
648     output = 'lyx.pot'
649     base = '.'
650     input_files = []
651     #
652     optlist, args = getopt.getopt(sys.argv[1:], 'ht:o:b:s:',
653         ['help', 'type=', 'output=', 'base=', 'src_file='])
654     for (opt, value) in optlist:
655         if opt in ['-h', '--help']:
656             print(Usage)
657             sys.exit(0)
658         elif opt in ['-o', '--output']:
659             output = value
660         elif opt in ['-b', '--base']:
661             base = value
662         elif opt in ['-t', '--type']:
663             input_type = value
664         elif opt in ['-s', '--src_file']:
665             input_files = [f.strip() for f in io.open(value, encoding='utf_8')]
666
667     if input_type not in ['ui', 'layouts', 'layouttranslations', 'qt4', 'languages', 'latexfonts', 'encodings', 'external', 'formats'] or output is None:
668         print('Wrong input type or output filename.')
669         sys.exit(1)
670
671     input_files += args
672
673     # Ensure a unique sorting of input files and ignore the order in which they
674     # are given on the command line. This is important to avoid huge
675     # pseudo-diffs in the generated .pot file which would then end up in the
676     # .po files as well. We had this situation for years with people using
677     # different build systems to remerge .po files.
678     input_files.sort()
679
680     if input_type == 'ui':
681         ui_l10n(input_files, output, base)
682     elif input_type == 'latexfonts':
683         latexfonts_l10n(input_files, output, base)
684     elif input_type == 'layouts':
685         layouts_l10n(input_files, output, base, False)
686     elif input_type == 'layouttranslations':
687         layouts_l10n(input_files, output, base, True)
688     elif input_type == 'qt4':
689         qt4_l10n(input_files, output, base)
690     elif input_type == 'external':
691         external_l10n(input_files, output, base)
692     elif input_type == 'formats':
693         formats_l10n(input_files, output, base)
694     elif input_type == 'encodings':
695         encodings_l10n(input_files, output, base)
696     else:
697         languages_l10n(input_files, output, base)
698
699