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