office-gobmx/l10ntools/scripts/po2lo

234 lines
8.6 KiB
Text
Raw Normal View History

2011-09-07 16:39:15 -05:00
#!/usr/bin/env python
# Version: MPL 1.1 / GPLv3+ / LGPLv3+
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License or as specified alternatively below. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Initial Developer of the Original Code is
# Miklos Vajna <vmiklos@frugalware.org>
# Portions created by the Initial Developer are Copyright (C) 2011 the
# Initial Developer. All Rights Reserved.
#
# Major Contributor(s):
#
# For minor contributions see the git repository.
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 3 or later (the "GPLv3+"), or
# the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
# in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
# instead of those above.
import getopt, sys, os, re
class Options:
"""Options of this script."""
def __init__(self):
self.input = None
self.output = None
self.language = None
self.template = None
class Entry:
"""Represents a single line in an SDF file."""
def __init__(self, items):
self.has_po = None
2011-09-07 16:39:15 -05:00
self.items = items # list of 15 fields
path = self.items[1].split('\\')
self.po = "%s/%s/%s.po" % (options.input.replace('\\', '/'), self.items[0], "/".join(path[:-1]))
prefix = ""
if len(self.items[5]):
prefix += "%s." % self.items[5]
if len(self.items[3]):
prefix += "%s." % self.items[3]
self.keys = []
# 10..13 are translation types
for idx in [10,12,13]:
try:
if len(self.items[idx]):
t = {10:'text', 12:'quickhelptext', 13:'title'}[idx]
self.keys.append((idx, self.sdf2po("%s#%s.%s%s" % (path[-1], self.items[4], prefix, t))))
except IndexError:
print("Tried to access field #%d in sdf file, but no such column! Broken sdf file?" % idx)
print("Fields: %s" % self.items)
sys.exit(1)
2011-09-07 16:39:15 -05:00
def translate(self, translations):
"""Translates text in the entry based on translations."""
self.items[9] = options.language
self.items[2] = ""
self.has_po = False
2011-09-07 16:39:15 -05:00
for idx, key in self.keys:
try:
self.items[8] = str(translations.snumber[(self.po, key)])
self.has_po = True
(text, fuzzy) = translations.data[(self.po, key)]
if fuzzy:
self.items[2] += "1"
else:
self.items[2] += "0"
self.items[idx] = text
2011-09-07 16:39:15 -05:00
self.items[14] = "2002-02-02 02:02:02"
except KeyError:
self.items[idx]=""
2011-09-07 16:39:15 -05:00
self.items[14] = self.items[14].strip()
def sdf2po(self, s):
"""Escapes special chars in po key names."""
return s.translate(normalizetable)
class Template:
"""Represents a reference template in SDF format."""
def __init__(self, path):
sock = xopen(path, "r", encoding='utf-8')
2011-09-07 16:39:15 -05:00
self.lines = []
for line in sock:
entry = Entry(line.split('\t'))
if os.path.exists(entry.po):
self.lines.append(entry)
def translate(self, translations):
"""Translates entires in the template based on translations."""
sock = xopen(options.output, "w", encoding='utf-8')
2011-09-07 16:39:15 -05:00
for line in self.lines:
temp = "\t".join(line.items)
2011-09-07 16:39:15 -05:00
line.translate(translations)
if line.has_po:
sock.write(temp)
sock.write("\t".join(line.items)+"\r\n")
2011-09-07 16:39:15 -05:00
sock.close()
class Translations:
"""Represents a set of .po files, containing translations."""
def __init__(self):
self.data = {}
self.snumber = {}
counter = 0
2011-09-07 16:39:15 -05:00
for root, dirs, files in os.walk(options.input):
for file in files:
path = "%s/%s" % (root, file)
sock = xopen(path, "r", encoding='utf-8')
key = None
2011-09-07 16:39:15 -05:00
buf = []
multiline = False
fuzzy = False
for line in sock:
if line.startswith("#: "):
key = line.strip()[3:]
fuzzy = False
2011-09-07 16:39:15 -05:00
elif line.startswith("#, fuzzy"):
fuzzy = True
elif line.startswith("msgid "):
counter = counter + 1
self.setserialnumber(path, key, counter)
2011-09-07 16:39:15 -05:00
elif line.startswith("msgstr "):
trans = line.strip()[8:-1]
if len(trans):
self.setdata(path, key, trans, fuzzy)
multiline = False
2011-09-07 16:39:15 -05:00
else:
buf = []
buf.append(trans)
multiline = True
elif multiline and line.startswith('"'):
buf.append(line.strip()[1:-1])
elif multiline and not len(line.strip()) and len("".join(buf)):
self.setdata(path, key, "".join(buf),fuzzy)
2011-09-07 16:39:15 -05:00
buf = []
multiline = False
if multiline and len("".join(buf)):
self.setdata(path, key, "".join(buf),fuzzy)
2011-09-07 16:39:15 -05:00
def setdata(self, path, key, s, fuzzy = False):
2011-09-07 16:39:15 -05:00
"""Sets the translation for a given path and key, handling (un)escaping
as well."""
if key:
# unescape the po special chars
s = s.replace('\\"', '"')
if key.split('#')[0].endswith(".xhp"):
s = self.escape_help_text(s)
else:
s = s.replace('\\\\', '\\')
self.data[(path.replace('\\', '/'), key)] = ( s , fuzzy )
2011-09-07 16:39:15 -05:00
def setserialnumber(self, path, key, number):
self.snumber[(path.replace('\\', '/'), key)] = ( number )
2011-09-07 16:39:15 -05:00
def escape_help_text(self, text):
"""Escapes the help text as it would be in an SDF file."""
for tag in helptagre.findall(text):
# <, >, " are only escaped in <[[:lower:]]> tags. Some HTML tags make it in in
# lowercase so those are dealt with. Some LibreOffice help tags are not
# escaped.
escapethistag = False
for escape_tag in ["ahelp", "link", "item", "emph", "defaultinline", "switchinline", "caseinline", "variable", "bookmark_value", "image", "embedvar", "alt"]:
if tag.startswith("<%s" % escape_tag) or tag == "</%s>" % escape_tag:
escapethistag = True
if tag in ["<br/>", "<help-id-missing/>"]:
escapethistag = True
if escapethistag:
escaped_tag = ("\\<" + tag[1:-1] + "\\>").replace('"', '\\"')
text = text.replace(tag, escaped_tag)
return text
def xopen(path, mode, encoding):
"""Wrapper around open() to support both python2 and python3."""
if sys.version_info >= (3,):
return open(path, mode, encoding=encoding)
else:
return open(path, mode)
2011-09-07 16:39:15 -05:00
def main():
"""Main function of this script."""
opts, args = getopt.getopt(sys.argv[1:], "si:o:l:t:", ["skipsource", "input=", "output=", "language=", "template="])
for opt, arg in opts:
if opt in ("-s", "--skipsource"):
pass
elif opt in ("-i", "--input"):
2012-02-24 16:35:56 -06:00
options.input = arg
2011-09-07 16:39:15 -05:00
elif opt in ("-o", "--output"):
options.output = arg
elif opt in ("-l", "--language"):
options.language = arg
elif opt in ("-t", "--template"):
options.template = arg
template = Template(options.template)
translations = Translations()
template.translate(translations)
# used by ecape_help_text
helptagre = re.compile('''<[/]??[a-z_\-]+?(?:| +[a-z]+?=".*?") *[/]??>''')
options = Options()
# used by sdf2po()
normalfilenamechars = "/#.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
normalizetable = ""
for i in map(chr, list(range(256))):
2011-09-07 16:39:15 -05:00
if i in normalfilenamechars:
normalizetable += i
else:
normalizetable += "_"
if __name__ == "__main__":
main()
# vim:set filetype=python shiftwidth=4 softtabstop=4 expandtab: