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
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
32 """Options of this script."""
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]))
48 if len(self
.items
[5]):
49 prefix
+= "%s." % self
.items
[5]
50 if len(self
.items
[3]):
51 prefix
+= "%s." % self
.items
[3]
53 # 10..13 are translation types
54 for idx
in [10,12,13]:
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
))))
60 print("Tried to access field #%d in sdf file, but no such column! Broken sdf file?" % idx
)
61 print("Fields: %s" % self
.items
)
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
:
70 self
.items
[idx
] = translations
.data
[(self
.po
, key
)]
72 self
.items
[14] = "2002-02-02 02:02:02"
75 self
.items
[14] = self
.items
[14].strip()
78 """Escapes special chars in po key names."""
80 return s
.translate(normalizetable
)
83 """Represents a reference template in SDF format."""
85 def __init__(self
, path
):
86 sock
= xopen(path
, "r", encoding
='utf-8')
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")
103 """Represents a set of .po files, containing translations."""
107 for root
, dirs
, files
in os
.walk(options
.input):
109 path
= "%s/%s" % (root
, file)
110 sock
= xopen(path
, "r", encoding
='utf-8')
116 if line
.startswith("#: "):
117 key
= line
.strip()[3:]
118 elif line
.startswith("#, fuzzy"):
120 elif line
.startswith("msgstr "):
121 trans
= line
.strip()[8:-1]
126 self
.setdata(path
, key
, trans
)
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
)):
138 self
.setdata(path
, key
, "".join(buf
))
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
148 # unescape the po special chars
149 s
= s
.replace('\\"', '"')
150 if key
.split('#')[0].endswith(".xhp"):
151 s
= self
.escape_help_text(s
)
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
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
:
167 if tag
in ["<br/>", "<help-id-missing/>"]:
170 escaped_tag
= ("\\<" + tag
[1:-1] + "\\>").replace('"', '\\"')
171 text
= text
.replace(tag
, escaped_tag
)
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
)
179 return open(path
, mode
)
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"):
188 elif opt
in ("-i", "--input"):
190 elif opt
in ("-o", "--output"):
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]+?=".*?") *[/]??>''')
206 normalfilenamechars
= "/#.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
208 for i
in map(chr, list(range(256))):
209 if i
in normalfilenamechars
:
212 normalizetable
+= "_"
214 if __name__
== "__main__":
217 # vim:set filetype=python shiftwidth=4 softtabstop=4 expandtab: