Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / l10ntools / scripts / po2lo
blobcdf8892fc950a518eb2e73da71104e3d89ec41e5
1 #!/usr/bin/env python
2 # Version: MPL 1.1 / GPLv3+ / LGPLv3+
4 # The contents of this file are subject to the Mozilla Public License Version
5 # 1.1 (the "License"); you may not use this file except in compliance with
6 # the License or as specified alternatively below. You may obtain a copy of
7 # the License at http://www.mozilla.org/MPL/
9 # Software distributed under the License is distributed on an "AS IS" basis,
10 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 # for the specific language governing rights and limitations under the
12 # License.
14 # The Initial Developer of the Original Code is
15 # Miklos Vajna <vmiklos@frugalware.org>
16 # Portions created by the Initial Developer are Copyright (C) 2011 the
17 # Initial Developer. All Rights Reserved.
19 # Major Contributor(s):
21 # For minor contributions see the git repository.
23 # Alternatively, the contents of this file may be used under the terms of
24 # either the GNU General Public License Version 3 or later (the "GPLv3+"), or
25 # the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
26 # in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
27 # instead of those above.
29 import getopt, sys, os, re
31 class Options:
32 """Options of this script."""
34 def __init__(self):
35 self.input = None
36 self.output = None
37 self.language = None
38 self.template = None
40 class Entry:
41 """Represents a single line in an SDF file."""
43 def __init__(self, items):
44 self.items = items # list of 15 fields
45 path = self.items[1].split('\\')
46 self.po = "%s/%s/%s.po" % (options.input.replace('\\', '/'), self.items[0], "/".join(path[:-1]))
47 prefix = ""
48 if len(self.items[5]):
49 prefix += "%s." % self.items[5]
50 if len(self.items[3]):
51 prefix += "%s." % self.items[3]
52 self.keys = []
53 # 10..13 are translation types
54 for idx in [10,12,13]:
55 try:
56 if len(self.items[idx]):
57 t = {10:'text', 12:'quickhelptext', 13:'title'}[idx]
58 self.keys.append((idx, self.sdf2po("%s#%s.%s%s" % (path[-1], self.items[4], prefix, t))))
59 except IndexError:
60 print("Tried to access field #%d in sdf file, but no such column! Broken sdf file?" % idx)
61 print("Fields: %s" % self.items)
62 sys.exit(1)
64 def translate(self, translations):
65 """Translates text in the entry based on translations."""
67 self.items[9] = options.language
68 for idx, key in self.keys:
69 try:
70 self.items[idx] = translations.data[(self.po, key)]
72 self.items[14] = "2002-02-02 02:02:02"
73 except KeyError:
74 pass
75 self.items[14] = self.items[14].strip()
77 def sdf2po(self, s):
78 """Escapes special chars in po key names."""
80 return s.translate(normalizetable)
82 class Template:
83 """Represents a reference template in SDF format."""
85 def __init__(self, path):
86 sock = xopen(path, "r", encoding='utf-8')
87 self.lines = []
88 for line in sock:
89 entry = Entry(line.split('\t'))
90 if os.path.exists(entry.po):
91 self.lines.append(entry)
93 def translate(self, translations):
94 """Translates entires in the template based on translations."""
96 sock = xopen(options.output, "w", encoding='utf-8')
97 for line in self.lines:
98 line.translate(translations)
99 sock.write("\t".join(line.items)+"\r\n")
100 sock.close()
102 class Translations:
103 """Represents a set of .po files, containing translations."""
105 def __init__(self):
106 self.data = {}
107 for root, dirs, files in os.walk(options.input):
108 for file in files:
109 path = "%s/%s" % (root, file)
110 sock = xopen(path, "r", encoding='utf-8')
111 key = None
112 buf = []
113 multiline = False
114 fuzzy = False
115 for line in sock:
116 if line.startswith("#: "):
117 key = line.strip()[3:]
118 elif line.startswith("#, fuzzy"):
119 fuzzy = True
120 elif line.startswith("msgstr "):
121 trans = line.strip()[8:-1]
122 if len(trans):
123 if fuzzy:
124 fuzzy = False
125 else:
126 self.setdata(path, key, trans)
127 multiline = False
128 else:
129 buf = []
130 buf.append(trans)
131 multiline = True
132 elif multiline and line.startswith('"'):
133 buf.append(line.strip()[1:-1])
134 elif multiline and not len(line.strip()) and len("".join(buf)):
135 if fuzzy:
136 fuzzy = False
137 else:
138 self.setdata(path, key, "".join(buf))
139 buf = []
140 multiline = False
141 if multiline and len("".join(buf)) and not fuzzy:
142 self.setdata(path, key, "".join(buf))
144 def setdata(self, path, key, s):
145 """Sets the translation for a given path and key, handling (un)escaping
146 as well."""
147 if key:
148 # unescape the po special chars
149 s = s.replace('\\"', '"')
150 if key.split('#')[0].endswith(".xhp"):
151 s = self.escape_help_text(s)
152 else:
153 s = s.replace('\\\\', '\\')
154 self.data[(path.replace('\\', '/'), key)] = s
156 def escape_help_text(self, text):
157 """Escapes the help text as it would be in an SDF file."""
159 for tag in helptagre.findall(text):
160 # <, >, " are only escaped in <[[:lower:]]> tags. Some HTML tags make it in in
161 # lowercase so those are dealt with. Some LibreOffice help tags are not
162 # escaped.
163 escapethistag = False
164 for escape_tag in ["ahelp", "link", "item", "emph", "defaultinline", "switchinline", "caseinline", "variable", "bookmark_value", "image", "embedvar", "alt"]:
165 if tag.startswith("<%s" % escape_tag) or tag == "</%s>" % escape_tag:
166 escapethistag = True
167 if tag in ["<br/>", "<help-id-missing/>"]:
168 escapethistag = True
169 if escapethistag:
170 escaped_tag = ("\\<" + tag[1:-1] + "\\>").replace('"', '\\"')
171 text = text.replace(tag, escaped_tag)
172 return text
174 def xopen(path, mode, encoding):
175 """Wrapper around open() to support both python2 and python3."""
176 if sys.version_info >= (3,):
177 return open(path, mode, encoding=encoding)
178 else:
179 return open(path, mode)
181 def main():
182 """Main function of this script."""
184 opts, args = getopt.getopt(sys.argv[1:], "si:o:l:t:", ["skipsource", "input=", "output=", "language=", "template="])
185 for opt, arg in opts:
186 if opt in ("-s", "--skipsource"):
187 pass
188 elif opt in ("-i", "--input"):
189 options.input = arg
190 elif opt in ("-o", "--output"):
191 options.output = arg
192 elif opt in ("-l", "--language"):
193 options.language = arg
194 elif opt in ("-t", "--template"):
195 options.template = arg
196 template = Template(options.template)
197 translations = Translations()
198 template.translate(translations)
200 # used by ecape_help_text
201 helptagre = re.compile('''<[/]??[a-z_\-]+?(?:| +[a-z]+?=".*?") *[/]??>''')
203 options = Options()
205 # used by sdf2po()
206 normalfilenamechars = "/#.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
207 normalizetable = ""
208 for i in map(chr, list(range(256))):
209 if i in normalfilenamechars:
210 normalizetable += i
211 else:
212 normalizetable += "_"
214 if __name__ == "__main__":
215 main()
217 # vim:set filetype=python shiftwidth=4 softtabstop=4 expandtab: