]> git.lyx.org Git - lyx.git/blob - po/lyx_pot.py
Merge branch 'master' into biblatex2
[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+(\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*#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/external_templates'''
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                     if help_string != "" and prev_help_string == '':
544                         print(u'#: %s:%d\nmsgid ""\n"%s\\n"' % \
545                             (relativePath(src, base), lineno+1, help_string), file=output)
546                         hadHelp = True
547                     elif help_string != "":
548                         print(u'"%s\\n"' % help_string, file=output)
549                     prev_help_string = help_string
550             elif HelpTextStart.match(line):
551                 inHelp = True
552                 prev_help_string = ''
553             else:
554                 continue
555             string = string.replace('"', '')
556             if string != "" and not inHelp:
557                 print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
558                     (relativePath(src, base), lineno+1, string), file=output)
559         input.close()
560     output.close()
561
562
563 def formats_l10n(input_files, output, base):
564     '''Generate pot file from configure.py'''
565     output = io.open(output, 'w', encoding='utf_8', newline='\n')
566     GuiName = re.compile(r'.*\\Format\s+\S+\s+\S+\s+"([^"]*)"\s+(\S*)\s+.*', re.IGNORECASE)
567     GuiName2 = re.compile(r'.*\\Format\s+\S+\s+\S+\s+([^"]\S+)\s+(\S*)\s+.*', re.IGNORECASE)
568     input = io.open(input_files[0], encoding='utf_8')
569     for lineno, line in enumerate(input.readlines()):
570         label = ""
571         labelsc = ""
572         if GuiName.match(line):
573             label = GuiName.match(line).group(1)
574             shortcut = GuiName.match(line).group(2).replace('"', '')
575         elif GuiName2.match(line):
576             label = GuiName2.match(line).group(1)
577             shortcut = GuiName2.match(line).group(2).replace('"', '')
578         else:
579             continue
580         label = label.replace('\\', '\\\\').replace('"', '')
581         if shortcut != "":
582             labelsc = label + "|" + shortcut
583         if label != "":
584             print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
585                 (relativePath(input_files[0], base), lineno+1, label), file=output)
586         if labelsc != "":
587             print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
588                 (relativePath(input_files[0], base), lineno+1, labelsc), file=output)
589     input.close()
590     output.close()
591
592
593 def encodings_l10n(input_files, output, base):
594     '''Generate pot file from lib/encodings'''
595     output = io.open(output, 'w', encoding='utf_8', newline='\n')
596     # assuming only one encodings file
597     #                 Encoding utf8      utf8    "Unicode (utf8)" UTF-8    variable inputenc
598     reg = re.compile('Encoding [\w-]+\s+[\w-]+\s+"([\w \-\(\)]+)"\s+[\w-]+\s+(fixed|variable|variableunsafe)\s+\w+.*')
599     input = io.open(input_files[0], encoding='utf_8')
600     for lineno, line in enumerate(input.readlines()):
601         if not line.startswith('Encoding'):
602             continue
603         if reg.match(line):
604             print(u'#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
605                 (relativePath(input_files[0], base), lineno+1, reg.match(line).groups()[0]), file=output)
606         else:
607             print("Error: Unable to handle line:")
608             print(line)
609             # No need to abort if the parsing fails
610             # sys.exit(1)
611     input.close()
612     output.close()
613
614
615
616 Usage = '''
617 lyx_pot.py [-b|--base top_src_dir] [-o|--output output_file] [-h|--help] [-s|src_file filename] -t|--type input_type input_files
618
619 where
620     --base:
621         path to the top source directory. default to '.'
622     --output:
623         output pot file, default to './lyx.pot'
624     --src_file
625         filename that contains a list of input files in each line
626     --input_type can be
627         ui: lib/ui/*
628         layouts: lib/layouts/*
629         layouttranslations: create lib/layouttranslations from po/*.po and lib/layouts/*
630         qt4: qt4 ui files
631         languages: file lib/languages
632         latexfonts: file lib/latexfonts
633         encodings: file lib/encodings
634         external: external templates file
635         formats: formats predefined in lib/configure.py
636 '''
637
638 if __name__ == '__main__':
639     input_type = None
640     output = 'lyx.pot'
641     base = '.'
642     input_files = []
643     #
644     optlist, args = getopt.getopt(sys.argv[1:], 'ht:o:b:s:',
645         ['help', 'type=', 'output=', 'base=', 'src_file='])
646     for (opt, value) in optlist:
647         if opt in ['-h', '--help']:
648             print(Usage)
649             sys.exit(0)
650         elif opt in ['-o', '--output']:
651             output = value
652         elif opt in ['-b', '--base']:
653             base = value
654         elif opt in ['-t', '--type']:
655             input_type = value
656         elif opt in ['-s', '--src_file']:
657             input_files = [f.strip() for f in io.open(value, encoding='utf_8')]
658
659     if input_type not in ['ui', 'layouts', 'layouttranslations', 'qt4', 'languages', 'latexfonts', 'encodings', 'external', 'formats'] or output is None:
660         print('Wrong input type or output filename.')
661         sys.exit(1)
662
663     input_files += args
664
665     # Ensure a unique sorting of input files and ignore the order in which they
666     # are given on the command line. This is important to avoid huge
667     # pseudo-diffs in the generated .pot file which would then end up in the
668     # .po files as well. We had this situation for years with people using
669     # different build systems to remerge .po files.
670     input_files.sort()
671
672     if input_type == 'ui':
673         ui_l10n(input_files, output, base)
674     elif input_type == 'latexfonts':
675         latexfonts_l10n(input_files, output, base)
676     elif input_type == 'layouts':
677         layouts_l10n(input_files, output, base, False)
678     elif input_type == 'layouttranslations':
679         layouts_l10n(input_files, output, base, True)
680     elif input_type == 'qt4':
681         qt4_l10n(input_files, output, base)
682     elif input_type == 'external':
683         external_l10n(input_files, output, base)
684     elif input_type == 'formats':
685         formats_l10n(input_files, output, base)
686     elif input_type == 'encodings':
687         encodings_l10n(input_files, output, base)
688     else:
689         languages_l10n(input_files, output, base)
690
691