]> git.lyx.org Git - lyx.git/blob - po/lyx_pot.py
Routines for calculating numerical labels for BibTeX citations.
[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 import sys, os, re, getopt
20
21 def relativePath(path, base):
22     '''return relative path from top source dir'''
23     # full pathname of path
24     path1 = os.path.normpath(os.path.realpath(path)).split(os.sep)
25     path2 = os.path.normpath(os.path.realpath(base)).split(os.sep)
26     if path1[:len(path2)] != path2:
27         print "Path %s is not under top source directory" % path
28     path3 = os.path.join(*path1[len(path2):]);
29     # replace all \ by / such that we get the same comments on Windows and *nix
30     path3 = path3.replace('\\', '/')
31     return path3
32
33
34 def writeString(outfile, infile, basefile, lineno, string):
35     string = string.replace('\\', '\\\\').replace('"', '')
36     if string == "":
37         return
38     print >> outfile, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
39         (relativePath(infile, basefile), lineno, string)
40
41
42 def ui_l10n(input_files, output, base):
43     '''Generate pot file from lib/ui/*'''
44     output = open(output, 'w')
45     Submenu = re.compile(r'^[^#]*Submenu\s+"([^"]*)"')
46     Popupmenu = re.compile(r'^[^#]*PopupMenu\s+"[^"]+"\s+"([^"]*)"')
47     IconPalette = re.compile(r'^[^#]*IconPalette\s+"[^"]+"\s+"([^"]*)"')
48     Toolbar = re.compile(r'^[^#]*Toolbar\s+"[^"]+"\s+"([^"]*)"')
49     Item = re.compile(r'[^#]*Item\s+"([^"]*)"')
50     TableInsert = re.compile(r'[^#]*TableInsert\s+"([^"]*)"')
51     for src in input_files:
52         input = open(src)
53         for lineno, line in enumerate(input.readlines()):
54             if Submenu.match(line):
55                 (string,) = Submenu.match(line).groups()
56                 string = string.replace('_', ' ')
57             elif Popupmenu.match(line):
58                 (string,) = Popupmenu.match(line).groups()
59             elif IconPalette.match(line):
60                 (string,) = IconPalette.match(line).groups()
61             elif Toolbar.match(line):
62                 (string,) = Toolbar.match(line).groups()
63             elif Item.match(line):
64                 (string,) = Item.match(line).groups()
65             elif TableInsert.match(line):
66                 (string,) = TableInsert.match(line).groups()
67             else:
68                 continue
69             string = string.replace('"', '')
70             if string != "":
71                 print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
72                     (relativePath(src, base), lineno+1, string)
73         input.close()
74     output.close()
75
76
77 def layouts_l10n(input_files, output, base):
78     '''Generate pot file from lib/layouts/*.{layout,inc,module}'''
79     out = open(output, 'w')
80     Style = re.compile(r'^Style\s+(.*)')
81     # include ???LabelString???, but exclude comment lines
82     LabelString = re.compile(r'^[^#]*LabelString\S*\s+(.*)')
83     GuiName = re.compile(r'\s*GuiName\s+(.*)')
84     ListName = re.compile(r'\s*ListName\s+(.*)')
85     CategoryName = re.compile(r'\s*Category\s+(.*)')
86     NameRE = re.compile(r'DeclareLyXModule.*{(.*)}')
87     InsetLayout = re.compile(r'^InsetLayout\s+(.*)')
88     DescBegin = re.compile(r'#+\s*DescriptionBegin\s*$')
89     DescEnd = re.compile(r'#+\s*DescriptionEnd\s*$')
90     Category = re.compile(r'#Category: (.*)$')
91     I18nPreamble = re.compile(r'\s*(Lang)|(Babel)Preamble\s*$')
92     EndI18nPreamble = re.compile(r'\s*End(Lang)|(Babel)Preamble\s*$')
93     I18nString = re.compile(r'_\(([^\)]+)\)')
94
95     for src in input_files:
96         readingDescription = False
97         readingI18nPreamble = False
98         descStartLine = -1
99         descLines = []
100         lineno = 0
101         for line in open(src).readlines():
102             lineno += 1
103             if readingDescription:
104                 res = DescEnd.search(line)
105                 if res != None:
106                     readingDescription = False
107                     desc = " ".join(descLines)
108                     writeString(out, src, base, lineno + 1, desc)
109                     continue
110                 descLines.append(line[1:].strip())
111                 continue
112             res = DescBegin.search(line)
113             if res != None:
114                 readingDescription = True
115                 descStartLine = lineno
116                 continue
117             if readingI18nPreamble:
118                 res = EndI18nPreamble.search(line)
119                 if res != None:
120                     readingI18nPreamble = False
121                     continue
122                 res = I18nString.search(line)
123                 if res != None:
124                     string = res.group(1)
125                     writeString(out, src, base, lineno, string)
126                 continue
127             res = I18nPreamble.search(line)
128             if res != None:
129                 readingI18nPreamble = True
130                 continue
131             res = NameRE.search(line)
132             if res != None:
133                 string = res.group(1)
134                 string = string.replace('\\', '\\\\').replace('"', '')
135                 if string != "":
136                     print >> out, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
137                         (relativePath(src, base), lineno + 1, string)
138                 continue
139             res = Style.search(line)
140             if res != None:
141                 string = res.group(1)
142                 string = string.replace('_', ' ')
143                 writeString(out, src, base, lineno, string)
144                 continue
145             res = LabelString.search(line)
146             if res != None:
147                 string = res.group(1)
148                 writeString(out, src, base, lineno, string)
149                 continue
150             res = GuiName.search(line)
151             if res != None:
152                 string = res.group(1)
153                 writeString(out, src, base, lineno, string)
154                 continue
155             res = CategoryName.search(line)
156             if res != None:
157                 string = res.group(1)
158                 writeString(out, src, base, lineno, string)
159                 continue
160             res = ListName.search(line)
161             if res != None:
162                 string = res.group(1)
163                 writeString(out, src, base, lineno, string)
164                 continue
165             res = InsetLayout.search(line)
166             if res != None:
167                 string = res.group(1)
168                 string = string.replace('_', ' ')
169                 writeString(out, src, base, lineno, string)
170                 continue
171             res = Category.search(line)
172             if res != None:
173                 string = res.group(1)
174                 writeString(out, src, base, lineno, string)
175                 continue
176     out.close()
177
178
179 def qt4_l10n(input_files, output, base):
180     '''Generate pot file from src/frontends/qt4/ui/*.ui'''
181     output = open(output, 'w')
182     pat = re.compile(r'\s*<string>(.*)</string>')
183     prop = re.compile(r'\s*<property.*name.*=.*shortcut')
184     for src in input_files:
185         input = open(src)
186         skipNextLine = False
187         for lineno, line in enumerate(input.readlines()):
188             # skip the line after <property name=shortcut>
189             if skipNextLine:
190                 skipNextLine = False
191                 continue
192             if prop.match(line):
193                 skipNextLine = True
194                 continue
195             # get lines that match <string>...</string>
196             if pat.match(line):
197                 (string,) = pat.match(line).groups()
198                 string = string.replace('&amp;', '&').replace('&lt;', '<').replace('&gt;', '>')
199                 string = string.replace('\\', '\\\\').replace('"', r'\"')
200                 print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
201                     (relativePath(src, base), lineno+1, string)
202         input.close()
203     output.close()
204
205
206 def languages_l10n(input_files, output, base):
207     '''Generate pot file from lib/language'''
208     output = open(output, 'w')
209     # assuming only one language file
210     reg = re.compile('[\w-]+\s+[\w"]+\s+"([\w \-\(\),]+)"\s+(true|false)\s+[\w-]+\s+\w+\s+"[^"]*"')
211     input = open(input_files[0])
212     for lineno, line in enumerate(input.readlines()):
213         if line[0] == '#':
214             continue
215         # From:
216         #   afrikaans   afrikaans       "Afrikaans"     false  iso8859-15 af_ZA  ""
217         # To:
218         #   #: lib/languages:2
219         #   msgid "Afrikaans"
220         #   msgstr ""
221         if reg.match(line):
222             print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
223                 (relativePath(input_files[0], base), lineno+1, reg.match(line).groups()[0])
224         else:
225             print "Error: Unable to handle line:"
226             print line
227             # No need to abort if the parsing fails (e.g. "ignore" language has no encoding)
228             # sys.exit(1)
229     input.close()
230     output.close()
231
232
233 def external_l10n(input_files, output, base):
234     '''Generate pot file from lib/external_templates'''
235     output = open(output, 'w')
236     Template = re.compile(r'^Template\s+(.*)')
237     GuiName = re.compile(r'\s*GuiName\s+(.*)')
238     HelpTextStart = re.compile(r'\s*HelpText\s')
239     HelpTextSection = re.compile(r'\s*(\S.*)\s*$')
240     HelpTextEnd = re.compile(r'\s*HelpTextEnd\s')
241     i = -1
242     for src in input_files:
243         input = open(src)
244         inHelp = False
245         hadHelp = False
246         prev_help_string = ''
247         for lineno, line in enumerate(input.readlines()):
248             if Template.match(line):
249                 (string,) = Template.match(line).groups()
250             elif GuiName.match(line):
251                 (string,) = GuiName.match(line).groups()
252             elif inHelp:
253                 if HelpTextEnd.match(line):
254                     if hadHelp:
255                         print >> output, '\nmsgstr ""\n'
256                     inHelp = False
257                     hadHelp = False
258                     prev_help_string = ''
259                 elif HelpTextSection.match(line):
260                     (help_string,) = HelpTextSection.match(line).groups()
261                     help_string = help_string.replace('"', '')
262                     if help_string != "" and prev_help_string == '':
263                         print >> output, '#: %s:%d\nmsgid ""\n"%s\\n"' % \
264                             (relativePath(src, base), lineno+1, help_string)
265                         hadHelp = True
266                     elif help_string != "":
267                         print >> output, '"%s\\n"' % help_string
268                     prev_help_string = help_string
269             elif HelpTextStart.match(line):
270                 inHelp = True
271                 prev_help_string = ''
272             else:
273                 continue
274             string = string.replace('"', '')
275             if string != "" and not inHelp:
276                 print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
277                     (relativePath(src, base), lineno+1, string)
278         input.close()
279     output.close()
280
281
282 def formats_l10n(input_files, output, base):
283     '''Generate pot file from configure.py'''
284     output = open(output, 'w')
285     GuiName = re.compile(r'.*\Format\s+\S+\s+\S+\s+"([^"]*)"\s+(\S*)\s+.*')
286     GuiName2 = re.compile(r'.*\Format\s+\S+\s+\S+\s+([^"]\S+)\s+(\S*)\s+.*')
287     input = open(input_files[0])
288     for lineno, line in enumerate(input.readlines()):
289         label = ""
290         labelsc = ""
291         if GuiName.match(line):
292             label = GuiName.match(line).group(1)
293             shortcut = GuiName.match(line).group(2).replace('"', '')
294         elif GuiName2.match(line):
295             label = GuiName2.match(line).group(1)
296             shortcut = GuiName2.match(line).group(2).replace('"', '')
297         else:
298             continue
299         label = label.replace('\\', '\\\\').replace('"', '')
300         if shortcut != "":
301             labelsc = label + "|" + shortcut
302         if label != "":
303             print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
304                 (relativePath(input_files[0], base), lineno+1, label)
305         if labelsc != "":
306             print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
307                 (relativePath(input_files[0], base), lineno+1, labelsc)
308     input.close()
309     output.close()
310
311
312 def encodings_l10n(input_files, output, base):
313     '''Generate pot file from lib/encodings'''
314     output = open(output, 'w')
315     # assuming only one encodings file
316     #                 Encoding utf8      utf8    "Unicode (utf8)" UTF-8    variable inputenc
317     reg = re.compile('Encoding [\w-]+\s+[\w-]+\s+"([\w \-\(\)]+)"\s+[\w-]+\s+(fixed|variable)\s+\w+.*')
318     input = open(input_files[0])
319     for lineno, line in enumerate(input.readlines()):
320         if not line.startswith('Encoding'):
321             continue
322         if reg.match(line):
323             print >> output, '#: %s:%d\nmsgid "%s"\nmsgstr ""\n' % \
324                 (relativePath(input_files[0], base), lineno+1, reg.match(line).groups()[0])
325         else:
326             print "Error: Unable to handle line:"
327             print line
328             # No need to abort if the parsing fails
329             # sys.exit(1)
330     input.close()
331     output.close()
332
333
334
335 Usage = '''
336 lyx_pot.py [-b|--base top_src_dir] [-o|--output output_file] [-h|--help] [-s|src_file filename] -t|--type input_type input_files
337
338 where
339     --base:
340         path to the top source directory. default to '.'
341     --output:
342         output pot file, default to './lyx.pot'
343     --src_file
344         filename that contains a list of input files in each line
345     --input_type can be
346         ui: lib/ui/*
347         layouts: lib/layouts/*
348         qt4: qt4 ui files
349         languages: file lib/languages
350         encodings: file lib/encodings
351         external: external templates file
352         formats: formats predefined in lib/configure.py
353 '''
354
355 if __name__ == '__main__':
356     input_type = None
357     output = 'lyx.pot'
358     base = '.'
359     input_files = []
360     #
361     optlist, args = getopt.getopt(sys.argv[1:], 'ht:o:b:s:',
362         ['help', 'type=', 'output=', 'base=', 'src_file='])
363     for (opt, value) in optlist:
364         if opt in ['-h', '--help']:
365             print Usage
366             sys.exit(0)
367         elif opt in ['-o', '--output']:
368             output = value
369         elif opt in ['-b', '--base']:
370             base = value
371         elif opt in ['-t', '--type']:
372             input_type = value
373         elif opt in ['-s', '--src_file']:
374             input_files = [f.strip() for f in open(value)]
375
376     if input_type not in ['ui', 'layouts', 'modules', 'qt4', 'languages', 'encodings', 'external', 'formats'] or output is None:
377         print 'Wrong input type or output filename.'
378         sys.exit(1)
379
380     input_files += args
381
382     if input_type == 'ui':
383         ui_l10n(input_files, output, base)
384     elif input_type == 'layouts':
385         layouts_l10n(input_files, output, base)
386     elif input_type == 'qt4':
387         qt4_l10n(input_files, output, base)
388     elif input_type == 'external':
389         external_l10n(input_files, output, base)
390     elif input_type == 'formats':
391         formats_l10n(input_files, output, base)
392     elif input_type == 'encodings':
393         encodings_l10n(input_files, output, base)
394     else:
395         languages_l10n(input_files, output, base)
396
397