diff options
Diffstat (limited to 'translate-toolkit-1.5.1/translate/convert')
118 files changed, 11749 insertions, 0 deletions
diff --git a/translate-toolkit-1.5.1/translate/convert/TODO b/translate-toolkit-1.5.1/translate/convert/TODO new file mode 100644 index 0000000..872582d --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/TODO @@ -0,0 +1,42 @@ +--update option + make recursive methods only run if input is newer than output +--force option + only overwrite files if this is given (work out which tools its applicable to) + +detect whether producing a .po or a .pot (make -P / --pot unneccessary) + +moz2po + make strings fuzzy if original == translation + +headers + charset=UTF-8 + +copyright + need to insert the standard copyright text unitialised + +csv2po + * change order of columns: original, translation, other stuff + * It does not currently reflow text to create a newline around literal \n + making it difficult to check files + * allow a --no-wrap option so that we remove PO line breaks that mess with + translators segmentation tools. + * We do not handle languages with N != 2 plural forms + +oo2po / po2oo + fix problem with OOO_VENDOR and \" quoting in offmgr.po (see help - about) + make oo output order the same as in input file... + +pot2po discussion with pavel: + pot2po should be as simple as using pot as one arg and po as the second ;-) + pot2po pot po -t po warns me for every file: pot2po: warning: writing to temporary output... + pot2po pot po -t po does not merge existing translations from old POs that have more then one comment and thus are duplicated in the new POs + +docs + not sure where else to put this! + docs are installed in /usr/lib/python2.3/site-packages/translate/doc + should be /usr/share/docs/translate-toolkit-0.8 + Not sure what's needed on Windows - non critical + +po2dtd + when reporting ampersand check failures should tell you the file not just the + name of the bad PO file not just the command line output name diff --git a/translate-toolkit-1.5.1/translate/convert/__init__.py b/translate-toolkit-1.5.1/translate/convert/__init__.py new file mode 100644 index 0000000..a1e2e07 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/__init__.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2005 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""translate.convert is part of the translate package +It contains code to convert between different storage formats for localizations + +@group XLIFF: *xliff* +@group Bilingual: pot2po po2tmx oo2po po2oo csv2tbx *wordfast* *ts* +@group Monolingual: *prop* *dtd* csv2po po2csv *html* *ical* *ini* *rc* *txt* moz2po po2moz *php* *sub* *symb* *monopo* *tiki* *web2py* *lang* +@group Support: accesskey convert +@group Other: poreplace +""" + diff --git a/translate-toolkit-1.5.1/translate/convert/accesskey.py b/translate-toolkit-1.5.1/translate/convert/accesskey.py new file mode 100644 index 0000000..5c076ab --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/accesskey.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2008 Zuza Software Foundation +# +# This file is part of The Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""functions used to manipulate access keys in strings""" + +from translate.storage.placeables.general import XMLEntityPlaceable + +DEFAULT_ACCESSKEY_MARKER = u"&" + +def extract(string, accesskey_marker=DEFAULT_ACCESSKEY_MARKER): + """Extract the label and accesskey from a label+accesskey string + + The function will also try to ignore &entities; which would obviously not + contain accesskeys. + + @type string: Unicode + @param string: A string that might contain a label with accesskey marker + @type accesskey_marker: Char + @param accesskey_marker: The character that is used to prefix an access key + """ + assert isinstance(string, unicode) + assert isinstance(accesskey_marker, unicode) + assert len(accesskey_marker) == 1 + if string == u"": + return u"", u"" + accesskey = u"" + label = string + marker_pos = 0 + while marker_pos >= 0: + marker_pos = string.find(accesskey_marker, marker_pos) + if marker_pos != -1: + marker_pos += 1 + if accesskey_marker == '&' and XMLEntityPlaceable.regex.match(string[marker_pos-1:]): + continue + label = string[:marker_pos-1] + string[marker_pos:] + accesskey = string[marker_pos] + break + return label, accesskey + +def combine(label, accesskey, + accesskey_marker=DEFAULT_ACCESSKEY_MARKER): + """Combine a label and and accesskey to form a label+accesskey string + + We place an accesskey marker before the accesskey in the label and this creates a + string with the two combined e.g. "File" + "F" = "&File" + + @type label: unicode + @param label: a label + @type accesskey: unicode char + @param accesskey: The accesskey + @rtype: unicode or None + @return: label+accesskey string or None if uncombineable + """ + assert isinstance(label, unicode) + assert isinstance(accesskey, unicode) + if len(accesskey) == 0: + return None + searchpos = 0 + accesskeypos = -1 + in_entity = False + accesskeyaltcasepos = -1 + while (accesskeypos < 0) and searchpos < len(label): + searchchar = label[searchpos] + if searchchar == '&': + in_entity = True + elif searchchar == ';': + in_entity = False + else: + if not in_entity: + if searchchar == accesskey.upper(): + # always prefer uppercase + accesskeypos = searchpos + if searchchar == accesskey.lower(): + # take lower case otherwise... + if accesskeyaltcasepos == -1: + # only want to remember first altcasepos + accesskeyaltcasepos = searchpos + # note: we keep on looping through in hope + # of exact match + searchpos += 1 + # if we didn't find an exact case match, use an alternate one if available + if accesskeypos == -1: + accesskeypos = accesskeyaltcasepos + # now we want to handle whatever we found... + if accesskeypos >= 0: + string = label[:accesskeypos] + accesskey_marker + label[accesskeypos:] + string = string.encode("UTF-8", "replace") + return string + else: + # can't currently mix accesskey if it's not in label + return None diff --git a/translate-toolkit-1.5.1/translate/convert/convert.py b/translate-toolkit-1.5.1/translate/convert/convert.py new file mode 100644 index 0000000..9796031 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/convert.py @@ -0,0 +1,371 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""Handles converting of files between formats (used by translate.convert tools)""" + +import os.path +from translate.misc import optrecurse +# don't import optparse ourselves, get the version from optrecurse +optparse = optrecurse.optparse +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +class ConvertOptionParser(optrecurse.RecursiveOptionParser, object): + """a specialized Option Parser for convertor tools...""" + def __init__(self, formats, usetemplates=False, usepots=False, allowmissingtemplate=False, description=None): + """construct the specialized Option Parser""" + optrecurse.RecursiveOptionParser.__init__(self, formats, usetemplates, + allowmissingtemplate=allowmissingtemplate, description=description) + self.usepots = usepots + self.setpotoption() + self.set_usage() + + def add_fuzzy_option(self, default=False): + """adds an option to include / exclude fuzzy translations""" + fuzzyhelp = "use translations marked fuzzy" + nofuzzyhelp = "don't use translations marked fuzzy" + if default: + fuzzyhelp += " (default)" + else: + nofuzzyhelp += " (default)" + self.add_option("", "--fuzzy", dest="includefuzzy", action="store_true", default=default, help=fuzzyhelp) + self.add_option("", "--nofuzzy", dest="includefuzzy", action="store_false", default=default, help=nofuzzyhelp) + self.passthrough.append("includefuzzy") + + def add_duplicates_option(self, default="msgctxt"): + """adds an option to say what to do with duplicate strings""" + self.add_option("", "--duplicates", dest="duplicatestyle", default=default, + type="choice", choices=["msgctxt", "merge"], + help="what to do with duplicate strings (identical source text): merge, msgctxt (default: '%s')" % default, metavar="DUPLICATESTYLE") + self.passthrough.append("duplicatestyle") + + def add_multifile_option(self, default="single"): + """adds an option to say how to split the po/pot files""" + self.add_option("", "--multifile", dest="multifilestyle", default=default, + type="choice", choices=["single", "toplevel", "onefile"], + help="how to split po/pot files (single, toplevel or onefile)", metavar="MULTIFILESTYLE") + self.passthrough.append("multifilestyle") + + def potifyformat(self, fileformat): + """converts a .po to a .pot where required""" + if fileformat is None: + return fileformat + elif fileformat == "po": + return "pot" + elif fileformat.endswith(os.extsep + "po"): + return fileformat + "t" + else: + return fileformat + + def getformathelp(self, formats): + """make a nice help string for describing formats...""" + # include implicit pot options... + helpformats = [] + for fileformat in formats: + helpformats.append(fileformat) + potformat = self.potifyformat(fileformat) + if potformat != fileformat: + helpformats.append(potformat) + return super(ConvertOptionParser, self).getformathelp(helpformats) + + def filterinputformats(self, options): + """filters input formats, processing relevant switches in options""" + if self.usepots and options.pot: + return [self.potifyformat(inputformat) for inputformat in self.inputformats] + else: + return self.inputformats + + def filteroutputoptions(self, options): + """filters output options, processing relevant switches in options""" + if self.usepots and options.pot: + outputoptions = {} + for (inputformat, templateformat), (outputformat, convertor) in self.outputoptions.iteritems(): + inputformat = self.potifyformat(inputformat) + templateformat = self.potifyformat(templateformat) + outputformat = self.potifyformat(outputformat) + outputoptions[(inputformat, templateformat)] = (outputformat, convertor) + return outputoptions + else: + return self.outputoptions + + def setpotoption(self): + """sets the -P/--pot option depending on input/output formats etc""" + if self.usepots: + potoption = optparse.Option("-P", "--pot", \ + action="store_true", dest="pot", default=False, \ + help="output PO Templates (.pot) rather than PO files (.po)") + self.define_option(potoption) + + def verifyoptions(self, options): + """verifies that the options are valid (required options are present, etc)""" + pass + + def run(self, argv=None): + """parses the command line options and runs the conversion""" + (options, args) = self.parse_args(argv) + options.inputformats = self.filterinputformats(options) + options.outputoptions = self.filteroutputoptions(options) + self.usepsyco(options) + self.verifyoptions(options) + self.recursiveprocess(options) + +def copyinput(inputfile, outputfile, templatefile, **kwargs): + """copies the input file to the output file""" + outputfile.write(inputfile.read()) + return True + +def copytemplate(inputfile, outputfile, templatefile, **kwargs): + """copies the template file to the output file""" + outputfile.write(templatefile.read()) + return True + +class Replacer: + """an object that knows how to replace strings in files""" + def __init__(self, searchstring, replacestring): + self.searchstring = searchstring + self.replacestring = replacestring + + def doreplace(self, text): + """actually replace the text""" + if self.searchstring is not None and self.replacestring is not None: + return text.replace(self.searchstring, self.replacestring) + else: + return text + + def searchreplaceinput(self, inputfile, outputfile, templatefile, **kwargs): + """copies the input file to the output file, searching and replacing""" + outputfile.write(self.doreplace(inputfile.read())) + return True + + def searchreplacetemplate(self, inputfile, outputfile, templatefile, **kwargs): + """copies the template file to the output file, searching and replacing""" + outputfile.write(self.doreplace(templatefile.read())) + return True + +# archive files need to know how to: +# - openarchive: creates an archive object for the archivefilename +# * requires a constructor that takes the filename +# - iterarchivefile: iterate through the names in the archivefile +# * requires the default iterator to do this +# - archivefileexists: check if a given pathname exists inside the archivefile +# * uses the in operator - requires __contains__ (or will use __iter__ by default) +# - openarchiveinputfile: returns an open input file from the archive, given the path +# * requires an archivefile.openinputfile method that takes the pathname +# - openarchiveoutputfile: returns an open output file from the archive, given the path +# * requires an archivefile.openoutputfile method that takes the pathname + +class ArchiveConvertOptionParser(ConvertOptionParser): + """ConvertOptionParser that can handle recursing into single archive files. + archiveformats maps extension to class. if the extension doesn't matter, it can be None. + if the extension is only valid for input/output/template, it can be given as (extension, filepurpose)""" + def __init__(self, formats, usetemplates=False, usepots=False, description=None, archiveformats=None): + if archiveformats is None: + self.archiveformats = {} + else: + self.archiveformats = archiveformats + self.archiveoptions = {} + ConvertOptionParser.__init__(self, formats, usetemplates, usepots, description=description) + + def setarchiveoptions(self, **kwargs): + """allows setting options that will always be passed to openarchive""" + self.archiveoptions = kwargs + + def isrecursive(self, fileoption, filepurpose='input'): + """checks if fileoption is a recursive file""" + if self.isarchive(fileoption, filepurpose): return True + return super(ArchiveConvertOptionParser, self).isrecursive(fileoption, filepurpose) + + def isarchive(self, fileoption, filepurpose='input'): + """returns whether the file option is an archive file""" + if not isinstance(fileoption, (str, unicode)): + return False + mustexist = (filepurpose != 'output') + if mustexist and not os.path.isfile(fileoption): + return False + fileext = self.splitext(fileoption)[1] + # if None is in the archive formats, then treat all non-directory inputs as archives + return self.getarchiveclass(fileext, filepurpose, os.path.isdir(fileoption)) is not None + + def getarchiveclass(self, fileext, filepurpose, isdir=False): + """returns the archiveclass for the given fileext and filepurpose""" + archiveclass = self.archiveformats.get(fileext, None) + if archiveclass is not None: + return archiveclass + archiveclass = self.archiveformats.get((fileext, filepurpose), None) + if archiveclass is not None: + return archiveclass + if not isdir: + archiveclass = self.archiveformats.get(None, None) + if archiveclass is not None: + return archiveclass + archiveclass = self.archiveformats.get((None, filepurpose), None) + if archiveclass is not None: + return archiveclass + return None + + def openarchive(self, archivefilename, filepurpose, **kwargs): + """creates an archive object for the given file""" + archiveext = self.splitext(archivefilename)[1] + archiveclass = self.getarchiveclass(archiveext, filepurpose, os.path.isdir(archivefilename)) + archiveoptions = self.archiveoptions.copy() + archiveoptions.update(kwargs) + return archiveclass(archivefilename, **archiveoptions) + + def recurseinputfiles(self, options): + """recurse through archive file / directories and return files to be converted""" + if self.isarchive(options.input, 'input'): + options.inputarchive = self.openarchive(options.input, 'input') + return self.recursearchivefiles(options) + else: + return super(ArchiveConvertOptionParser, self).recurseinputfiles(options) + + def recursearchivefiles(self, options): + """recurse through archive files and convert files""" + inputfiles = [] + for inputpath in options.inputarchive: + if self.isexcluded(options, inputpath): + continue + top, name = os.path.split(inputpath) + if not self.isvalidinputname(options, name): + continue + inputfiles.append(inputpath) + return inputfiles + + def openinputfile(self, options, fullinputpath): + """opens the input file""" + if self.isarchive(options.input, 'input'): + return options.inputarchive.openinputfile(fullinputpath) + else: + return super(ArchiveConvertOptionParser, self).openinputfile(options, fullinputpath) + + def getfullinputpath(self, options, inputpath): + """gets the absolute path to an input file""" + if self.isarchive(options.input, 'input'): + return inputpath + else: + return os.path.join(options.input, inputpath) + + def opentemplatefile(self, options, fulltemplatepath): + """opens the template file (if required)""" + if fulltemplatepath is not None: + if options.recursivetemplate and self.isarchive(options.template, 'template'): + # TODO: deal with different names in input/template archives + if fulltemplatepath in options.templatearchive: + return options.templatearchive.openinputfile(fulltemplatepath) + else: + self.warning("missing template file %s" % fulltemplatepath) + return super(ArchiveConvertOptionParser, self).opentemplatefile(options, fulltemplatepath) + + def getfulltemplatepath(self, options, templatepath): + """gets the absolute path to a template file""" + if templatepath is not None and self.usetemplates and options.template: + if self.isarchive(options.template, 'template'): + return templatepath + elif not options.recursivetemplate: + return templatepath + else: + return os.path.join(options.template, templatepath) + else: + return None + + def templateexists(self, options, templatepath): + """returns whether the given template exists...""" + if templatepath is not None: + if self.isarchive(options.template, 'template'): + # TODO: deal with different names in input/template archives + return templatepath in options.templatearchive + return super(ArchiveConvertOptionParser, self).templateexists(options, templatepath) + + def getfulloutputpath(self, options, outputpath): + """gets the absolute path to an output file""" + if self.isarchive(options.output, 'output'): + return outputpath + elif options.recursiveoutput and options.output: + return os.path.join(options.output, outputpath) + else: + return outputpath + + def checkoutputsubdir(self, options, subdir): + """checks to see if subdir under options.output needs to be created, creates if neccessary""" + if not self.isarchive(options.output, 'output'): + super(ArchiveConvertOptionParser, self).checkoutputsubdir(options, subdir) + + def openoutputfile(self, options, fulloutputpath): + """opens the output file""" + if self.isarchive(options.output, 'output'): + outputstream = options.outputarchive.openoutputfile(fulloutputpath) + if outputstream is None: + self.warning("Could not find where to put %s in output archive; writing to tmp" % fulloutputpath) + return StringIO() + return outputstream + else: + return super(ArchiveConvertOptionParser, self).openoutputfile(options, fulloutputpath) + + def inittemplatearchive(self, options): + """opens the templatearchive if not already open""" + if not self.usetemplates: + return + if options.template and self.isarchive(options.template, 'template') and not hasattr(options, "templatearchive"): + options.templatearchive = self.openarchive(options.template, 'template') + + def initoutputarchive(self, options): + """creates an outputarchive if required""" + if options.output and self.isarchive(options.output, 'output'): + options.outputarchive = self.openarchive(options.output, 'output', mode="w") + + def recursiveprocess(self, options): + """recurse through directories and convert files""" + if hasattr(options, "multifilestyle"): + self.setarchiveoptions(multifilestyle=options.multifilestyle) + for filetype in ("input", "output", "template"): + allowoption = "allowrecursive%s" % filetype + if options.multifilestyle == "onefile" and getattr(options, allowoption, True): + setattr(options, allowoption, False) + self.inittemplatearchive(options) + self.initoutputarchive(options) + return super(ArchiveConvertOptionParser, self).recursiveprocess(options) + + def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath): + """run an invidividual conversion""" + if self.isarchive(options.output, 'output'): + inputfile = self.openinputfile(options, fullinputpath) + # TODO: handle writing back to same archive as input/template + templatefile = self.opentemplatefile(options, fulltemplatepath) + outputfile = self.openoutputfile(options, fulloutputpath) + passthroughoptions = self.getpassthroughoptions(options) + if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions): + if not outputfile.isatty(): + outputfile.close() + return True + else: + if fulloutputpath and os.path.isfile(fulloutputpath): + outputfile.close() + os.unlink(fulloutputpath) + return False + else: + return super(ArchiveConvertOptionParser, self).processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath) + +def main(argv=None): + parser = ArchiveConvertOptionParser({}, description=__doc__) + parser.run(argv) + diff --git a/translate-toolkit-1.5.1/translate/convert/csv2po b/translate-toolkit-1.5.1/translate/convert/csv2po new file mode 100755 index 0000000..17192c2 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/csv2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2003, 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a comma-separated values (.csv) file to a gettext .po localization file""" + +from translate.convert import csv2po + +if __name__ == '__main__': + csv2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/csv2po.py b/translate-toolkit-1.5.1/translate/convert/csv2po.py new file mode 100644 index 0000000..9ad3d92 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/csv2po.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2003-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert Comma-Separated Value (.csv) files to Gettext PO localization files + +See: http://translate.sourceforge.net/wiki/toolkit/csv2po for examples and +usage instructions +""" + +import sys +from translate.misc import sparse +from translate.storage import po +from translate.storage import csvl10n + +def replacestrings(source, *pairs): + for orig, new in pairs: + source = source.replace(orig, new) + return source + +def quotecsvstr(source): + return '"' + replacestrings(source, ('\\"','"'), ('"','\\"'), ("\\\\'", "\\'"), ('\\\\n', '\\n')) + '"' + +def simplify(string): + return filter(type(string).isalnum, string) + tokens = sparse.SimpleParser().tokenize(string) + return " ".join(tokens) + +class csv2po: + """a class that takes translations from a .csv file and puts them in a .po file""" + def __init__(self, templatepo=None, charset=None, duplicatestyle="keep"): + """construct the converter...""" + self.pofile = templatepo + self.charset = charset + self.duplicatestyle = duplicatestyle + if self.pofile is not None: + self.unmatched = 0 + self.makeindex() + + def makeindex(self): + """makes indexes required for searching...""" + self.commentindex = {} + self.sourceindex = {} + self.simpleindex = {} + self.duplicatecomments = [] + for pounit in self.pofile.units: + joinedcomment = " ".join(pounit.getlocations()) + source = pounit.source + # the definitive way to match is by source comment (joinedcomment) + if joinedcomment in self.commentindex: + # unless more than one thing matches... + self.duplicatecomments.append(joinedcomment) + else: + self.commentindex[joinedcomment] = pounit + # do simpler matching in case things have been mangled... + simpleid = simplify(source) + # but check for duplicates + if simpleid in self.simpleindex and not (source in self.sourceindex): + # keep a list of them... + self.simpleindex[simpleid].append(pounit) + else: + self.simpleindex[simpleid] = [pounit] + # also match by standard msgid + self.sourceindex[source] = pounit + for comment in self.duplicatecomments: + if comment in self.commentindex: + del self.commentindex[comment] + + def convertunit(self, csvunit): + """converts csv unit to po unit""" + pounit = po.pounit(encoding="UTF-8") + if csvunit.comment: + pounit.addlocation(csvunit.comment) + pounit.source = csvunit.source + pounit.target = csvunit.target + return pounit + + def handlecsvunit(self, csvunit): + """handles reintegrating a csv unit into the .po file""" + if len(csvunit.comment.strip()) > 0 and csvunit.comment in self.commentindex: + pounit = self.commentindex[csvunit.comment] + elif csvunit.source in self.sourceindex: + pounit = self.sourceindex[csvunit.source] + elif simplify(csvunit.source) in self.simpleindex: + thepolist = self.simpleindex[simplify(csvunit.source)] + if len(thepolist) > 1: + csvfilename = getattr(self.csvfile, "filename", "(unknown)") + matches = "\n ".join(["possible match: " + pounit.source for pounit in thepolist]) + print >> sys.stderr, "%s - csv entry not found in pofile, multiple matches found:\n location\t%s\n original\t%s\n translation\t%s\n %s" % (csvfilename, csvunit.comment, csvunit.source, csvunit.target, matches) + self.unmatched += 1 + return + pounit = thepolist[0] + else: + csvfilename = getattr(self.csvfile, "filename", "(unknown)") + print >> sys.stderr, "%s - csv entry not found in pofile:\n location\t%s\n original\t%s\n translation\t%s" % (csvfilename, csvunit.comment, csvunit.source, csvunit.target) + self.unmatched += 1 + return + if pounit.hasplural(): + # we need to work out whether we matched the singular or the plural + singularid = pounit.source.strings[0] + pluralid = pounit.source.strings[1] + if csvunit.source == singularid: + pounit.msgstr[0] = csvunit.target + elif csvunit.source == pluralid: + pounit.msgstr[1] = csvunit.target + elif simplify(csvunit.source) == simplify(singularid): + pounit.msgstr[0] = csvunit.target + elif simplify(csvunit.source) == simplify(pluralid): + pounit.msgstr[1] = csvunit.target + else: + print >> sys.stderr, "couldn't work out singular or plural: %r, %r, %r" % \ + (csvunit.source, singularid, pluralid) + self.unmatched += 1 + return + else: + pounit.target = csvunit.target + + def convertstore(self, thecsvfile): + """converts a csvfile to a pofile, and returns it. uses templatepo if given at construction""" + self.csvfile = thecsvfile + if self.pofile is None: + self.pofile = po.pofile() + mergemode = False + else: + mergemode = True + if self.pofile.units and self.pofile.units[0].isheader(): + targetheader = self.pofile.units[0] + targetheader.msgstr = [line.replace("CHARSET", "UTF-8").replace("ENCODING", "8bit") for line in targetheader.msgstr] + else: + targetheader = self.pofile.makeheader(charset="UTF-8", encoding="8bit") + targetheader.addnote("extracted from %s" % self.csvfile.filename, "developer") + mightbeheader = True + for csvunit in self.csvfile.units: + if self.charset is not None: + csvunit.source = csvunit.source.decode(self.charset) + csvunit.target = csvunit.target.decode(self.charset) + if mightbeheader: + # ignore typical header strings... + mightbeheader = False + if [item.strip().lower() for item in csvunit.comment, csvunit.source, csvunit.target] == \ + ["location", "source", "target"]: + continue + if len(csvunit.comment.strip()) == 0 and csvunit.source.find("Content-Type:") != -1: + continue + if mergemode: + self.handlecsvunit(csvunit) + else: + pounit = self.convertunit(csvunit) + self.pofile.addunit(pounit) + self.pofile.removeduplicates(self.duplicatestyle) + return self.pofile + +def convertcsv(inputfile, outputfile, templatefile, charset=None, columnorder=None, duplicatestyle="msgctxt"): + """reads in inputfile using csvl10n, converts using csv2po, writes to outputfile""" + inputstore = csvl10n.csvfile(inputfile, fieldnames=columnorder) + if templatefile is None: + convertor = csv2po(charset=charset, duplicatestyle=duplicatestyle) + else: + templatestore = po.pofile(templatefile) + convertor = csv2po(templatestore, charset=charset, duplicatestyle=duplicatestyle) + outputstore = convertor.convertstore(inputstore) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {("csv", "po"): ("po", convertcsv), ("csv", "pot"): ("po", convertcsv), + ("csv", None): ("po", convertcsv)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_option("", "--charset", dest="charset", default=None, + help="set charset to decode from csv files", metavar="CHARSET") + parser.add_option("", "--columnorder", dest="columnorder", default=None, + help="specify the order and position of columns (location,source,target)") + parser.add_duplicates_option() + parser.passthrough.append("charset") + parser.passthrough.append("columnorder") + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/csv2tbx b/translate-toolkit-1.5.1/translate/convert/csv2tbx new file mode 100755 index 0000000..deb9ef6 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/csv2tbx @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a comma-separated values (.csv) file to a .tbx glossary file""" + +from translate.convert import csv2tbx + +if __name__ == '__main__': + csv2tbx.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/csv2tbx.py b/translate-toolkit-1.5.1/translate/convert/csv2tbx.py new file mode 100644 index 0000000..a1333de --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/csv2tbx.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2006-2007 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert Comma-Separated Value (.csv) files to a TermBase eXchange (.tbx) glossary file""" + +from translate.misc import sparse +from translate.storage import tbx +from translate.storage import csvl10n + +class csv2tbx: + """a class that takes translations from a .csv file and puts them in a .tbx file""" + def __init__(self, charset=None): + """construct the converter...""" + self.charset = charset + + def convertfile(self, thecsvfile): + """converts a csvfile to a tbxfile, and returns it. uses templatepo if given at construction""" + mightbeheader = True + self.tbxfile = tbx.tbxfile() + for thecsv in thecsvfile.units: + if mightbeheader: + # ignore typical header strings... + mightbeheader = False + if [item.strip().lower() for item in thecsv.comment, thecsv.source, thecsv.target] == \ + ["comment", "original", "translation"]: + continue + if len(thecsv.comment.strip()) == 0 and thecsv.source.find("Content-Type:") != -1: + continue + term = tbx.tbxunit.buildfromunit(thecsv) + # TODO: we might want to get the location or other information from CSV + self.tbxfile.addunit(term) + return self.tbxfile + +def convertcsv(inputfile, outputfile, templatefile, charset=None, columnorder=None): + """reads in inputfile using csvl10n, converts using csv2tbx, writes to outputfile""" + inputstore = csvl10n.csvfile(inputfile, fieldnames=columnorder) + convertor = csv2tbx(charset=charset) + outputstore = convertor.convertfile(inputstore) + if len(outputstore.units) == 0: + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(): + from translate.convert import convert + formats = {("csv", "tbx"): ("tbx", convertcsv), ("csv", None): ("tbx", convertcsv)} + parser = convert.ConvertOptionParser(formats, usetemplates=False, description=__doc__) + parser.add_option("", "--charset", dest="charset", default=None, + help="set charset to decode from csv files", metavar="CHARSET") + parser.add_option("", "--columnorder", dest="columnorder", default=None, + help="specify the order and position of columns (comment,source,target)") + parser.passthrough.append("charset") + parser.passthrough.append("columnorder") + parser.run() + + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/dtd2po.py b/translate-toolkit-1.5.1/translate/convert/dtd2po.py new file mode 100644 index 0000000..5778945 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/dtd2po.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""script to convert a mozilla .dtd UTF-8 localization format to a +gettext .po localization file using the po and dtd modules, and the +dtd2po convertor class which is in this module +You can convert back to .dtd using po2dtd.py""" + +from translate.storage import po +from translate.storage import dtd +from translate.misc import quote +from translate.convert import accesskey as accesskeyfn + +def is_css_entity(entity): + """Says if the given entity is likely to contain CSS that should not be + translated.""" + if '.' in entity: + prefix, suffix = entity.rsplit('.', 1) + if suffix in ["height", "width", "unixWidth", "macWidth", "size"] or suffix.startswith("style"): + return True + return False + +class dtd2po: + def __init__(self, blankmsgstr=False, duplicatestyle="msgctxt"): + self.currentgroup = None + self.blankmsgstr = blankmsgstr + self.duplicatestyle = duplicatestyle + + def convertcomments(self, thedtd, thepo): + entity = quote.rstripeol(thedtd.entity) + if len(entity) > 0: + thepo.addlocation(thedtd.entity) + for commenttype, comment in thedtd.comments: + # handle groups + if (commenttype == "locgroupstart"): + groupcomment = comment.replace('BEGIN','GROUP') + self.currentgroup = groupcomment + elif (commenttype == "locgroupend"): + groupcomment = comment.replace('END','GROUP') + self.currentgroup = None + # handle automatic comment + if commenttype == "automaticcomment": + thepo.addnote(comment, origin="developer") + # handle normal comments + else: + thepo.addnote(quote.stripcomment(comment), origin="developer") + # handle group stuff + if self.currentgroup is not None: + thepo.addnote(quote.stripcomment(self.currentgroup), origin="translator") + if is_css_entity(entity): + thepo.addnote("Do not translate this. Only change the numeric values if you need this dialogue box to appear bigger", origin="developer") + + def convertstrings(self, thedtd, thepo): + # extract the string, get rid of quoting + unquoted = dtd.unquotefromdtd(thedtd.definition).replace("\r", "") + # escape backslashes... but not if they're for a newline + # unquoted = unquoted.replace("\\", "\\\\").replace("\\\\n", "\\n") + # now split the string into lines and quote them + lines = unquoted.split('\n') + while lines and not lines[0].strip(): + del lines[0] + while lines and not lines[-1].strip(): + del lines[-1] + # quotes have been escaped already by escapeforpo, so just add the start and end quotes + if len(lines) > 1: + thepo.source = "\n".join([lines[0].rstrip() + ' '] + \ + [line.strip() + ' ' for line in lines[1:-1]] + \ + [lines[-1].lstrip()]) + elif lines: + thepo.source = lines[0] + else: + thepo.source = "" + thepo.target = "" + + def convertunit(self, thedtd): + """converts a dtd unit to a po unit, returns None if empty or not for translation""" + if thedtd is None: + return None + if getattr(thedtd, "entityparameter", None) == "SYSTEM": + return None + thepo = po.pounit(encoding="UTF-8") + # remove unwanted stuff + for commentnum in range(len(thedtd.comments)): + commenttype, locnote = thedtd.comments[commentnum] + # if this is a localization note + if commenttype == 'locnote': + # parse the locnote into the entity and the actual note + typeend = quote.findend(locnote,'LOCALIZATION NOTE') + # parse the id + idstart = locnote.find('(', typeend) + if idstart == -1: continue + idend = locnote.find(')', idstart+1) + entity = locnote[idstart+1:idend].strip() + # parse the actual note + actualnotestart = locnote.find(':', idend+1) + actualnoteend = locnote.find('-->', idend) + actualnote = locnote[actualnotestart+1:actualnoteend].strip() + # if it's for this entity, process it + if thedtd.entity == entity: + # if it says don't translate (and nothing more), + if actualnote.startswith("DONT_TRANSLATE"): + # take out the entity,definition and the DONT_TRANSLATE comment + thedtd.entity = "" + thedtd.definition = "" + del thedtd.comments[commentnum] + # finished this for loop + break + else: + # convert it into an automatic comment, to be processed by convertcomments + thedtd.comments[commentnum] = ("automaticcomment", actualnote) + # do a standard translation + self.convertcomments(thedtd, thepo) + self.convertstrings(thedtd, thepo) + if thepo.isblank() and not thepo.getlocations(): + return None + else: + return thepo + + def convertmixedunit(self, labeldtd, accesskeydtd): + labelpo = self.convertunit(labeldtd) + accesskeypo = self.convertunit(accesskeydtd) + if labelpo is None: + return accesskeypo + if accesskeypo is None: + return labelpo + thepo = po.pounit(encoding="UTF-8") + thepo.addlocations(labelpo.getlocations()) + thepo.addlocations(accesskeypo.getlocations()) + thepo.msgidcomment = thepo._extract_msgidcomments() + labelpo._extract_msgidcomments() + thepo.msgidcomment = thepo._extract_msgidcomments() + accesskeypo._extract_msgidcomments() + thepo.addnote(labelpo.getnotes("developer"), "developer") + thepo.addnote(accesskeypo.getnotes("developer"), "developer") + thepo.addnote(labelpo.getnotes("translator"), "translator") + thepo.addnote(accesskeypo.getnotes("translator"), "translator") + # redo the strings from original dtd... + label = dtd.unquotefromdtd(labeldtd.definition).decode('UTF-8') + accesskey = dtd.unquotefromdtd(accesskeydtd.definition).decode('UTF-8') + label = accesskeyfn.combine(label, accesskey) + if label is None: + return None + thepo.source = label + thepo.target = "" + return thepo + + def findmixedentities(self, thedtdfile): + """creates self.mixedentities from the dtd file...""" + self.mixedentities = {} # those entities which have a .label/.title and .accesskey combined + for entity in thedtdfile.index.keys(): + for labelsuffix in dtd.labelsuffixes: + if entity.endswith(labelsuffix): + entitybase = entity[:entity.rfind(labelsuffix)] + # see if there is a matching accesskey in this line, making this a + # mixed entity + for akeytype in dtd.accesskeysuffixes: + if thedtdfile.index.has_key(entitybase + akeytype): + # add both versions to the list of mixed entities + self.mixedentities[entity] = {} + self.mixedentities[entitybase+akeytype] = {} + # check if this could be a mixed entity (labelsuffix and ".accesskey") + + def convertdtdunit(self, thedtdfile, thedtd, mixbucket="dtd"): + """converts a dtd unit from thedtdfile to a po unit, handling mixed entities along the way...""" + # keep track of whether accesskey and label were combined + if thedtd.entity in self.mixedentities: + # use special convertmixed unit which produces one pounit with + # both combined for the label and None for the accesskey + alreadymixed = self.mixedentities[thedtd.entity].get(mixbucket, None) + if alreadymixed: + # we are successfully throwing this away... + return None + elif alreadymixed is None: + # depending on what we come across first, work out the label and the accesskey + labeldtd, accesskeydtd = None, None + labelentity, accesskeyentity = None, None + for labelsuffix in dtd.labelsuffixes: + if thedtd.entity.endswith(labelsuffix): + entitybase = thedtd.entity[:thedtd.entity.rfind(labelsuffix)] + for akeytype in dtd.accesskeysuffixes: + if thedtdfile.index.has_key(entitybase + akeytype): + labelentity, labeldtd = thedtd.entity, thedtd + accesskeyentity = labelentity[:labelentity.rfind(labelsuffix)]+akeytype + accesskeydtd = thedtdfile.index[accesskeyentity] + break + else: + for akeytype in dtd.accesskeysuffixes: + if thedtd.entity.endswith(akeytype): + accesskeyentity, accesskeydtd = thedtd.entity, thedtd + for labelsuffix in dtd.labelsuffixes: + labelentity = accesskeyentity[:accesskeyentity.rfind(akeytype)]+labelsuffix + if thedtdfile.index.has_key(labelentity): + labeldtd = thedtdfile.index[labelentity] + break + else: + labelentity = None + accesskeyentity = None + thepo = self.convertmixedunit(labeldtd, accesskeydtd) + if thepo is not None: + if accesskeyentity is not None: + self.mixedentities[accesskeyentity][mixbucket] = True + if labelentity is not None: + self.mixedentities[labelentity][mixbucket] = True + return thepo + else: + # otherwise the mix failed. add each one separately and remember they weren't mixed + if accesskeyentity is not None: + self.mixedentities[accesskeyentity][mixbucket] = False + if labelentity is not None: + self.mixedentities[labelentity][mixbucket] = False + return self.convertunit(thedtd) + + def convertstore(self, thedtdfile): + thetargetfile = po.pofile() + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit", x_accelerator_marker="&") + targetheader.addnote("extracted from %s" % thedtdfile.filename, "developer") + thetargetfile.addunit(targetheader) + thedtdfile.makeindex() + self.findmixedentities(thedtdfile) + # go through the dtd and convert each unit + for thedtd in thedtdfile.units: + if thedtd.isnull(): + continue + thepo = self.convertdtdunit(thedtdfile, thedtd) + if thepo is not None: + thetargetfile.addunit(thepo) + thetargetfile.removeduplicates(self.duplicatestyle) + return thetargetfile + + def mergestore(self, origdtdfile, translateddtdfile): + thetargetfile = po.pofile() + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit") + targetheader.addnote("extracted from %s, %s" % (origdtdfile.filename, translateddtdfile.filename), "developer") + thetargetfile.addunit(targetheader) + origdtdfile.makeindex() + self.findmixedentities(origdtdfile) + translateddtdfile.makeindex() + self.findmixedentities(translateddtdfile) + # go through the dtd files and convert each unit + for origdtd in origdtdfile.units: + if origdtd.isnull(): + continue + origpo = self.convertdtdunit(origdtdfile, origdtd, mixbucket="orig") + if origdtd.entity in self.mixedentities: + mixedentitydict = self.mixedentities[origdtd.entity] + if "orig" not in mixedentitydict: + # this means that the entity is mixed in the translation, but not the original - treat as unmixed + mixbucket = "orig" + del self.mixedentities[origdtd.entity] + elif mixedentitydict["orig"]: + # the original entity is already mixed successfully + mixbucket = "translate" + else: + # ?? + mixbucket = "orig" + else: + mixbucket = "translate" + if origpo is None: + # this means its a mixed entity (with accesskey) that's already been dealt with) + continue + if origdtd.entity in translateddtdfile.index: + translateddtd = translateddtdfile.index[origdtd.entity] + translatedpo = self.convertdtdunit(translateddtdfile, translateddtd, mixbucket=mixbucket) + else: + translatedpo = None + if origpo is not None: + if translatedpo is not None and not self.blankmsgstr: + origpo.target = translatedpo.source + thetargetfile.addunit(origpo) + thetargetfile.removeduplicates(self.duplicatestyle) + return thetargetfile + +def convertdtd(inputfile, outputfile, templatefile, pot=False, duplicatestyle="msgctxt"): + """reads in inputfile and templatefile using dtd, converts using dtd2po, writes to outputfile""" + inputstore = dtd.dtdfile(inputfile) + convertor = dtd2po(blankmsgstr=pot, duplicatestyle=duplicatestyle) + if templatefile is None: + outputstore = convertor.convertstore(inputstore) + else: + templatestore = dtd.dtdfile(templatefile) + outputstore = convertor.mergestore(templatestore, inputstore) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"dtd": ("po", convertdtd), ("dtd", "dtd"): ("po", convertdtd)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/html2po b/translate-toolkit-1.5.1/translate/convert/html2po new file mode 100755 index 0000000..b2293ce --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/html2po @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""extracts localizable strings from HTML files to gettext .po files +You can merge translated strings back using po2html""" + +from translate.convert import html2po + +if __name__ == '__main__': + html2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/html2po.py b/translate-toolkit-1.5.1/translate/convert/html2po.py new file mode 100644 index 0000000..2e7e732 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/html2po.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert HTML files to Gettext PO localization files + +See: http://translate.sourceforge.net/wiki/toolkit/html2po for examples and +usage instructions +""" + +from translate.storage import po +from translate.storage import html + +class html2po: + def convertfile(self, inputfile, filename, includeheader, includeuntagged=False, duplicatestyle="msgctxt", keepcomments=False): + """converts a html file to .po format""" + thetargetfile = po.pofile() + htmlparser = html.htmlfile(includeuntaggeddata=includeuntagged, inputfile=inputfile) + if includeheader: + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit") + thetargetfile.addunit(targetheader) + for htmlunit in htmlparser.units: + thepo = thetargetfile.addsourceunit(htmlunit.source) + thepo.addlocations(htmlunit.getlocations()) + if keepcomments: + thepo.addnote(htmlunit.getnotes(), "developer") + thetargetfile.removeduplicates(duplicatestyle) + return thetargetfile + +def converthtml(inputfile, outputfile, templates, includeuntagged=False, pot=False, duplicatestyle="msgctxt", keepcomments=False): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + convertor = html2po() + outputfilepos = outputfile.tell() + includeheader = outputfilepos == 0 + outputstore = convertor.convertfile(inputfile, getattr(inputfile, "name", "unknown"), includeheader, includeuntagged, duplicatestyle=duplicatestyle, keepcomments=keepcomments) + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + from translate.misc import stdiotell + import sys + sys.stdout = stdiotell.StdIOWrapper(sys.stdout) + formats = {"html":("po", converthtml), "htm":("po", converthtml), "xhtml":("po", converthtml), None:("po", converthtml)} + parser = convert.ConvertOptionParser(formats, usepots=True, description=__doc__) + parser.add_option("-u", "--untagged", dest="includeuntagged", default=False, action="store_true", + help="include untagged sections") + parser.passthrough.append("includeuntagged") + parser.add_option("--keepcomments", dest="keepcomments", default=False, action="store_true", + help="preserve html comments as translation notes in the output") + parser.passthrough.append("keepcomments") + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/ical2po b/translate-toolkit-1.5.1/translate/convert/ical2po new file mode 100755 index 0000000..448c68b --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/ical2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert an iCal file to a gettext .po localization file""" + +from translate.convert import ical2po + +if __name__ == '__main__': + ical2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/ical2po.py b/translate-toolkit-1.5.1/translate/convert/ical2po.py new file mode 100644 index 0000000..955d456 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/ical2po.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2007 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert iCal files to Gettext PO localization files""" + +import sys +from translate.storage import po +from translate.storage import ical + +class ical2po: + """convert a iCal file to a .po file for handling the translation...""" + def convert_store(self, input_store, duplicatestyle="msgctxt"): + """converts a iCal file to a .po file...""" + output_store = po.pofile() + output_header = output_store.makeheader(charset="UTF-8", encoding="8bit") + output_header.addnote("extracted from %s" % input_store.filename, "developer") + output_store.addunit(output_header) + for input_unit in input_store.units: + output_unit = self.convert_unit(input_unit, "developer") + if output_unit is not None: + output_store.addunit(output_unit) + output_store.removeduplicates(duplicatestyle) + return output_store + + def merge_store(self, template_store, input_store, blankmsgstr=False, duplicatestyle="msgctxt"): + """converts two iCal files to a .po file...""" + output_store = po.pofile() + output_header = output_store.makeheader(charset="UTF-8", encoding="8bit") + output_header.addnote("extracted from %s, %s" % (template_store.filename, input_store.filename), "developer") + output_store.addunit(output_header) + input_store.makeindex() + for template_unit in template_store.units: + origpo = self.convert_unit(template_unit, "developer") + # try and find a translation of the same name... + template_unit_name = "".join(template_unit.getlocations()) + if template_unit_name in input_store.locationindex: + translatedini = input_store.locationindex[template_unit_name] + translatedpo = self.convert_unit(translatedini, "translator") + else: + translatedpo = None + # if we have a valid po unit, get the translation and add it... + if origpo is not None: + if translatedpo is not None and not blankmsgstr: + origpo.target = translatedpo.source + output_store.addunit(origpo) + elif translatedpo is not None: + print >> sys.stderr, "error converting original iCal definition %s" % origini.name + output_store.removeduplicates(duplicatestyle) + return output_store + + def convert_unit(self, input_unit, commenttype): + """Converts a .ini unit to a .po unit. Returns None if empty + or not for translation.""" + if input_unit is None: + return None + # escape unicode + output_unit = po.pounit(encoding="UTF-8") + output_unit.addlocation("".join(input_unit.getlocations())) + output_unit.addnote(input_unit.getnotes("developer"), "developer") + output_unit.source = input_unit.source + output_unit.target = "" + return output_unit + +def convertical(input_file, output_file, template_file, pot=False, duplicatestyle="msgctxt"): + """Reads in L{input_file} using iCal, converts using L{ical2po}, writes to L{output_file}""" + input_store = ical.icalfile(input_file) + convertor = ical2po() + if template_file is None: + output_store = convertor.convert_store(input_store, duplicatestyle=duplicatestyle) + else: + template_store = ical.icalfile(template_file) + output_store = convertor.merge_store(template_store, input_store, blankmsgstr=pot, duplicatestyle=duplicatestyle) + if output_store.isempty(): + return 0 + output_file.write(str(output_store)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"ics": ("po", convertical), ("ics", "ics"): ("po", convertical)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/ini2po b/translate-toolkit-1.5.1/translate/convert/ini2po new file mode 100755 index 0000000..32976c2 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/ini2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2002, 2003 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a .ini file to a gettext .po localization file""" + +from translate.convert import ini2po + +if __name__ == '__main__': + ini2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/ini2po.py b/translate-toolkit-1.5.1/translate/convert/ini2po.py new file mode 100644 index 0000000..7b1dbd8 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/ini2po.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2007 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert .ini files to Gettext PO localization files""" + +import sys +from translate.storage import po +from translate.storage import ini + +class ini2po: + """convert a .ini file to a .po file for handling the translation...""" + def convert_store(self, input_store, duplicatestyle="msgctxt"): + """converts a .ini file to a .po file...""" + output_store = po.pofile() + output_header = output_store.makeheader(charset="UTF-8", encoding="8bit") + output_header.addnote("extracted from %s" % input_store.filename, "developer") + output_store.addunit(output_header) + for input_unit in input_store.units: + output_unit = self.convert_unit(input_unit, "developer") + if output_unit is not None: + output_store.addunit(output_unit) + output_store.removeduplicates(duplicatestyle) + return output_store + + def merge_store(self, template_store, input_store, blankmsgstr=False, duplicatestyle="msgctxt"): + """converts two .ini files to a .po file...""" + output_store = po.pofile() + output_header = output_store.makeheader(charset="UTF-8", encoding="8bit") + output_header.addnote("extracted from %s, %s" % (template_store.filename, input_store.filename), "developer") + output_store.addunit(output_header) + input_store.makeindex() + for template_unit in template_store.units: + origpo = self.convert_unit(template_unit, "developer") + # try and find a translation of the same name... + template_unit_name = "".join(template_unit.getlocations()) + if template_unit_name in input_store.locationindex: + translatedini = input_store.locationindex[template_unit_name] + translatedpo = self.convert_unit(translatedini, "translator") + else: + translatedpo = None + # if we have a valid po unit, get the translation and add it... + if origpo is not None: + if translatedpo is not None and not blankmsgstr: + origpo.target = translatedpo.source + output_store.addunit(origpo) + elif translatedpo is not None: + print >> sys.stderr, "error converting original ini definition %s" % origini.name + output_store.removeduplicates(duplicatestyle) + return output_store + + def convert_unit(self, input_unit, commenttype): + """Converts a .ini unit to a .po unit. Returns None if empty + or not for translation.""" + if input_unit is None: + return None + # escape unicode + output_unit = po.pounit(encoding="UTF-8") + output_unit.addlocation("".join(input_unit.getlocations())) + output_unit.source = input_unit.source + output_unit.target = "" + return output_unit + +def convertini(input_file, output_file, template_file, pot=False, duplicatestyle="msgctxt", dialect="default"): + """Reads in L{input_file} using ini, converts using L{ini2po}, writes to L{output_file}""" + input_store = ini.inifile(input_file, dialect=dialect) + convertor = ini2po() + if template_file is None: + output_store = convertor.convert_store(input_store, duplicatestyle=duplicatestyle) + else: + template_store = ini.inifile(template_file, dialect=dialect) + output_store = convertor.merge_store(template_store, input_store, blankmsgstr=pot, duplicatestyle=duplicatestyle) + if output_store.isempty(): + return 0 + output_file.write(str(output_store)) + return 1 + +def convertisl(input_file, output_file, template_file, pot=False, duplicatestyle="msgctxt", dialect="inno"): + return convertini(input_file, output_file, template_file, pot=False, duplicatestyle="msgctxt", dialect=dialect) + +def main(argv=None): + from translate.convert import convert + formats = { + "ini": ("po", convertini), ("ini", "ini"): ("po", convertini), + "isl": ("po", convertisl), ("isl", "isl"): ("po", convertisl), + "iss": ("po", convertisl), ("iss", "iss"): ("po", convertisl), + } + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/moz2po b/translate-toolkit-1.5.1/translate/convert/moz2po new file mode 100755 index 0000000..da9a148 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/moz2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""Converts Mozilla .dtd and .properties files to Gettext .po files""" + +from translate.convert import moz2po + +if __name__ == '__main__': + moz2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/moz2po.py b/translate-toolkit-1.5.1/translate/convert/moz2po.py new file mode 100644 index 0000000..903a6d4 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/moz2po.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert Mozilla .dtd and .properties files to Gettext PO localization files + +See: http://translate.sourceforge.net/wiki/toolkit/moz2po for examples and +usage instructions +""" + +from translate.convert import dtd2po +from translate.convert import prop2po +from translate.convert import mozfunny2prop +from translate.storage import xpi +from translate.convert import convert + +def main(argv=None): + formats = {(None, "*"): ("*", convert.copytemplate), + ("*", "*"): ("*", convert.copyinput), + "*": ("*", convert.copyinput)} + # handle formats that convert to .po files + converters = [("dtd", dtd2po.convertdtd), ("properties", prop2po.convertmozillaprop), + ("it", mozfunny2prop.it2po), ("ini", mozfunny2prop.ini2po), ("inc", mozfunny2prop.inc2po)] + for format, converter in converters: + formats[(format, format)] = (format + ".po", converter) + formats[format] = (format + ".po", converter) + # handle search and replace + replacer = convert.Replacer("en-US", "${locale}") + for replaceformat in ("js", "rdf", "manifest"): + formats[(None, replaceformat)] = (replaceformat, replacer.searchreplacetemplate) + formats[(replaceformat, replaceformat)] = (replaceformat, replacer.searchreplaceinput) + formats[replaceformat] = (replaceformat, replacer.searchreplaceinput) + parser = convert.ArchiveConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__, archiveformats={"xpi": xpi.XpiFile}) + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/mozfunny2prop.py b/translate-toolkit-1.5.1/translate/convert/mozfunny2prop.py new file mode 100644 index 0000000..0a7399a --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/mozfunny2prop.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2005, 2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""converts funny mozilla files to properties files""" + +import string +from translate.misc import quote +from translate.convert import prop2po +from translate.misc.wStringIO import StringIO + +def inc2prop(lines): + """convert a .inc file with #defines in it to a properties file""" + yield "# converted from #defines file\n" + for line in lines: + line = line.decode("utf-8") + if line.startswith("# "): + commented = True + line = line.replace("# ", "", 1) + else: + commented = False + if not line.strip(): + yield line + elif line.startswith("#define"): + parts = string.split(line.replace("#define", "", 1).strip(), maxsplit=1) + if not parts: + continue + if len(parts) == 1: + key, value = parts[0], "" + else: + key, value = parts + # special case: uncomment MOZ_LANGPACK_CONTRIBUTORS + if key == "MOZ_LANGPACK_CONTRIBUTORS": + commented = False + if commented: + yield "# " + yield "%s = %s\n" % (key, value) + else: + if commented: + yield "# " + yield line + +def it2prop(lines, encoding="cp1252"): + """convert a pseudo-properties .it file to a conventional properties file""" + yield "# converted from pseudo-properties .it file\n" + # differences: ; instead of # for comments + # [section] titles that we replace with # section: comments + for line in lines: + line = line.decode(encoding) + if not line.strip(): + yield line + elif line.lstrip().startswith(";"): + yield line.replace(";", "#", 1) + elif line.lstrip().startswith("[") and line.rstrip().endswith("]"): + yield "# section: "+line + else: + yield line + +def funny2prop(lines, itencoding="cp1252"): + hashstarts = len([line for line in lines if line.startswith("#")]) + if hashstarts: + for line in inc2prop(lines): + yield line + else: + for line in it2prop(lines, encoding=itencoding): + yield line + +def inc2po(inputfile, outputfile, templatefile, encoding=None, pot=False, duplicatestyle="msgctxt"): + """wraps prop2po but converts input/template files to properties first""" + inputlines = inputfile.readlines() + inputproplines = [line for line in inc2prop(inputlines)] + inputpropfile = StringIO("".join(inputproplines)) + if templatefile is not None: + templatelines = templatefile.readlines() + templateproplines = [line for line in inc2prop(templatelines)] + templatepropfile = StringIO("".join(templateproplines)) + else: + templatepropfile = None + return prop2po.convertprop(inputpropfile, outputfile, templatepropfile, personality="mozilla", pot=pot, duplicatestyle=duplicatestyle) + +def it2po(inputfile, outputfile, templatefile, encoding="cp1252", pot=False, duplicatestyle="msgctxt"): + """wraps prop2po but converts input/template files to properties first""" + inputlines = inputfile.readlines() + inputproplines = [line for line in it2prop(inputlines, encoding=encoding)] + inputpropfile = StringIO("".join(inputproplines)) + if templatefile is not None: + templatelines = templatefile.readlines() + templateproplines = [line for line in it2prop(templatelines, encoding=encoding)] + templatepropfile = StringIO("".join(templateproplines)) + else: + templatepropfile = None + return prop2po.convertprop(inputpropfile, outputfile, templatepropfile, personality="mozilla", pot=pot, duplicatestyle=duplicatestyle) + +def ini2po(inputfile, outputfile, templatefile, encoding="UTF-8", pot=False, duplicatestyle="msgctxt"): + return it2po(inputfile=inputfile, outputfile=outputfile, templatefile=templatefile, encoding=encoding, pot=pot, duplicatestyle=duplicatestyle) + +def main(argv=None): + import sys + lines = sys.stdin.readlines() + for line in funny2prop(lines): + sys.stdout.write(line) + +if __name__ == "__main__": + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/odf2xliff b/translate-toolkit-1.5.1/translate/convert/odf2xliff new file mode 100755 index 0000000..5d902d1 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/odf2xliff @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Extracts localizable strings from OpenDocument files to XLIFF files + +Files supported include: +- .odt (Wordprocessor documents) +- .ods (Spreadsheets) +- .odp (Presentations) +- .sxw (old OpenOffice.org 1.0 Wordprocessor documents) +""" + +from translate.convert import odf2xliff + +if __name__ == '__main__': + odf2xliff.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/odf2xliff.py b/translate-toolkit-1.5.1/translate/convert/odf2xliff.py new file mode 100644 index 0000000..8b14c17 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/odf2xliff.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert OpenDocument (ODF) files to XLIFF localization files""" + +# Import from ttk +from translate.storage import factory + +from translate.misc.contextlib import contextmanager, nested +from translate.misc.context import with_ +from translate.storage import odf_io + +def convertodf(inputfile, outputfile, templates, engine): + """reads in stdin using fromfileclass, converts using convertorclass, + writes to stdout + """ + + def translate_toolkit_implementation(store): + import cStringIO + import zipfile + + from translate.storage.xml_extract import extract + from translate.storage import odf_shared + + contents = odf_io.open_odf(inputfile) + for data in contents.values(): + parse_state = extract.ParseState(odf_shared.no_translate_content_elements, + odf_shared.inline_elements) + extract.build_store(cStringIO.StringIO(data), store, parse_state) + + def itools_implementation(store): + from itools.handlers import get_handler + from itools.gettext.po import encode_source + import itools.odf + + filename = getattr(inputfile, 'name', 'unkown') + handler = get_handler(filename) + + try: + get_units = handler.get_units + except AttributeError: + message = 'error: the file "%s" could not be processed' + raise AttributeError, message % filename + + # Make the XLIFF file + for source, context, line in get_units(): + source = encode_source(source) + unit = store.UnitClass(source) + store.addunit(unit) + + @contextmanager + def store_context(): + store = factory.getobject(outputfile) + try: + store.setfilename(store.getfilenode('NoName'), inputfile.name) + except: + print "couldn't set origin filename" + yield store + store.save() + + def with_block(store): + if engine == "toolkit": + translate_toolkit_implementation(store) + else: + itools_implementation(store) + + # Since the convertoptionsparser will give us an open file, we risk that + # it could have been opened in non-binary mode on Windows, and then we'll + # have problems, so let's make sure we have what we want. + inputfile.close() + inputfile = file(inputfile.name, mode='rb') + with_(store_context(), with_block) + return True + + +def main(argv=None): + def add_options(parser): + parser.add_option("", "--engine", dest="engine", default="toolkit", + type="choice", choices=["toolkit", "itools"], + help="""Choose whether itools (--engine=itools) or the translate toolkit (--engine=toolkit) + should be used as the engine to convert an ODF file to an XLIFF file.""") + parser.passthrough = ['engine'] + return parser + + from translate.convert import convert + # For formats see OpenDocument 1.2 draft 7 Appendix C + formats = {"sxw": ("xlf", convertodf), + "odt": ("xlf", convertodf), # Text + "ods": ("xlf", convertodf), # Spreadsheet + "odp": ("xlf", convertodf), # Presentation + "odg": ("xlf", convertodf), # Drawing + "odc": ("xlf", convertodf), # Chart + "odf": ("xlf", convertodf), # Formula + "odi": ("xlf", convertodf), # Image + "odm": ("xlf", convertodf), # Master Document + "ott": ("xlf", convertodf), # Text template + "ots": ("xlf", convertodf), # Spreadsheet template + "otp": ("xlf", convertodf), # Presentation template + "otg": ("xlf", convertodf), # Drawing template + "otc": ("xlf", convertodf), # Chart template + "otf": ("xlf", convertodf), # Formula template + "oti": ("xlf", convertodf), # Image template + "oth": ("xlf", convertodf), # Web page template + } + parser = convert.ConvertOptionParser(formats, description=__doc__) + add_options(parser) + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/oo2po b/translate-toolkit-1.5.1/translate/convert/oo2po new file mode 100755 index 0000000..2c96f47 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/oo2po @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# +# Copyright 2002-2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts OpenOffice.org exported .oo files to Gettext .po files""" + +from translate.convert import oo2po + +if __name__ == '__main__': + oo2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/oo2po.py b/translate-toolkit-1.5.1/translate/convert/oo2po.py new file mode 100644 index 0000000..e6a9de5 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/oo2po.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2003-2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert an OpenOffice.org (SDF) localization file to Gettext PO localization files + +See: http://translate.sourceforge.net/wiki/toolkit/oo2po for examples and +usage instructions +""" + +import os +import sys +from translate.storage import po +from translate.storage import oo + +# TODO: support using one GSI file as template, another as input (for when English is in one and translation in another) + +class oo2po: + def __init__(self, sourcelanguage, targetlanguage, blankmsgstr=False, long_keys=False): + """construct an oo2po converter for the specified languages""" + self.sourcelanguage = sourcelanguage + self.targetlanguage = targetlanguage + self.blankmsgstr = blankmsgstr + self.long_keys = long_keys + + def maketargetunit(self, part1, part2, translators_comment, key, subkey): + """makes a base unit (.po or XLIFF) out of a subkey of two parts""" + #TODO: Do better + text1 = getattr(part1, subkey) + if text1 == "": + return None + text2 = getattr(part2, subkey) + + unit = po.pounit(text1.decode('utf-8'), encoding="UTF-8") + unit.target = text2.decode('utf-8') + unit.addlocation(key + "." + subkey) + if getattr(translators_comment, subkey).strip() != "": + unit.addnote(getattr(translators_comment, subkey), origin="developer") + return unit + + def convertelement(self, theoo): + """convert an oo element into a list of base units (.po or XLIFF)""" + if self.sourcelanguage in theoo.languages: + part1 = theoo.languages[self.sourcelanguage] + else: + print >> sys.stderr, "/".join(theoo.lines[0].getkey()), "language not found: %s" % (self.sourcelanguage) + return [] + if self.blankmsgstr: + # use a blank part2 + part2 = oo.ooline() + else: + if self.targetlanguage in theoo.languages: + part2 = theoo.languages[self.targetlanguage] + else: + # if the language doesn't exist, the translation is missing ... so make it blank + part2 = oo.ooline() + if "x-comment" in theoo.languages: + translators_comment = theoo.languages["x-comment"] + else: + translators_comment = oo.ooline() + key = oo.makekey(part1.getkey(), self.long_keys) + unitlist = [] + for subkey in ("text", "quickhelptext", "title"): + unit = self.maketargetunit(part1, part2, translators_comment, key, subkey) + if unit is not None: + unitlist.append(unit) + return unitlist + + def convertstore(self, theoofile, duplicatestyle="msgctxt"): + """converts an entire oo file to a base class format (.po or XLIFF)""" + thetargetfile = po.pofile() + # create a header for the file + bug_url = 'http://qa.openoffice.org/issues/enter_bug.cgi' + ('''?subcomponent=ui&comment=&short_desc=Localization issue in file: %(filename)s&component=l10n&form_name=enter_issue''' % {"filename": theoofile.filename}).replace(" ", "%20").replace(":", "%3A") + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit", x_accelerator_marker="~", report_msgid_bugs_to=bug_url) + targetheader.addnote("extracted from %s" % theoofile.filename, "developer") + thetargetfile.addunit(targetheader) + thetargetfile.setsourcelanguage(self.sourcelanguage) + thetargetfile.settargetlanguage(self.targetlanguage) + # go through the oo and convert each element + for theoo in theoofile.units: + unitlist = self.convertelement(theoo) + for unit in unitlist: + thetargetfile.addunit(unit) + thetargetfile.removeduplicates(duplicatestyle) + return thetargetfile + +def verifyoptions(options): + """verifies the commandline options""" + if not options.pot and not options.targetlanguage: + raise ValueError("You must specify the target language unless generating POT files (-P)") + +def convertoo(inputfile, outputfile, templates, pot=False, sourcelanguage=None, targetlanguage=None, duplicatestyle="msgid_comment", multifilestyle="single"): + """reads in stdin using inputstore class, converts using convertorclass, writes to stdout""" + inputstore = oo.oofile() + if hasattr(inputfile, "filename"): + inputfilename = inputfile.filename + else: + inputfilename = "(input file name not known)" + inputstore.filename = inputfilename + inputstore.parse(inputfile.read()) + if not sourcelanguage: + testlangtype = targetlanguage or (inputstore and inputstore.languages[0]) or "" + if testlangtype.isdigit(): + sourcelanguage = "01" + else: + sourcelanguage = "en-US" + if not sourcelanguage in inputstore.languages: + print >> sys.stderr, "Warning: sourcelanguage '%s' not found in inputfile '%s' (contains %s)" % (sourcelanguage, inputfilename, ", ".join(inputstore.languages)) + if targetlanguage and targetlanguage not in inputstore.languages: + print >> sys.stderr, "Warning: targetlanguage '%s' not found in inputfile '%s' (contains %s)" % (targetlanguage, inputfilename, ", ".join(inputstore.languages)) + convertor = oo2po(sourcelanguage, targetlanguage, blankmsgstr=pot, long_keys=multifilestyle!="single") + outputstore = convertor.convertstore(inputstore, duplicatestyle) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"oo":("po", convertoo), "sdf":("po", convertoo)} + # always treat the input as an archive unless it is a directory + archiveformats = {(None, "input"): oo.oomultifile} + parser = convert.ArchiveConvertOptionParser(formats, usepots=True, description=__doc__, archiveformats=archiveformats) + parser.add_option("-l", "--language", dest="targetlanguage", default=None, + help="set target language to extract from oo file (e.g. af-ZA)", metavar="LANG") + parser.add_option("", "--source-language", dest="sourcelanguage", default=None, + help="set source language code (default en-US)", metavar="LANG") + parser.add_option("", "--nonrecursiveinput", dest="allowrecursiveinput", default=True, action="store_false", help="don't treat the input oo as a recursive store") + parser.add_duplicates_option() + parser.add_multifile_option() + parser.passthrough.append("pot") + parser.passthrough.append("sourcelanguage") + parser.passthrough.append("targetlanguage") + parser.verifyoptions = verifyoptions + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/oo2xliff b/translate-toolkit-1.5.1/translate/convert/oo2xliff new file mode 100755 index 0000000..cf11570 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/oo2xliff @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2002-2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts OpenOffice.org exported .oo files to xliff files""" + +from translate.convert import oo2xliff + +if __name__ == '__main__': + oo2xliff.main() diff --git a/translate-toolkit-1.5.1/translate/convert/oo2xliff.py b/translate-toolkit-1.5.1/translate/convert/oo2xliff.py new file mode 100644 index 0000000..474ba55 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/oo2xliff.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2003-2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert an OpenOffice.org (SDF) localization file to XLIFF localization files + +User documentation: http://translate.sourceforge.net/wiki/toolkit/oo2po +""" + +import os +import sys +from translate.storage import xliff +from translate.storage import oo + +# TODO: support using one GSI file as template, another as input (for when English is in one and translation in another) + +class oo2xliff: + def __init__(self, sourcelanguage, targetlanguage, blankmsgstr=False, long_keys=False): + """construct an oo2xliff converter for the specified languages""" + self.sourcelanguage = sourcelanguage + self.targetlanguage = targetlanguage + self.blankmsgstr = blankmsgstr + self.long_keys = long_keys + + def maketargetunit(self, part1, part2, translators_comment, key, subkey): + """makes a base unit (.po or XLIFF) out of a subkey of two parts""" + #TODO: Do better + text1 = getattr(part1, subkey) + if text1 == "": + return None + text2 = getattr(part2, subkey) + + unit = xliff.xliffunit(text1) + unit.target = text2 + if unit.target: + unit.markfuzzy(False) + else: + unit.markfuzzy(True) + unit.addlocation(key + "." + subkey) + if getattr(translators_comment, subkey).strip() != "": + unit.addnote(getattr(translators_comment, subkey), origin="developer") + return unit + + def convertelement(self, theoo): + """convert an oo element into a list of base units (.po or XLIFF)""" + if self.sourcelanguage in theoo.languages: + part1 = theoo.languages[self.sourcelanguage] + else: + print >> sys.stderr, "/".join(theoo.lines[0].getkey()), "language not found: %s" % (self.sourcelanguage) + return [] + if self.blankmsgstr: + # use a blank part2 + part2 = oo.ooline() + else: + if self.targetlanguage in theoo.languages: + part2 = theoo.languages[self.targetlanguage] + else: + # if the language doesn't exist, the translation is missing ... so make it blank + part2 = oo.ooline() + if "x-comment" in theoo.languages: + translators_comment = theoo.languages["x-comment"] + else: + translators_comment = oo.ooline() + key = oo.makekey(part1.getkey(), self.long_keys) + unitlist = [] + for subkey in ("text", "quickhelptext", "title"): + unit = self.maketargetunit(part1, part2, translators_comment, key, subkey) + if unit is not None: + unitlist.append(unit) + return unitlist + + def convertstore(self, theoofile, duplicatestyle="msgctxt"): + """converts an entire oo file to a base class format (.po or XLIFF)""" + thetargetfile = xliff.xlifffile() + thetargetfile.setsourcelanguage(self.sourcelanguage) + thetargetfile.settargetlanguage(self.targetlanguage) + # create a header for the file + bug_url = 'http://qa.openoffice.org/issues/enter_bug.cgi' + ('''?subcomponent=ui&comment=&short_desc=Localization issue in file: %(filename)s&component=l10n&form_name=enter_issue''' % {"filename": theoofile.filename}).replace(" ", "%20").replace(":", "%3A") + # go through the oo and convert each element + for theoo in theoofile.units: + unitlist = self.convertelement(theoo) + for unit in unitlist: + thetargetfile.addunit(unit) + return thetargetfile + +def verifyoptions(options): + """verifies the commandline options""" + if not options.targetlanguage: + raise ValueError("You must specify the target language.") + +def convertoo(inputfile, outputfile, templates, pot=False, sourcelanguage=None, targetlanguage=None, duplicatestyle="msgctxt", multifilestyle="single"): + """reads in stdin using inputstore class, converts using convertorclass, writes to stdout""" + inputstore = oo.oofile() + if hasattr(inputfile, "filename"): + inputfilename = inputfile.filename + else: + inputfilename = "(input file name not known)" + inputstore.filename = inputfilename + inputstore.parse(inputfile.read()) + if not sourcelanguage: + testlangtype = targetlanguage or (inputstore and inputstore.languages[0]) or "" + if testlangtype.isdigit(): + sourcelanguage = "01" + else: + sourcelanguage = "en-US" + if not sourcelanguage in inputstore.languages: + print >> sys.stderr, "Warning: sourcelanguage '%s' not found in inputfile '%s' (contains %s)" % (sourcelanguage, inputfilename, ", ".join(inputstore.languages)) + if not pot and targetlanguage and targetlanguage not in inputstore.languages: + print >> sys.stderr, "Warning: targetlanguage '%s' not found in inputfile '%s' (contains %s)" % (targetlanguage, inputfilename, ", ".join(inputstore.languages)) + convertor = oo2xliff(sourcelanguage, targetlanguage, blankmsgstr=pot, long_keys=multifilestyle!="single") + outputstore = convertor.convertstore(inputstore, duplicatestyle) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"oo":("xlf", convertoo), "sdf":("xlf", convertoo)} + # always treat the input as an archive unless it is a directory + archiveformats = {(None, "input"): oo.oomultifile} + parser = convert.ArchiveConvertOptionParser(formats, usepots=False, description=__doc__, archiveformats=archiveformats) + parser.add_option("-l", "--language", dest="targetlanguage", default=None, + help="set target language to extract from oo file (e.g. af-ZA)", metavar="LANG") + parser.add_option("", "--source-language", dest="sourcelanguage", default=None, + help="set source language code (default en-US)", metavar="LANG") + parser.add_option("", "--nonrecursiveinput", dest="allowrecursiveinput", default=True, action="store_false", help="don't treat the input oo as a recursive store") + parser.add_duplicates_option() + parser.add_multifile_option() + parser.passthrough.append("sourcelanguage") + parser.passthrough.append("targetlanguage") + parser.verifyoptions = verifyoptions + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/php2po b/translate-toolkit-1.5.1/translate/convert/php2po new file mode 100755 index 0000000..50edf91 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/php2po @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""extracts localizable strings from PHP files to gettext .po files +You can merge translated strings back using po2php""" + +from translate.convert import php2po + +if __name__ == '__main__': + php2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/php2po.py b/translate-toolkit-1.5.1/translate/convert/php2po.py new file mode 100644 index 0000000..9ffc412 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/php2po.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert PHP localization files to Gettext PO localization files + +See: http://translate.sourceforge.net/wiki/toolkit/php2po for examples and +usage instructions +""" + +import sys +from translate.storage import po +from translate.storage import php + +class php2po: + """convert a .php file to a .po file for handling the translation...""" + def convertstore(self, inputstore, duplicatestyle="msgctxt"): + """converts a .php file to a .po file...""" + outputstore = po.pofile() + outputheader = outputstore.makeheader(charset="UTF-8", encoding="8bit") + outputheader.addnote("extracted from %s" % inputstore.filename, "developer") + outputstore.addunit(outputheader) + for inputunit in inputstore.units: + outputunit = self.convertunit(inputunit, "developer") + if outputunit is not None: + outputstore.addunit(outputunit) + outputstore.removeduplicates(duplicatestyle) + return outputstore + + def mergestore(self, templatestore, inputstore, blankmsgstr=False, duplicatestyle="msgctxt"): + """converts two .properties files to a .po file...""" + outputstore = po.pofile() + outputheader = outputstore.makeheader(charset="UTF-8", encoding="8bit") + outputheader.addnote("extracted from %s, %s" % (templatestore.filename, inputstore.filename), "developer") + outputstore.addunit(outputheader) + inputstore.makeindex() + # loop through the original file, looking at units one by one + for templateunit in templatestore.units: + outputunit = self.convertunit(templateunit, "developer") + # try and find a translation of the same name... + if templateunit.name in inputstore.locationindex: + translatedinputunit = inputstore.locationindex[templateunit.name] + # Need to check that this comment is not a copy of the developer comments + translatedoutputunit = self.convertunit(translatedinputunit, "translator") + else: + translatedoutputunit = None + # if we have a valid po unit, get the translation and add it... + if outputunit is not None: + if translatedoutputunit is not None and not blankmsgstr: + outputunit.target = translatedoutputunit.source + outputstore.addunit(outputunit) + elif translatedoutputunit is not None: + print >> sys.stderr, "error converting original properties definition %s" % templateunit.name + outputstore.removeduplicates(duplicatestyle) + return outputstore + + def convertunit(self, inputunit, origin): + """Converts a .php unit to a .po unit""" + outputunit = po.pounit(encoding="UTF-8") + outputunit.addnote(inputunit.getnotes(origin), origin) + outputunit.addlocation("".join(inputunit.getlocations())) + outputunit.source = inputunit.source + outputunit.target = "" + return outputunit + +def convertphp(inputfile, outputfile, templatefile, pot=False, duplicatestyle="msgctxt"): + """reads in inputfile using php, converts using php2po, writes to outputfile""" + inputstore = php.phpfile(inputfile) + convertor = php2po() + if templatefile is None: + outputstore = convertor.convertstore(inputstore, duplicatestyle=duplicatestyle) + else: + templatestore = php.phpfile(templatefile) + outputstore = convertor.mergestore(templatestore, inputstore, blankmsgstr=pot, duplicatestyle=duplicatestyle) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"php": ("po", convertphp), ("php", "php"): ("po", convertphp)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2csv b/translate-toolkit-1.5.1/translate/convert/po2csv new file mode 100755 index 0000000..53a6828 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2csv @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2003, 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a gettext .po localization file to a comma-separated values (.csv) file""" + +from translate.convert import po2csv + +if __name__ == '__main__': + po2csv.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2csv.py b/translate-toolkit-1.5.1/translate/convert/po2csv.py new file mode 100644 index 0000000..740dc04 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2csv.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2003-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert Gettext PO localization files to Comma-Separated Value (.csv) files + +see: http://translate.sourceforge.net/wiki/toolkit/po2csv for examples and +usage instructions +""" + +from translate.storage import po +from translate.storage import csvl10n + +class po2csv: + def convertcomments(self, inputunit): + return " ".join(inputunit.getlocations()) + + def convertunit(self, inputunit): + csvunit = csvl10n.csvunit() + if inputunit.isheader(): + csvunit.comment = "location" + csvunit.source = "source" + csvunit.target = "target" + elif inputunit.isblank(): + return None + else: + csvunit.comment = self.convertcomments(inputunit) + csvunit.source = inputunit.source + csvunit.target = inputunit.target + return csvunit + + def convertplurals(self, inputunit): + csvunit = csvl10n.csvunit() + csvunit.comment = self.convertcomments(inputunit) + csvunit.source = inputunit.source.strings[1] + csvunit.target = inputunit.target.strings[1] + return csvunit + + def convertstore(self, inputstore, columnorder=None): + outputstore = csvl10n.csvfile(fieldnames=columnorder) + for inputunit in inputstore.units: + outputunit = self.convertunit(inputunit) + if outputunit is not None: + outputstore.addunit(outputunit) + if inputunit.hasplural(): + outputunit = self.convertplurals(inputunit) + if outputunit is not None: + outputstore.addunit(outputunit) + return outputstore + +def convertcsv(inputfile, outputfile, templatefile, columnorder=None): + """reads in inputfile using po, converts using po2csv, writes to outputfile""" + # note that templatefile is not used, but it is required by the converter... + inputstore = po.pofile(inputfile) + if inputstore.isempty(): + return 0 + convertor = po2csv() + outputstore = convertor.convertstore(inputstore, columnorder) + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"po":("csv", convertcsv)} + parser = convert.ConvertOptionParser(formats, usepots=True, description=__doc__) + parser.add_option("", "--columnorder", dest="columnorder", default=None, + help="specify the order and position of columns (location,source,target)") + parser.passthrough.append("columnorder") + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2dtd.py b/translate-toolkit-1.5.1/translate/convert/po2dtd.py new file mode 100644 index 0000000..c068f20 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2dtd.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2009 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""script that converts a .po file to a UTF-8 encoded .dtd file as used by mozilla +either done using a template or just using the .po file""" + +from translate.storage import dtd +from translate.storage import po +from translate.misc import quote +from translate.convert import accesskey +import warnings + +def getmixedentities(entities): + """returns a list of mixed .label and .accesskey entities from a list of entities""" + mixedentities = [] # those entities which have a .label and .accesskey combined + # search for mixed entities... + for entity in entities: + for labelsuffix in dtd.labelsuffixes: + if entity.endswith(labelsuffix): + entitybase = entity[:entity.rfind(labelsuffix)] + # see if there is a matching accesskey, making this a mixed entity + for akeytype in dtd.accesskeysuffixes: + if entitybase + akeytype in entities: + # add both versions to the list of mixed entities + mixedentities += [entity, entitybase+akeytype] + return mixedentities + +def applytranslation(entity, dtdunit, inputunit, mixedentities): + """applies the translation for entity in the po unit to the dtd unit""" + # this converts the po-style string to a dtd-style string + unquotedstr = inputunit.target + # check there aren't missing entities... + if len(unquotedstr.strip()) == 0: + return + # handle mixed entities + for labelsuffix in dtd.labelsuffixes: + if entity.endswith(labelsuffix): + if entity in mixedentities: + unquotedstr, akey = accesskey.extract(unquotedstr) + break + else: + for akeytype in dtd.accesskeysuffixes: + if entity.endswith(akeytype): + if entity in mixedentities: + label, unquotedstr = accesskey.extract(unquotedstr) + if not unquotedstr: + warnings.warn("Could not find accesskey for %s" % entity) + else: + original = dtd.unquotefromdtd(dtdunit.definition) + # For the sake of diffs we keep the case of the + # accesskey the same if we know the translation didn't + # change. Casing matters in XUL. + if unquotedstr == dtdunit.source and original.lower() == unquotedstr.lower(): + if original.isupper(): + unquotedstr = unquotedstr.upper() + elif original.islower(): + unquotedstr = unquotedstr.lower() + if len(unquotedstr) > 0: + dtdunit.definition = dtd.quotefordtd(dtd.removeinvalidamps(entity, unquotedstr)) + +class redtd: + """this is a convertor class that creates a new dtd based on a template using translations in a po""" + def __init__(self, dtdfile): + self.dtdfile = dtdfile + + def convertstore(self, inputstore, includefuzzy=False): + # translate the strings + for inunit in inputstore.units: + # there may be more than one entity due to msguniq merge + if includefuzzy or not inunit.isfuzzy(): + self.handleinunit(inunit) + return self.dtdfile + + def handleinunit(self, inunit): + entities = inunit.getlocations() + mixedentities = getmixedentities(entities) + for entity in entities: + if self.dtdfile.index.has_key(entity): + # now we need to replace the definition of entity with msgstr + dtdunit = self.dtdfile.index[entity] # find the dtd + applytranslation(entity, dtdunit, inunit, mixedentities) + +class po2dtd: + """this is a convertor class that creates a new dtd file based on a po file without a template""" + def convertcomments(self, inputunit, dtdunit): + entities = inputunit.getlocations() + if len(entities) > 1: + # don't yet handle multiple entities + dtdunit.comments.append(("conversionnote",'<!-- CONVERSION NOTE - multiple entities -->\n')) + dtdunit.entity = entities[0] + elif len(entities) == 1: + dtdunit.entity = entities[0] + else: + # this produces a blank entity, which doesn't write anything out + dtdunit.entity = "" + + if inputunit.isfuzzy(): + dtdunit.comments.append(("potype", "fuzzy\n")) + for note in inputunit.getnotes("translator").split("\n"): + if not note: + continue + note = quote.unstripcomment(note) + if (note.find('LOCALIZATION NOTE') == -1) or (note.find('GROUP') == -1): + dtdunit.comments.append(("comment", note)) + # msgidcomments are special - they're actually localization notes + msgidcomment = inputunit._extract_msgidcomments() + if msgidcomment: + locnote = quote.unstripcomment("LOCALIZATION NOTE ("+dtdunit.entity+"): "+msgidcomment) + dtdunit.comments.append(("locnote", locnote)) + + + def convertstrings(self, inputunit, dtdunit): + if inputunit.istranslated(): + unquoted = inputunit.target + else: + unquoted = inputunit.source + dtdunit.definition = dtd.quotefordtd(dtd.removeinvalidamps(dtdunit.entity, unquoted)) + + def convertunit(self, inputunit): + dtdunit = dtd.dtdunit() + self.convertcomments(inputunit, dtdunit) + self.convertstrings(inputunit, dtdunit) + return dtdunit + + def convertstore(self, inputstore, includefuzzy=False): + outputstore = dtd.dtdfile() + self.currentgroups = [] + for inputunit in inputstore.units: + if includefuzzy or not inputunit.isfuzzy(): + dtdunit = self.convertunit(inputunit) + if dtdunit is not None: + outputstore.addunit(dtdunit) + return outputstore + +def convertdtd(inputfile, outputfile, templatefile, includefuzzy=False): + inputstore = po.pofile(inputfile) + if templatefile is None: + convertor = po2dtd() + else: + templatestore = dtd.dtdfile(templatefile) + convertor = redtd(templatestore) + outputstore = convertor.convertstore(inputstore, includefuzzy) + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + # handle command line options + from translate.convert import convert + formats = {"po": ("dtd", convertdtd), ("po", "dtd"): ("dtd", convertdtd)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_fuzzy_option() + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2html b/translate-toolkit-1.5.1/translate/convert/po2html new file mode 100755 index 0000000..37c02d7 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2html @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""translates html files using gettext .po localization +You can generate the po files using html2po""" + +from translate.convert import po2html + +if __name__ == '__main__': + po2html.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2html.py b/translate-toolkit-1.5.1/translate/convert/po2html.py new file mode 100644 index 0000000..eed2780 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2html.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert Gettext PO localization files to HTML files + +see: http://translate.sourceforge.net/wiki/toolkit/po2html for examples and +usage instructions +""" + +from translate.storage import po +try: + import textwrap +except: + textwrap = None + +try: + import tidy +except: + tidy = None + +class po2html: + """po2html can take a po file and generate html. best to give it a template file otherwise will just concat msgstrs""" + def __init__(self, wrap=None, usetidy=None): + self.wrap = wrap + self.tidy = tidy and usetidy + + def wrapmessage(self, message): + """rewraps text as required""" + if self.wrap is None: + return message + return "\n".join([textwrap.fill(line, self.wrap, replace_whitespace=False) for line in message.split("\n")]) + + def convertstore(self, inputstore, includefuzzy): + """converts a file to .po format""" + htmlresult = "" + for inputunit in inputstore.units: + if inputunit.isheader(): + continue + if includefuzzy or not inputunit.isfuzzy(): + htmlresult += self.wrapmessage(inputunit.target) + "\n" + "\n" + else: + htmlresult += self.wrapmessage(inputunit.source) + "\n" + "\n" + return htmlresult.encode('utf-8') + + def mergestore(self, inputstore, templatetext, includefuzzy): + """converts a file to .po format""" + htmlresult = templatetext.replace("\n", " ") + if isinstance(htmlresult, str): + #TODO: get the correct encoding + htmlresult = htmlresult.decode('utf-8') + # TODO: use the algorithm from html2po to get blocks and translate them individually + # rather than using replace + for inputunit in inputstore.units: + if inputunit.isheader(): + continue + msgid = inputunit.source + msgstr = None + if includefuzzy or not inputunit.isfuzzy(): + msgstr = self.wrapmessage(inputunit.target) + else: + msgstr = self.wrapmessage(inputunit.source) + if msgstr.strip(): + # TODO: "msgid" is already html-encoded ("&" -> "&"), while + # "msgstr" is not encoded -> thus the replace fails + # see test_po2html.py in line 67 + htmlresult = htmlresult.replace(msgid, msgstr, 1) + htmlresult = htmlresult.encode('utf-8') + if self.tidy: + htmlresult = str(tidy.parseString(htmlresult)) + return htmlresult + +def converthtml(inputfile, outputfile, templatefile, wrap=None, includefuzzy=False, usetidy=True): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + inputstore = po.pofile(inputfile) + convertor = po2html(wrap=wrap, usetidy=usetidy) + if templatefile is None: + outputstring = convertor.convertstore(inputstore, includefuzzy) + else: + templatestring = templatefile.read() + outputstring = convertor.mergestore(inputstore, templatestring, includefuzzy) + outputfilepos = outputfile.tell() + outputfile.write(outputstring) + return 1 + +def main(argv=None): + from translate.convert import convert + from translate.misc import stdiotell + import sys + sys.stdout = stdiotell.StdIOWrapper(sys.stdout) + formats = {("po", "htm"):("htm", converthtml), ("po", "html"):("html", converthtml), ("po", "xhtml"):("xhtml", converthtml), ("po"):("html", converthtml)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + if textwrap is not None: + parser.add_option("-w", "--wrap", dest="wrap", default=None, type="int", + help="set number of columns to wrap html at", metavar="WRAP") + parser.passthrough.append("wrap") + if tidy is not None: + parser.add_option("", "--notidy", dest="usetidy", default=True, + help="disables the use of HTML tidy", action="store_false") + parser.passthrough.append("usetidy") + parser.add_fuzzy_option() + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2ical b/translate-toolkit-1.5.1/translate/convert/po2ical new file mode 100755 index 0000000..13a34eb --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2ical @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a gettext .po localization file to an iCal file""" + +from translate.convert import po2ical + +if __name__ == '__main__': + po2ical.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2ical.py b/translate-toolkit-1.5.1/translate/convert/po2ical.py new file mode 100644 index 0000000..b914afc --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2ical.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +"""convert Gettext PO localization files to iCal files""" + +from translate.storage import factory +from translate.storage import ical + +class reical: + def __init__(self, templatefile): + self.templatefile = templatefile + self.templatestore = ical.icalfile(templatefile) + self.inputdict = {} + + def convertstore(self, inputstore, includefuzzy=False): + self.makestoredict(inputstore, includefuzzy) + for unit in self.templatestore.units: + for location in unit.getlocations(): + if self.inputdict.has_key(location): + unit.target = self.inputdict[location] + else: + unit.target = unit.source + return str(self.templatestore) + + def makestoredict(self, store, includefuzzy=False): + # make a dictionary of the translations + for unit in store.units: + if includefuzzy or not unit.isfuzzy(): + # there may be more than one entity due to msguniq merge + for location in unit.getlocations(): + inistring = unit.target + if len(inistring.strip()) == 0: + inistring = unit.source + self.inputdict[location] = inistring + +def convertical(inputfile, outputfile, templatefile, includefuzzy=False): + inputstore = factory.getobject(inputfile) + if templatefile is None: + raise ValueError("must have template file for iCal files") + else: + convertor = reical(templatefile) + outputstring = convertor.convertstore(inputstore, includefuzzy) + outputfile.write(outputstring) + return 1 + +def main(argv=None): + # handle command line options + from translate.convert import convert + formats = {("po", "ics"): ("ics", convertical)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_fuzzy_option() + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2ini b/translate-toolkit-1.5.1/translate/convert/po2ini new file mode 100755 index 0000000..8abff3c --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2ini @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2007 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a gettext .po localization file to a .ini file""" + +from translate.convert import po2ini + +if __name__ == '__main__': + po2ini.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2ini.py b/translate-toolkit-1.5.1/translate/convert/po2ini.py new file mode 100644 index 0000000..b0f12ea --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2ini.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +"""convert Gettext PO localization files to .ini files""" + +from translate.storage import factory +from translate.storage import ini + +class reini: + def __init__(self, templatefile, dialect): + self.templatefile = templatefile + self.templatestore = ini.inifile(templatefile, dialect=dialect) + self.inputdict = {} + + def convertstore(self, inputstore, includefuzzy=False): + self.makestoredict(inputstore, includefuzzy) + for unit in self.templatestore.units: + for location in unit.getlocations(): + unit.target = self.inputdict[location] + return str(self.templatestore) + + def makestoredict(self, store, includefuzzy=False): + # make a dictionary of the translations + for unit in store.units: + if includefuzzy or not unit.isfuzzy(): + # there may be more than one entity due to msguniq merge + for location in unit.getlocations(): + inistring = unit.target + if len(inistring.strip()) == 0: + inistring = unit.source + self.inputdict[location] = inistring + +def convertini(inputfile, outputfile, templatefile, includefuzzy=False, dialect="default"): + inputstore = factory.getobject(inputfile) + if templatefile is None: + raise ValueError("must have template file for ini files") + else: + convertor = reini(templatefile, dialect) + outputstring = convertor.convertstore(inputstore, includefuzzy) + outputfile.write(outputstring) + return 1 + +def convertisl(inputfile, outputfile, templatefile, includefuzzy=False, dialect="inno"): + convertini(inputfile, outputfile, templatefile, includefuzzy=False, dialect=dialect) + +def main(argv=None): + # handle command line options + from translate.convert import convert + formats = { + ("po", "ini"): ("ini", convertini), + ("po", "isl"): ("isl", convertisl), + } + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_fuzzy_option() + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2moz b/translate-toolkit-1.5.1/translate/convert/po2moz new file mode 100755 index 0000000..de39a93 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2moz @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""script that converts a set of .po files to a set of .dtd and .properties files +either done using a template or just using the .po file""" + +from translate.convert import po2moz + +if __name__ == '__main__': + po2moz.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2moz.py b/translate-toolkit-1.5.1/translate/convert/po2moz.py new file mode 100644 index 0000000..64c798b --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2moz.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert Gettext PO localization files to Mozilla .dtd and .properties files + +see: http://translate.sourceforge.net/wiki/toolkit/po2moz for examples and +usage instructions +""" + +import os.path +from translate.convert import po2dtd +from translate.convert import po2prop +from translate.convert import prop2mozfunny +from translate.storage import xpi +from translate.convert import convert + +class MozConvertOptionParser(convert.ArchiveConvertOptionParser): + def __init__(self, formats, usetemplates=False, usepots=False, description=None): + convert.ArchiveConvertOptionParser.__init__(self, formats, usetemplates, usepots, description=description, archiveformats={"xpi": xpi.XpiFile}) + + def initoutputarchive(self, options): + """creates an outputarchive if required""" + if options.output and self.isarchive(options.output, 'output'): + newlang = None + newregion = None + if options.locale is not None: + if options.locale.count("-") > 1: + raise ValueError("Invalid locale: %s - should be of the form xx-YY" % options.locale) + elif "-" in options.locale: + newlang, newregion = options.locale.split("-") + else: + newlang, newregion = options.locale, "" + if options.clonexpi is not None: + originalxpi = xpi.XpiFile(options.clonexpi, "r") + options.outputarchive = originalxpi.clone(options.output, "w", newlang=newlang, newregion=newregion) + elif self.isarchive(options.template, 'template'): + options.outputarchive = options.templatearchive.clone(options.output, "a", newlang=newlang, newregion=newregion) + else: + if os.path.exists(options.output): + options.outputarchive = xpi.XpiFile(options.output, "a", locale=newlang, region=newregion) + else: + # FIXME: this is unlikely to work because it has no jar files + options.outputarchive = xpi.XpiFile(options.output, "w", locale=newlang, region=newregion) + + def splitinputext(self, inputpath): + """splits a inputpath into name and extension""" + # TODO: not sure if this should be here, was in po2moz + d, n = os.path.dirname(inputpath), os.path.basename(inputpath) + s = n.find(".") + if s == -1: + return (inputpath, "") + root = os.path.join(d, n[:s]) + ext = n[s+1:] + return (root, ext) + + def recursiveprocess(self, options): + """recurse through directories and convert files""" + self.replacer.replacestring = options.locale + result = super(MozConvertOptionParser, self).recursiveprocess(options) + if self.isarchive(options.output, 'output'): + if options.progress in ('console', 'verbose'): + print "writing xpi file..." + options.outputarchive.close() + return result + +def main(argv=None): + # handle command line options + formats = {("dtd.po", "dtd"): ("dtd", po2dtd.convertdtd), + ("properties.po", "properties"): ("properties", po2prop.convertmozillaprop), + ("it.po", "it"): ("it", prop2mozfunny.po2it), + ("ini.po", "ini"): ("ini", prop2mozfunny.po2ini), + ("inc.po", "inc"): ("inc", prop2mozfunny.po2inc), + # (None, "*"): ("*", convert.copytemplate), + ("*", "*"): ("*", convert.copyinput), + "*": ("*", convert.copyinput)} + # handle search and replace + replacer = convert.Replacer("${locale}", None) + for replaceformat in ("js", "rdf", "manifest"): + formats[(None, replaceformat)] = (replaceformat, replacer.searchreplacetemplate) + formats[(replaceformat, replaceformat)] = (replaceformat, replacer.searchreplaceinput) + formats[replaceformat] = (replaceformat, replacer.searchreplaceinput) + parser = MozConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_option("-l", "--locale", dest="locale", default=None, + help="set output locale (required as this sets the directory names)", metavar="LOCALE") + parser.add_option("", "--clonexpi", dest="clonexpi", default=None, + help="clone xpi structure from the given xpi file") + parser.add_fuzzy_option() + parser.replacer = replacer + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2oo b/translate-toolkit-1.5.1/translate/convert/po2oo new file mode 100755 index 0000000..e7e41d9 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2oo @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# +# Copyright 2002-2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""script that converts a .po file with translations based on a .pot file +generated from a OpenOffice localization .oo back to the .oo (but translated) +Uses the original .oo to do the conversion as this makes sure we don't +leave out any unincluded stuff...""" + +from translate.convert import po2oo + +if __name__ == '__main__': + po2oo.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2oo.py b/translate-toolkit-1.5.1/translate/convert/po2oo.py new file mode 100644 index 0000000..f3e1537 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2oo.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert Gettext PO localization files to an OpenOffice.org (SDF) localization file + +see: http://translate.sourceforge.net/wiki/toolkit/po2oo for examples and +usage instructions +""" + +import sys +import os +from translate.storage import oo +from translate.storage import factory +from translate.filters import pofilter +from translate.filters import checks +from translate.filters import autocorrect +import time + +class reoo: + def __init__(self, templatefile, languages=None, timestamp=None, includefuzzy=False, long_keys=False, filteraction="exclude"): + """construct a reoo converter for the specified languages (timestamp=0 means leave unchanged)""" + # languages is a pair of language ids + self.long_keys = long_keys + self.readoo(templatefile) + self.languages = languages + self.filteraction = filteraction + if timestamp is None: + self.timestamp = time.strptime("2002-02-02 02:02:02", "%Y-%m-%d %H:%M:%S") + else: + self.timestamp = timestamp + if self.timestamp: + self.timestamp_str = time.strftime("%Y-%m-%d %H:%M:%S", self.timestamp) + else: + self.timestamp_str = None + self.includefuzzy = includefuzzy + + def makeindex(self): + """makes an index of the oo keys that are used in the source file""" + self.index = {} + for ookey, theoo in self.o.ookeys.iteritems(): + sourcekey = oo.makekey(ookey, self.long_keys) + self.index[sourcekey] = theoo + + def readoo(self, of): + """read in the oo from the file""" + oosrc = of.read() + self.o = oo.oofile() + self.o.parse(oosrc) + self.makeindex() + + def handleunit(self, unit): + # TODO: make this work for multiple columns in oo... + locations = unit.getlocations() + # technically our formats should just have one location for each entry... + # but we handle multiple ones just to be safe... + for location in locations: + subkeypos = location.rfind('.') + subkey = location[subkeypos+1:] + key = location[:subkeypos] + # this is just to handle our old system of using %s/%s:%s instead of %s/%s#%s + key = key.replace(':', '#') + # this is to handle using / instead of \ in the sourcefile... + key = key.replace('\\', '/') + key = oo.normalizefilename(key) + if self.index.has_key(key): + # now we need to replace the definition of entity with msgstr + theoo = self.index[key] # find the oo + self.applytranslation(key, subkey, theoo, unit) + else: + print >> sys.stderr, "couldn't find key %s from po in %d keys" % (key, len(self.index)) + try: + sourceunitlines = str(unit) + if isinstance(sourceunitlines, unicode): + sourceunitlines = sourceunitlines.encode("utf-8") + print >> sys.stderr, sourceunitlines + except: + print >> sys.stderr, "error outputting source unit %r" % (str(unit),) + + def applytranslation(self, key, subkey, theoo, unit): + """applies the translation from the source unit to the oo unit""" + if not self.includefuzzy and unit.isfuzzy(): + return + makecopy = False + if self.languages is None: + part1 = theoo.lines[0] + if len(theoo.lines) > 1: + part2 = theoo.lines[1] + else: + makecopy = True + else: + part1 = theoo.languages[self.languages[0]] + if self.languages[1] in theoo.languages: + part2 = theoo.languages[self.languages[1]] + else: + makecopy = True + if makecopy: + part2 = oo.ooline(part1.getparts()) + unquotedid = unit.source + unquotedstr = unit.target + # If there is no translation, we don't want to add a line + if len(unquotedstr) == 0: + return + if isinstance(unquotedstr, unicode): + unquotedstr = unquotedstr.encode("UTF-8") + # finally set the new definition in the oo, but not if its empty + if len(unquotedstr) > 0: + setattr(part2, subkey, unquotedstr) + # set the modified time + if self.timestamp_str: + part2.timestamp = self.timestamp_str + if self.languages: + part2.languageid = self.languages[1] + if makecopy: + theoo.addline(part2) + + def convertstore(self, sourcestore): + self.p = sourcestore + # translate the strings + for unit in self.p.units: + # there may be more than one element due to msguniq merge + if filter.validelement(unit, self.p.filename, self.filteraction): + self.handleunit(unit) + # return the modified oo file object + return self.o + +def getmtime(filename): + import stat + return time.localtime(os.stat(filename)[stat.ST_MTIME]) + +class oocheckfilter(pofilter.pocheckfilter): + def validelement(self, unit, filename, filteraction): + """Returns whether or not to use unit in conversion. (filename is just for error reporting)""" + if filteraction == "none": return True + filterresult = self.filterunit(unit) + if filterresult: + if filterresult != autocorrect: + for filtername, filtermessage in filterresult.iteritems(): + location = unit.getlocations()[0].encode('utf-8') + if filtername in self.options.error: + print >> sys.stderr, "Error at %s::%s: %s" % (filename, location, filtermessage) + return not filteraction in ["exclude-all", "exclude-serious"] + if filtername in self.options.warning or self.options.alwayswarn: + print >> sys.stderr, "Warning at %s::%s: %s" % (filename, location, filtermessage) + return not filteraction in ["exclude-all"] + return True + +class oofilteroptions: + error = ['variables', 'xmltags', 'escapes'] + warning = ['blank'] + #To only issue warnings for tests listed in warning, change the following to False: + alwayswarn = True + limitfilters = error + warning + #To use all available tests, uncomment the following: + #limitfilters = [] + #To exclude certain tests, list them in here: + excludefilters = {} + includefuzzy = False + includereview = False + includeheader = False + autocorrect = False + +options = oofilteroptions() +filter = oocheckfilter(options, [checks.OpenOfficeChecker, checks.StandardUnitChecker], checks.openofficeconfig) + +def convertoo(inputfile, outputfile, templatefile, sourcelanguage=None, targetlanguage=None, timestamp=None, includefuzzy=False, multifilestyle="single", filteraction=None): + inputstore = factory.getobject(inputfile) + inputstore.filename = getattr(inputfile, 'name', '') + if not targetlanguage: + raise ValueError("You must specify the target language") + if not sourcelanguage: + if targetlanguage.isdigit(): + sourcelanguage = "01" + else: + sourcelanguage = "en-US" + languages = (sourcelanguage, targetlanguage) + if templatefile is None: + raise ValueError("must have template file for oo files") + else: + convertor = reoo(templatefile, languages=languages, timestamp=timestamp, includefuzzy=includefuzzy, long_keys=multifilestyle != "single", filteraction=filteraction) + outputstore = convertor.convertstore(inputstore) + # TODO: check if we need to manually delete missing items + outputfile.write(str(outputstore)) + return True + +def main(argv=None): + from translate.convert import convert + formats = {("po", "oo"):("oo", convertoo), ("xlf", "oo"):("oo", convertoo), ("po", "sdf"):("sdf", convertoo)} + # always treat the input as an archive unless it is a directory + archiveformats = {(None, "output"): oo.oomultifile, (None, "template"): oo.oomultifile} + parser = convert.ArchiveConvertOptionParser(formats, usetemplates=True, description=__doc__, archiveformats=archiveformats) + parser.add_option("-l", "--language", dest="targetlanguage", default=None, + help="set target language code (e.g. af-ZA) [required]", metavar="LANG") + parser.add_option("", "--source-language", dest="sourcelanguage", default=None, + help="set source language code (default en-US)", metavar="LANG") + parser.add_option("-T", "--keeptimestamp", dest="timestamp", default=None, action="store_const", const=0, + help="don't change the timestamps of the strings") + parser.add_option("", "--nonrecursiveoutput", dest="allowrecursiveoutput", default=True, action="store_false", help="don't treat the output oo as a recursive store") + parser.add_option("", "--nonrecursivetemplate", dest="allowrecursivetemplate", default=True, action="store_false", help="don't treat the template oo as a recursive store") + parser.add_option("", "--filteraction", dest="filteraction", default="none", metavar="ACTION", + help="action on pofilter failure: none (default), warn, exclude-serious, exclude-all") + parser.add_fuzzy_option() + parser.add_multifile_option() + parser.passthrough.append("sourcelanguage") + parser.passthrough.append("targetlanguage") + parser.passthrough.append("timestamp") + parser.passthrough.append("filteraction") + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2php b/translate-toolkit-1.5.1/translate/convert/po2php new file mode 100755 index 0000000..013e1c3 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2php @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""translates php files using gettext .po localization +You can generate the po files using php2po""" + +from translate.convert import po2php + +if __name__ == '__main__': + po2php.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2php.py b/translate-toolkit-1.5.1/translate/convert/po2php.py new file mode 100644 index 0000000..cc59ec8 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2php.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +"""convert Gettext PO localization files to PHP localization files + +see: http://translate.sourceforge.net/wiki/toolkit/po2php for examples and +usage instructions +""" + +from translate.misc import quote +from translate.storage import po +from translate.storage import php + +eol = "\n" + +class rephp: + def __init__(self, templatefile): + self.templatefile = templatefile + self.inputdict = {} + + def convertstore(self, inputstore, includefuzzy=False): + self.inmultilinemsgid = False + self.inecho = False + self.makestoredict(inputstore, includefuzzy) + outputlines = [] + for line in self.templatefile.readlines(): + outputstr = self.convertline(line) + outputlines.append(outputstr) + return outputlines + + def makestoredict(self, store, includefuzzy=False): + '''make a dictionary of the translations''' + for unit in store.units: + if includefuzzy or not unit.isfuzzy(): + for location in unit.getlocations(): + inputstring = unit.target + if len(inputstring.strip()) == 0: + inputstring = unit.source + self.inputdict[location] = inputstring + + def convertline(self, line): + line = unicode(line, 'utf-8') + returnline = "" + # handle multiline msgid if we're in one + if self.inmultilinemsgid: + # see if there's more + endpos = line.rfind("%s;" % self.quotechar) + # if there was no '; or the quote is escaped, we have to continue + if endpos >= 0 and line[endpos-1] != '\\': + self.inmultilinemsgid = False + # if we're echoing... + if self.inecho: + returnline = line + # otherwise, this could be a comment + elif line.strip()[:2] == '//' or line.strip()[:2] == '/*': + returnline = quote.rstripeol(line)+eol + else: + line = quote.rstripeol(line) + equalspos = line.find('=') + # if no equals, just repeat it + if equalspos == -1: + returnline = quote.rstripeol(line)+eol + # otherwise, this is a definition + else: + # now deal with the current string... + key = line[:equalspos].strip() + lookupkey = key.replace(" ", "") + # Calculate space around the equal sign + prespace = line[len(line[:equalspos].rstrip()):equalspos] + postspacestart = len(line[equalspos+1:]) + postspaceend = len(line[equalspos+1:].lstrip()) + postspace = line[equalspos+1:equalspos+(postspacestart-postspaceend)+1] + self.quotechar = line[equalspos+(postspacestart-postspaceend)+1] + inlinecomment = line[line.rfind("%s;" % self.quotechar)+2:] + if self.inputdict.has_key(lookupkey): + self.inecho = False + value = php.phpencode(self.inputdict[lookupkey], self.quotechar) + if isinstance(value, str): + value = value.decode('utf8') + returnline = key + prespace + "=" + postspace + self.quotechar + value + self.quotechar + ';' + inlinecomment + eol + else: + self.inecho = True + returnline = line+eol + # no string termination means carry string on to next line + endpos = line.rfind("%s;" % self.quotechar) + # if there was no '; or the quote is escaped, we have to continue + if endpos == -1 or line[endpos-1] == '\\': + self.inmultilinemsgid = True + if isinstance(returnline, unicode): + returnline = returnline.encode('utf-8') + return returnline + +def convertphp(inputfile, outputfile, templatefile, includefuzzy=False): + inputstore = po.pofile(inputfile) + if templatefile is None: + raise ValueError("must have template file for php files") + # convertor = po2php() + else: + convertor = rephp(templatefile) + outputphplines = convertor.convertstore(inputstore, includefuzzy) + outputfile.writelines(outputphplines) + return 1 + +def main(argv=None): + # handle command line options + from translate.convert import convert + formats = {("po", "php"): ("php", convertphp)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_fuzzy_option() + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2prop b/translate-toolkit-1.5.1/translate/convert/po2prop new file mode 100755 index 0000000..b551c6e --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2prop @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2002, 2003 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a gettext .po localization file to a .properties file""" + +from translate.convert import po2prop + +if __name__ == '__main__': + po2prop.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2prop.py b/translate-toolkit-1.5.1/translate/convert/po2prop.py new file mode 100644 index 0000000..0fafe8a --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2prop.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +"""convert Gettext PO localization files to Java/Mozilla .properties files + +see: http://translate.sourceforge.net/wiki/toolkit/po2prop for examples and +usage instructions +""" + +from translate.misc import quote +from translate.storage import po + +eol = "\n" + +class reprop: + def __init__(self, templatefile): + self.templatefile = templatefile + self.inputdict = {} + + def convertstore(self, inputstore, personality, includefuzzy=False): + self.personality = personality + self.inmultilinemsgid = False + self.inecho = False + self.makestoredict(inputstore, includefuzzy) + outputlines = [] + for line in self.templatefile.readlines(): + outputstr = self.convertline(line) + outputlines.append(outputstr) + return outputlines + + def makestoredict(self, store, includefuzzy=False): + # make a dictionary of the translations + for unit in store.units: + if includefuzzy or not unit.isfuzzy(): + # there may be more than one entity due to msguniq merge + for entity in unit.getlocations(): + propstring = unit.target + + # NOTE: triple-space as a string means leave it empty (special signal) + if len(propstring.strip()) == 0 and propstring != " ": + propstring = unit.source + self.inputdict[entity] = propstring + + def convertline(self, line): + returnline = "" + # handle multiline msgid if we're in one + if self.inmultilinemsgid: + msgid = quote.rstripeol(line).strip() + # see if there's more + self.inmultilinemsgid = (msgid[-1:] == '\\') + # if we're echoing... + if self.inecho: + returnline = line + # otherwise, this could be a comment + elif line.strip()[:1] == '#': + returnline = quote.rstripeol(line)+eol + else: + line = quote.rstripeol(line) + equalspos = line.find('=') + # if no equals, just repeat it + if equalspos == -1: + returnline = quote.rstripeol(line)+eol + # otherwise, this is a definition + else: + # backslash at end means carry string on to next line + if quote.rstripeol(line)[-1:] == '\\': + self.inmultilinemsgid = True + # now deal with the current string... + key = line[:equalspos].strip() + # Calculate space around the equal sign + prespace = line.lstrip()[line.lstrip().find(' '):equalspos] + postspacestart = len(line[equalspos+1:]) + postspaceend = len(line[equalspos+1:].lstrip()) + postspace = line[equalspos+1:equalspos+(postspacestart-postspaceend)+1] + if self.inputdict.has_key(key): + self.inecho = False + value = self.inputdict[key] + if isinstance(value, str): + value = value.decode('utf8') + if self.personality == "mozilla": + returnline = key+prespace+"="+postspace+quote.mozillapropertiesencode(value)+eol + else: + returnline = key+prespace+"="+postspace+quote.javapropertiesencode(value)+eol + else: + self.inecho = True + returnline = line+eol + if isinstance(returnline, unicode): + returnline = returnline.encode('utf-8') + return returnline + +def convertmozillaprop(inputfile, outputfile, templatefile, includefuzzy=False): + """Mozilla specific convertor function""" + return convertprop(inputfile, outputfile, templatefile, personality="mozilla", includefuzzy=includefuzzy) + +def convertprop(inputfile, outputfile, templatefile, personality, includefuzzy=False): + inputstore = po.pofile(inputfile) + if templatefile is None: + raise ValueError("must have template file for properties files") + # convertor = po2prop() + else: + convertor = reprop(templatefile) + outputproplines = convertor.convertstore(inputstore, personality, includefuzzy) + outputfile.writelines(outputproplines) + return 1 + +def main(argv=None): + # handle command line options + from translate.convert import convert + formats = {("po", "properties"): ("properties", convertprop)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_option("", "--personality", dest="personality", default="java", type="choice", + choices=["java", "mozilla"], + help="set the output behaviour: java (default), mozilla", metavar="TYPE") + parser.add_fuzzy_option() + parser.passthrough.append("personality") + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2rc b/translate-toolkit-1.5.1/translate/convert/po2rc new file mode 100755 index 0000000..2f7bf05 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2rc @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# +# Copyright 2002, 2003, 2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a Gettext PO localization file to a Windows +Resource (.rc) file""" + +from translate.convert import po2rc + +if __name__ == '__main__': + po2rc.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2rc.py b/translate-toolkit-1.5.1/translate/convert/po2rc.py new file mode 100644 index 0000000..7d5219c --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2rc.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2006,2008-2009 Zuza Software Foundation +# +# This file is part of the Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""Convert Gettext PO localization files back to Windows Resource (.rc) files + +See: http://translate.sourceforge.net/wiki/toolkit/po2rc for examples and +usage instructions. +""" + +from translate.storage import po +from translate.storage import rc + +class rerc: + def __init__(self, templatefile, charset="utf-8", lang=None, sublang=None): + self.templatefile = templatefile + self.templatestore = rc.rcfile(templatefile) + self.inputdict = {} + self.charset = charset + self.lang = lang + self.sublang = sublang + + def convertstore(self, inputstore, includefuzzy=False): + self.makestoredict(inputstore, includefuzzy) + outputblocks = [] + for block in self.templatestore.blocks: + outputblocks.append(self.convertblock(block)) + if self.charset == "utf-8": + outputblocks.insert(0, "#pragma code_page(65001)\n") + outputblocks.append("#pragma code_page(default)") + return outputblocks + + def makestoredict(self, store, includefuzzy=False): + """ make a dictionary of the translations""" + for unit in store.units: + if includefuzzy or not unit.isfuzzy(): + for location in unit.getlocations(): + rcstring = unit.target + if len(rcstring.strip()) == 0: + rcstring = unit.source + self.inputdict[location] = rc.escape_to_rc(rcstring).encode(self.charset) + + def convertblock(self, block): + newblock = block + if isinstance(newblock, unicode): + newblock = newblock.encode('utf-8') + if newblock.startswith("LANGUAGE"): + return "LANGUAGE %s, %s" % (self.lang, self.sublang) + for unit in self.templatestore.units: + location = unit.getlocations()[0] + if self.inputdict.has_key(location): + if self.inputdict[location] != unit.match.groupdict()['value']: + newmatch = unit.match.group().replace(unit.match.groupdict()['value'], self.inputdict[location]) + newblock = newblock.replace(unit.match.group(), newmatch) + if isinstance(newblock, unicode): + newblock = newblock.encode(self.charset) + return newblock + +def convertrc(inputfile, outputfile, templatefile, includefuzzy=False, charset=None, lang=None, sublang=None): + inputstore = po.pofile(inputfile) + if not lang: + raise ValueError("must specify a target language") + if templatefile is None: + raise ValueError("must have template file for rc files") + # convertor = po2rc() + else: + convertor = rerc(templatefile, charset, lang, sublang) + outputrclines = convertor.convertstore(inputstore, includefuzzy) + outputfile.writelines(outputrclines) + return 1 + +def main(argv=None): + # handle command line options + from translate.convert import convert + formats = {("po", "rc"): ("rc", convertrc)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + defaultcharset = "utf-8" + parser.add_option("", "--charset", dest="charset", default=defaultcharset, + help="charset to use to decode the RC files (default: %s)" % defaultcharset, metavar="CHARSET") + parser.add_option("-l", "--lang", dest="lang", default=None, + help="LANG entry", metavar="LANG") + defaultsublang="SUBLANG_DEFAULT" + parser.add_option("", "--sublang", dest="sublang", default=defaultsublang, + help="SUBLANG entry (default: %s)" % defaultsublang, metavar="SUBLANG") + parser.passthrough.append("charset") + parser.passthrough.append("lang") + parser.passthrough.append("sublang") + parser.add_fuzzy_option() + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2sub b/translate-toolkit-1.5.1/translate/convert/po2sub new file mode 100755 index 0000000..9060d4f --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2sub @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a gettext .po localization file to a supported subtitle file""" + +from translate.convert import po2sub + +if __name__ == '__main__': + po2sub.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2sub.py b/translate-toolkit-1.5.1/translate/convert/po2sub.py new file mode 100644 index 0000000..4cffde4 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2sub.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2008-2009 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +"""Convert Gettext PO localization files to subtitle files""" + +from translate.storage import factory +from translate.storage import subtitles + +class resub: + def __init__(self, templatefile): + self.templatefile = templatefile + self.templatestore = subtitles.SubtitleFile(templatefile) + self._inputdict = {} + + def convertstore(self, inputstore, includefuzzy=False): + self._makestoredict(inputstore, includefuzzy) + for unit in self.templatestore.units: + for location in unit.getlocations(): + if location in self._inputdict: + unit.target = self._inputdict[location] + else: + unit.target = unit.source + return str(self.templatestore) + + def _makestoredict(self, store, includefuzzy=False): + # make a dictionary of the translations + for unit in store.units: + if includefuzzy or not unit.isfuzzy(): + # there may be more than one entity due to msguniq merge + for location in unit.getlocations(): + substring = unit.target + if len(substring.strip()) == 0: + substring = unit.source + self._inputdict[location] = substring + +def convertsub(inputfile, outputfile, templatefile, includefuzzy=False): + inputstore = factory.getobject(inputfile) + if templatefile is None: + raise ValueError("must have template file for subtitle files") + else: + convertor = resub(templatefile) + outputstring = convertor.convertstore(inputstore, includefuzzy) + outputfile.write(outputstring) + return 1 + +def main(argv=None): + # handle command line options + from translate.convert import convert + formats = {("po", "srt"): ("srt", convertsub)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_fuzzy_option() + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2symb b/translate-toolkit-1.5.1/translate/convert/po2symb new file mode 100755 index 0000000..fae60c1 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2symb @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of the Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""Convert a gettext .po localization file to a Symbian localisation file""" + +from translate.convert import po2symb + +if __name__ == '__main__': + po2symb.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2symb.py b/translate-toolkit-1.5.1/translate/convert/po2symb.py new file mode 100644 index 0000000..a202446 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2symb.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of the Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""convert Gettext PO localization files to Symbian translation files.""" + +import sys +from translate.storage import factory +from translate.storage.pypo import po_escape_map +from translate.storage.symbian import * + +def escape(text): + for key, val in po_escape_map.iteritems(): + text = text.replace(key, val) + return '"%s"' % text + +def replace_header_items(ps, replacments): + match = read_while(ps, header_item_or_end_re.match, lambda match: match is None) + while not ps.current_line.startswith('*/'): + match = header_item_re.match(ps.current_line) + if match is not None: + key = match.groupdict()['key'] + if key in replacments: + ps.current_line = match.expand('\g<key>\g<space>%s\n' % replacments[key]) + ps.read_line() + +def parse(ps, header_replacements, body_replacements): + replace_header_items(ps, header_replacements) + try: + while True: + eat_whitespace(ps) + skip_no_translate(ps) + match = string_entry_re.match(ps.current_line) + if match is not None: + key = match.groupdict()['id'] + if key in body_replacements: + value = body_replacements[key].target or body_replacements[key].source + ps.current_line = match.expand(u'\g<start>\g<id>\g<space>%s\n' % escape(value)) + ps.read_line() + except StopIteration: + pass + +def line_saver(charset): + result = [] + def save_line(line): + result.append(line.encode(charset)) + return result, save_line + +def write_symbian(f, header_replacements, body_replacements): + lines = list(f) + charset = read_charset(lines) + result, save_line = line_saver(charset) + parse(ParseState(iter(lines), charset, save_line), header_replacements, body_replacements) + return result + +def build_location_index(store): + po_header = store.parseheader() + index = {} + for unit in store.units: + for location in unit.getlocations(): + index[location] = unit + index['r_string_languagegroup_name'] = store.UnitClass(po_header['Language-Team']) + return index + +def build_header_index(store): + po_header = store.parseheader() + return {'Author': po_header['Last-Translator']} + +def convert_symbian(input_file, output_file, template_file, pot=False, duplicatestyle="msgctxt"): + store = factory.getobject(input_file) + location_index = build_location_index(store) + header_index = build_header_index(store) + output = write_symbian(template_file, header_index, location_index) + for line in output: + output_file.write(line) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"po": ("r0", convert_symbian)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2tiki b/translate-toolkit-1.5.1/translate/convert/po2tiki new file mode 100644 index 0000000..f63af7d --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2tiki @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# +# Copyright 2008 Mozilla Corporation, Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a gettext .po localization file to a TikiWiki style language.php file""" + +from translate.convert import po2tiki + +if __name__ == '__main__': + po2tiki.main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2tiki.py b/translate-toolkit-1.5.1/translate/convert/po2tiki.py new file mode 100644 index 0000000..4860174 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2tiki.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2008 Mozilla Corporation, Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" Convert .po files to TikiWiki's language.php files. """ + +import sys +from translate.storage import tiki +from translate.storage import po + +class po2tiki: + def convertstore(self, thepofile): + """Converts a given (parsed) po file to a tiki file. + + @param thepofile: a pofile pre-loaded with input data + """ + thetargetfile = tiki.TikiStore() + for unit in thepofile.units: + if not (unit.isblank() or unit.isheader()): + newunit = tiki.TikiUnit(unit.source) + newunit.settarget(unit.target) + locations = unit.getlocations() + if locations: + newunit.addlocations(locations) + # If a word is "untranslated" but the target isn't empty and isn't the same as the source + # it's been translated and we switch it. This is an assumption but should remain true as long + # as these scripts are used. + if newunit.getlocations() == ["untranslated"] and unit.source != unit.target and unit.target != "": + newunit.location = [] + newunit.addlocation("translated") + + thetargetfile.addunit(newunit) + return thetargetfile + +def convertpo(inputfile, outputfile, template=None): + """Converts from po file format to tiki. + + @param inputfile: file handle of the source + @param outputfile: file handle to write to + @param template: unused + """ + inputstore = po.pofile(inputfile) + if inputstore.isempty(): + return False + convertor = po2tiki() + outputstore = convertor.convertstore(inputstore) + outputfile.write(str(outputstore)) + return True + +def main(argv=None): + """Will convert from .po to tiki style .php""" + from translate.convert import convert + from translate.misc import stdiotell + sys.stdout = stdiotell.StdIOWrapper(sys.stdout) + + formats = {"po":("tiki",convertpo)} + + parser = convert.ConvertOptionParser(formats, description=__doc__) + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2tmx b/translate-toolkit-1.5.1/translate/convert/po2tmx new file mode 100755 index 0000000..3915aa5 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2tmx @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2005 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts Gettext .po files to Qt .ts localization files +You can convert from .ts to .po using po2ts""" + +from translate.convert import po2tmx + +if __name__ == '__main__': + po2tmx.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2tmx.py b/translate-toolkit-1.5.1/translate/convert/po2tmx.py new file mode 100644 index 0000000..82a0314 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2tmx.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2005, 2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert Gettext PO localization files to a TMX (Translation Memory eXchange) file + +see: http://translate.sourceforge.net/wiki/toolkit/po2tmx for examples and +usage instructions +""" + +from translate.storage import po +from translate.storage import tmx +from translate.convert import convert +from translate.misc import wStringIO +import os + +class po2tmx: + def convertfile(self, inputfile, sourcelanguage='en', targetlanguage=None): + """converts a .po file to TMX file""" + # TODO: This seems to not be used... remove it + inputstore = inputfile + for inunit in inputstore.units: + if inunit.isheader() or inunit.isblank() or not inunit.istranslated() or inunit.isfuzzy(): + continue + source = inunit.source + translation = inunit.target + # TODO place source location in comments + tmxfile.addtranslation(source, sourcelanguage, translation, targetlanguage) + return str(tmxfile) + + def convertfiles(self, inputfile, tmxfile, sourcelanguage='en', targetlanguage=None): + """converts a .po file (possibly many) to TMX file""" + inputstore = po.pofile(inputfile) + for inunit in inputstore.units: + if inunit.isheader() or inunit.isblank() or not inunit.istranslated() or inunit.isfuzzy(): + continue + source = inunit.source + translation = inunit.target + # TODO place source location in comments + tmxfile.addtranslation(source, sourcelanguage, translation, targetlanguage) + +def convertpo(inputfile, outputfile, templatefile, sourcelanguage='en', targetlanguage=None): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + convertor = po2tmx() + convertor.convertfiles(inputfile, outputfile.tmxfile, sourcelanguage, targetlanguage) + return 1 + +class tmxmultifile: + def __init__(self, filename, mode=None): + """initialises tmxmultifile from a seekable inputfile or writable outputfile""" + self.filename = filename + if mode is None: + if os.path.exists(filename): + mode = 'r' + else: + mode = 'w' + self.mode = mode +# self.multifilestyle = multifilestyle + self.multifilename = os.path.splitext(filename)[0] +# self.multifile = open(filename, mode) + self.tmxfile = tmx.tmxfile() + + def openoutputfile(self, subfile): + """returns a pseudo-file object for the given subfile""" + def onclose(contents): + pass + outputfile = wStringIO.CatchStringOutput(onclose) + outputfile.filename = subfile + outputfile.tmxfile = self.tmxfile + return outputfile + + +class TmxOptionParser(convert.ArchiveConvertOptionParser): + def recursiveprocess(self, options): + if not options.targetlanguage: + raise ValueError("You must specify the target language") + super(TmxOptionParser, self).recursiveprocess(options) + self.output = open(options.output, 'w') + options.outputarchive.tmxfile.setsourcelanguage(options.sourcelanguage) + self.output.write(str(options.outputarchive.tmxfile)) + +def main(argv=None): + formats = {"po": ("tmx", convertpo), ("po", "tmx"): ("tmx", convertpo)} + archiveformats = {(None, "output"): tmxmultifile, (None, "template"): tmxmultifile} + parser = TmxOptionParser(formats, usepots=False, usetemplates=False, description=__doc__, archiveformats=archiveformats) + parser.add_option("-l", "--language", dest="targetlanguage", default=None, + help="set target language code (e.g. af-ZA) [required]", metavar="LANG") + parser.add_option("", "--source-language", dest="sourcelanguage", default='en', + help="set source language code (default: en)", metavar="LANG") + parser.passthrough.append("sourcelanguage") + parser.passthrough.append("targetlanguage") + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2ts b/translate-toolkit-1.5.1/translate/convert/po2ts new file mode 100755 index 0000000..e95fbe6 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2ts @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts Gettext .po files to Qt .ts localization files +You can convert from .ts to .po using po2ts""" + +from translate.convert import po2ts + +if __name__ == '__main__': + po2ts.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2ts.py b/translate-toolkit-1.5.1/translate/convert/po2ts.py new file mode 100644 index 0000000..4616051 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2ts.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert Gettext PO localization files to Qt Linguist (.ts) files + +see: http://translate.sourceforge.net/wiki/toolkit/po2ts for examples and +usage instructions +""" + +from translate.storage import po +from translate.storage import ts + +class po2ts: + def convertstore(self, inputstore, templatefile=None, context=None): + """converts a .po file to .ts format (using a template .ts file if given)""" + if templatefile is None: + tsfile = ts.QtTsParser() + else: + tsfile = ts.QtTsParser(templatefile) + for inputunit in inputstore.units: + if inputunit.isheader() or inputunit.isblank(): + continue + source = inputunit.source + translation = inputunit.target + comment = inputunit.getnotes("translator") + transtype = None + if not inputunit.istranslated(): + transtype = "unfinished" + elif inputunit.getnotes("developer") == "(obsolete)": + transtype = "obsolete" + if isinstance(source, str): + source = source.decode("utf-8") + if isinstance(translation, str): + translation = translation.decode("utf-8") + for sourcelocation in inputunit.getlocations(): + if context is None: + if "#" in sourcelocation: + contextname = sourcelocation[:sourcelocation.find("#")] + else: + contextname = sourcelocation + else: + contextname = context + tsfile.addtranslation(contextname, source, translation, comment, transtype, createifmissing=True) + return tsfile.getxml() + +def convertpo(inputfile, outputfile, templatefile, context): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + inputstore = po.pofile(inputfile) + if inputstore.isempty(): + return 0 + convertor = po2ts() + outputstring = convertor.convertstore(inputstore, templatefile, context) + outputfile.write(outputstring) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"po": ("ts", convertpo), ("po", "ts"): ("ts", convertpo)} + parser = convert.ConvertOptionParser(formats, usepots=False, usetemplates=True, description=__doc__) + parser.add_option("-c", "--context", dest="context", default=None, + help="use supplied context instead of the one in the .po file comment") + parser.passthrough.append("context") + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2txt b/translate-toolkit-1.5.1/translate/convert/po2txt new file mode 100755 index 0000000..5e3ea45 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2txt @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""script to translate a set of plain text message files using gettext .po localization +You can generate the po files using txt2po""" + +from translate.convert import po2txt + +if __name__ == '__main__': + po2txt.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2txt.py b/translate-toolkit-1.5.1/translate/convert/po2txt.py new file mode 100644 index 0000000..3ec6353 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2txt.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert Gettext PO localization files to plain text (.txt) files + +see: http://translate.sourceforge.net/wiki/toolkit/po2txt for examples and +usage instructions +""" + +from translate.storage import factory +try: + import textwrap +except: + textwrap = None + +class po2txt: + """po2txt can take a po file and generate txt. best to give it a template file otherwise will just concat msgstrs""" + def __init__(self, wrap=None): + self.wrap = wrap + + def wrapmessage(self, message): + """rewraps text as required""" + if self.wrap is None: + return message + return "\n".join([textwrap.fill(line, self.wrap, replace_whitespace=False) for line in message.split("\n")]) + + def convertstore(self, inputstore, includefuzzy): + """converts a file to txt format""" + txtresult = "" + for unit in inputstore.units: + if unit.isheader(): + continue + if unit.istranslated() or (includefuzzy and unit.isfuzzy()): + txtresult += self.wrapmessage(unit.target) + "\n" + "\n" + else: + txtresult += self.wrapmessage(unit.source) + "\n" + "\n" + return txtresult.rstrip() + + def mergestore(self, inputstore, templatetext, includefuzzy): + """converts a file to txt format""" + txtresult = templatetext + # TODO: make a list of blocks of text and translate them individually + # rather than using replace + for unit in inputstore.units: + if unit.isheader(): + continue + if not unit.isfuzzy() or includefuzzy: + txtsource = unit.source + txttarget = self.wrapmessage(unit.target) + if unit.istranslated(): + txtresult = txtresult.replace(txtsource, txttarget) + return txtresult + +def converttxt(inputfile, outputfile, templatefile, wrap=None, includefuzzy=False, encoding='utf-8'): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + inputstore = factory.getobject(inputfile) + convertor = po2txt(wrap=wrap) + if templatefile is None: + outputstring = convertor.convertstore(inputstore, includefuzzy) + else: + templatestring = templatefile.read().decode(encoding) + outputstring = convertor.mergestore(inputstore, templatestring, includefuzzy) + outputfile.write(outputstring.encode('utf-8')) + return 1 + +def main(argv=None): + from translate.convert import convert + from translate.misc import stdiotell + import sys + sys.stdout = stdiotell.StdIOWrapper(sys.stdout) + formats = {("po", "txt"):("txt", converttxt), ("po"):("txt", converttxt), ("xlf", "txt"):("txt", converttxt), ("xlf"):("txt", converttxt)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.add_option("", "--encoding", dest="encoding", default='utf-8', type="string", + help="The encoding of the template file (default: UTF-8)") + parser.passthrough.append("encoding") + if textwrap is not None: + parser.add_option("-w", "--wrap", dest="wrap", default=None, type="int", + help="set number of columns to wrap text at", metavar="WRAP") + parser.passthrough.append("wrap") + parser.add_fuzzy_option() + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2web2py b/translate-toolkit-1.5.1/translate/convert/po2web2py new file mode 100755 index 0000000..5fbf9be --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2web2py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2009 Zuza Software Foundation +# +# This file is part of translate. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""convert GNU/gettext PO files to web2py translation dictionaries (.py)""" + +from translate.convert import po2web2py + +if __name__ == '__main__': + po2web2py.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2web2py.py b/translate-toolkit-1.5.1/translate/convert/po2web2py.py new file mode 100644 index 0000000..6df690f --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2web2py.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2009 Zuza Software Foundation +# +# This file is part of translate. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# (c) 2009 Dominic König (dominic@nursix.org) +# + +"""convert GNU/gettext PO files to web2py translation dictionaries (.py)""" + +from translate.storage import factory + +class po2pydict: + def __init__(self): + return + + def convertstore(self, inputstore, includefuzzy): + from StringIO import StringIO + str_obj = StringIO() + + mydict = dict() + for unit in inputstore.units: + if unit.isheader(): + continue + if unit.istranslated() or (includefuzzy and unit.isfuzzy()): + mydict[unit.source] = unit.target + else: + mydict[unit.source] = '*** ' + unit.source + + str_obj.write('{\n') + for source_str in mydict: + str_obj.write("%s:%s,\n" % (repr(str(source_str)),repr(str(mydict[source_str])))) + str_obj.write('}\n') + str_obj.seek(0) + return str_obj + +def convertpy(inputfile, outputfile, templatefile=None, includefuzzy=False): + inputstore = factory.getobject(inputfile) + convertor = po2pydict() + outputstring = convertor.convertstore(inputstore, includefuzzy) + outputfile.write(outputstring.read()) + return 1 + +def main(argv=None): + from translate.convert import convert + from translate.misc import stdiotell + import sys + sys.stdout = stdiotell.StdIOWrapper(sys.stdout) + formats = {("po", "py"):("py", convertpy), ("po"):("py", convertpy)} + parser = convert.ConvertOptionParser(formats, usetemplates=False, description=__doc__) + parser.add_fuzzy_option() + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2wordfast b/translate-toolkit-1.5.1/translate/convert/po2wordfast new file mode 100755 index 0000000..33e6988 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2wordfast @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# +# Copyright 2007 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts Gettext .po files to Wordfast Translation Memory files""" + +from translate.convert import po2wordfast + +if __name__ == '__main__': + po2wordfast.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2wordfast.py b/translate-toolkit-1.5.1/translate/convert/po2wordfast.py new file mode 100644 index 0000000..db46196 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2wordfast.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2005-2007 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert Gettext PO localization files to a Wordfast translation memory file + +see: http://translate.sourceforge.net/wiki/toolkit/po2wordfast for examples and +usage instructions +""" + +from translate.storage import po +from translate.storage import wordfast +from translate.convert import convert +from translate.misc import wStringIO +import os + +class po2wordfast: + def convertfiles(self, inputfile, wffile, sourcelanguage='en', targetlanguage=None): + """converts a .po file (possibly many) to a Wordfast TM file""" + inputstore = po.pofile(inputfile) + for inunit in inputstore.units: + if inunit.isheader() or inunit.isblank() or not inunit.istranslated(): + continue + source = inunit.source + target = inunit.target + newunit = wffile.addsourceunit(source) + newunit.target = target + newunit.targetlang = targetlanguage + +def convertpo(inputfile, outputfile, templatefile, sourcelanguage='en', targetlanguage=None): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + convertor = po2wordfast() + outputfile.wffile.header.targetlang = targetlanguage + convertor.convertfiles(inputfile, outputfile.wffile, sourcelanguage, targetlanguage) + return 1 + +class wfmultifile: + def __init__(self, filename, mode=None): + """initialises wfmultifile from a seekable inputfile or writable outputfile""" + self.filename = filename + if mode is None: + if os.path.exists(filename): + mode = 'r' + else: + mode = 'w' + self.mode = mode + self.multifilename = os.path.splitext(filename)[0] + self.wffile = wordfast.WordfastTMFile() + + def openoutputfile(self, subfile): + """returns a pseudo-file object for the given subfile""" + def onclose(contents): + pass + outputfile = wStringIO.CatchStringOutput(onclose) + outputfile.filename = subfile + outputfile.wffile = self.wffile + return outputfile + + +class WfOptionParser(convert.ArchiveConvertOptionParser): + def recursiveprocess(self, options): + if not options.targetlanguage: + raise ValueError("You must specify the target language") + super(WfOptionParser, self).recursiveprocess(options) + self.output = open(options.output, 'w') + #options.outputarchive.wffile.setsourcelanguage(options.sourcelanguage) + self.output.write(str(options.outputarchive.wffile)) + +def main(argv=None): + formats = {"po": ("txt", convertpo), ("po", "txt"): ("txt", convertpo)} + archiveformats = {(None, "output"): wfmultifile, (None, "template"): wfmultifile} + parser = WfOptionParser(formats, usepots=False, usetemplates=False, description=__doc__, archiveformats=archiveformats) + parser.add_option("-l", "--language", dest="targetlanguage", default=None, + help="set target language code (e.g. af-ZA) [required]", metavar="LANG") + parser.add_option("", "--source-language", dest="sourcelanguage", default='en', + help="set source language code (default: en)", metavar="LANG") + parser.passthrough.append("sourcelanguage") + parser.passthrough.append("targetlanguage") + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/po2xliff b/translate-toolkit-1.5.1/translate/convert/po2xliff new file mode 100755 index 0000000..fac5eb0 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2xliff @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2005 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts Gettext .po files to .xliff localization files +You can convert from .xliff to .po using po2xliff""" + +from translate.convert import po2xliff + +if __name__ == '__main__': + po2xliff.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/po2xliff.py b/translate-toolkit-1.5.1/translate/convert/po2xliff.py new file mode 100644 index 0000000..4fdc77a --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/po2xliff.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2005, 2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert Gettext PO localization files to XLIFF localization files + +see: http://translate.sourceforge.net/wiki/toolkit/po2xliff for examples and +usage instructions +""" + +from translate.storage import po +from translate.storage import poxliff + +class po2xliff: + def convertunit(self, outputstore, inputunit, filename): + """creates a transunit node""" + source = inputunit.source + target = inputunit.target + if inputunit.isheader(): + unit = outputstore.addheaderunit(target, filename) + else: + unit = outputstore.addsourceunit(source, filename, True) + unit.target = target + #Explicitly marking the fuzzy state will ensure that normal (translated) + #units in the PO file end up as approved in the XLIFF file. + if target: + unit.markfuzzy(inputunit.isfuzzy()) + else: + unit.markapproved(False) + + #Handle #: location comments + for location in inputunit.getlocations(): + unit.createcontextgroup("po-reference", self.contextlist(location), purpose="location") + + #Handle #. automatic comments + comment = inputunit.getnotes("developer") + if comment: + unit.createcontextgroup("po-entry", [("x-po-autocomment", comment)], purpose="information") + unit.addnote(comment, origin="developer") + + #TODO: x-format, etc. + + + #Handle # other comments + comment = inputunit.getnotes("translator") + if comment: + unit.createcontextgroup("po-entry", [("x-po-trancomment", comment)], purpose="information") + unit.addnote(comment, origin="po-translator") + + return unit + + def contextlist(self, location): + contexts = [] + if ":" in location: + sourcefile, linenumber = location.split(":", 1) + else: + sourcefile, linenumber = location, None + contexts.append(("sourcefile", sourcefile)) + if linenumber: + contexts.append(("linenumber", linenumber)) + return contexts + + def convertstore(self, inputstore, templatefile=None, **kwargs): + """converts a .po file to .xlf format""" + if templatefile is None: + outputstore = poxliff.PoXliffFile(**kwargs) + else: + outputstore = poxliff.PoXliffFile(templatefile, **kwargs) + filename = inputstore.filename + for inputunit in inputstore.units: + if inputunit.isblank(): + continue + transunitnode = self.convertunit(outputstore, inputunit, filename) + return str(outputstore) + +def convertpo(inputfile, outputfile, templatefile): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + inputstore = po.pofile(inputfile) + if inputstore.isempty(): + return 0 + convertor = po2xliff() + outputstring = convertor.convertstore(inputstore, templatefile) + outputfile.write(outputstring) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"po": ("xlf", convertpo), ("po", "xlf"): ("xlf", convertpo)} + parser = convert.ConvertOptionParser(formats, usepots=True, usetemplates=True, description=__doc__) + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/poreplace.py b/translate-toolkit-1.5.1/translate/convert/poreplace.py new file mode 100644 index 0000000..4df9841 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/poreplace.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to do replacements on translated strings inside po files""" + +# this is used as the basis for other scripts, it currently replaces nothing + +from translate.storage import po + +class poreplace: + def convertstring(self, postr): + """does the conversion required on the given string (nothing in this case)""" + return postr + + def convertfile(self, thepofile): + """goes through a po file and converts each element""" + for thepo in thepofile.units: + thepo.msgstr = [self.convertstring(postr) for postr in thepo.msgstr] + return thepofile + + def convertpo(self, inputfile, outputfile, templatefile): + """reads in inputfile using po, converts using poreplace, writes to outputfile""" + # note that templatefile is not used, but it is required by the converter... + inputstore = po.pofile(inputfile) + if inputstore.isempty(): + return 0 + outputstore = self.convertfile(inputstore) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(converterclass, argv=None): + # handle command line options + from translate.convert import convert + replacer = converterclass() + formats = {"po":("po", replacer.convertpo), "pot":("pot", replacer.convertpo)} + parser = convert.ConvertOptionParser(formats, usepots=True) + parser.run(argv) + +if __name__ == '__main__': + main(poreplace) + diff --git a/translate-toolkit-1.5.1/translate/convert/pot2po b/translate-toolkit-1.5.1/translate/convert/pot2po new file mode 100755 index 0000000..3b6cf72 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/pot2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert a gettext .pot template to a .po translation file, merging in existing translations if present""" + +from translate.convert import pot2po + +if __name__ == '__main__': + pot2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/pot2po.py b/translate-toolkit-1.5.1/translate/convert/pot2po.py new file mode 100644 index 0000000..1150e93 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/pot2po.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2009 Zuza Software Foundation +# +# This file is part of translate. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""Convert template files (like .pot or template .xlf files) translation files, +preserving existing translations. + +See: http://translate.sourceforge.net/wiki/toolkit/pot2po for examples and +usage instructions. +""" + +from translate.storage import factory +from translate.search import match +from translate.misc.multistring import multistring +from translate.tools import pretranslate +from translate.storage import poheader + + +def convertpot(input_file, output_file, template_file, tm=None, min_similarity=75, fuzzymatching=True, **kwargs): + """Main conversion function""" + + input_store = factory.getobject(input_file) + template_store = None + if template_file is not None: + template_store = factory.getobject(template_file) + output_store = convert_stores(input_store, template_store, tm, min_similarity, fuzzymatching, **kwargs) + output_file.write(str(output_store)) + return 1 + +def convert_stores(input_store, template_store, tm=None, min_similarity=75, fuzzymatching=True, **kwargs): + """Actual conversion function, works on stores not files, returns + a properly initialized pretranslated output store, with structure + based on input_store, metadata based on template_store, migrates + old translations from template_store and pretranslating from tm""" + + #prepare for merging + output_store = type(input_store)() + #create fuzzy matchers to be used by pretranslate.pretranslate_unit + matchers = [] + _prepare_merge(input_store, output_store, template_store) + if fuzzymatching: + if template_store: + matcher = match.matcher(template_store, max_candidates=1, min_similarity=min_similarity, max_length=3000, usefuzzy=True) + matcher.addpercentage = False + matchers.append(matcher) + if tm: + matcher = pretranslate.memory(tm, max_candidates=1, min_similarity=min_similarity, max_length=1000) + matcher.addpercentage = False + matchers.append(matcher) + + #initialize store + _store_pre_merge(input_store, output_store, template_store) + + # Do matching + for input_unit in input_store.units: + if input_unit.istranslatable(): + input_unit = pretranslate.pretranslate_unit(input_unit, template_store, matchers, mark_reused=True) + _unit_post_merge(input_unit, input_store, output_store, template_store) + output_store.addunit(input_unit) + + #finalize store + _store_post_merge(input_store, output_store, template_store) + + return output_store + + +##dispatchers +def _prepare_merge(input_store, output_store, template_store, **kwargs): + """Prepare stores & TM matchers before merging.""" + #dispatch to format specific functions + prepare_merge_hook = "_prepare_merge_%s" % input_store.__class__.__name__ + if globals().has_key(prepare_merge_hook): + globals()[prepare_merge_hook](input_store, output_store, template_store, **kwargs) + + #generate an index so we can search by source string and location later on + input_store.makeindex() + if template_store: + template_store.makeindex() + + +def _store_pre_merge(input_store, output_store, template_store, **kwargs) : + """Initialize the new file with things like headers and metadata.""" + #formats that implement poheader interface are a special case + if isinstance(input_store, poheader.poheader): + _do_poheaders(input_store, output_store, template_store) + + #dispatch to format specific functions + store_pre_merge_hook = "_store_pre_merge_%s" % input_store.__class__.__name__ + if globals().has_key(store_pre_merge_hook): + globals()[store_pre_merge_hook](input_store, output_store, template_store, **kwargs) + + +def _store_post_merge(input_store, output_store, template_store, **kwargs) : + """Close file after merging all translations, used for adding + statistics, obsolete messages and similar wrapup tasks.""" + #dispatch to format specific functions + store_post_merge_hook = "_store_post_merge_%s" % input_store.__class__.__name__ + if globals().has_key(store_post_merge_hook): + globals()[store_post_merge_hook](input_store, output_store, template_store, **kwargs) + +def _unit_post_merge(input_unit, input_store, output_store, template_store, **kwargs): + """Handle any unit level cleanup and situations not handled by the merge() + function.""" + #dispatch to format specific functions + unit_post_merge_hook = "_unit_post_merge_%s" % input_unit.__class__.__name__ + if globals().has_key(unit_post_merge_hook): + globals()[unit_post_merge_hook](input_unit, input_store, output_store, template_store, **kwargs) + + +##format specific functions +def _prepare_merge_pofile(input_store, output_store, template_store): + """PO format specific template preparation logic.""" + #we need to revive obsolete units to be able to consider + #their translation when matching + if template_store: + for unit in template_store.units: + if unit.isobsolete(): + unit.resurrect() + + +def _unit_post_merge_pounit(input_unit, input_store, output_store, template_store): + """PO format specific plural string initializtion logic.""" + #FIXME: do we want to do that for poxliff also? + if input_unit.hasplural() and len(input_unit.target) == 0: + # untranslated plural unit; Let's ensure that we have the correct number of plural forms: + nplurals, plural = output_store.getheaderplural() + if nplurals and nplurals.isdigit() and nplurals != '2': + input_unit.target = multistring([""]*int(nplurals)) + + +def _store_post_merge_pofile(input_store, output_store, template_store): + """PO format specific: adds newly obsoleted messages to end of store.""" + #Let's take care of obsoleted messages + if template_store: + newlyobsoleted = [] + for unit in template_store.units: + if unit.isheader(): + continue + if unit.target and not (input_store.findunit(unit.source) or hasattr(unit, "reused")): + #not in .pot, make it obsolete + unit.makeobsolete() + newlyobsoleted.append(unit) + elif unit.isobsolete(): + output_store.addunit(unit) + for unit in newlyobsoleted: + output_store.addunit(unit) + + +def _do_poheaders(input_store, output_store, template_store): + """Adds initialized PO headers to output store.""" + # header values + charset = "UTF-8" + encoding = "8bit" + project_id_version = None + pot_creation_date = None + po_revision_date = None + last_translator = None + language_team = None + mime_version = None + plural_forms = None + kwargs = {} + + if template_store is not None and isinstance(template_store, poheader.poheader): + templateheadervalues = template_store.parseheader() + for key, value in templateheadervalues.iteritems(): + if key == "Project-Id-Version": + project_id_version = value + elif key == "Last-Translator": + last_translator = value + elif key == "Language-Team": + language_team = value + elif key == "PO-Revision-Date": + po_revision_date = value + elif key in ("POT-Creation-Date", "MIME-Version"): + # don't know how to handle these keys, or ignoring them + pass + elif key == "Content-Type": + kwargs[key] = value + elif key == "Content-Transfer-Encoding": + encoding = value + elif key == "Plural-Forms": + plural_forms = value + else: + kwargs[key] = value + + inputheadervalues = input_store.parseheader() + for key, value in inputheadervalues.iteritems(): + if key in ("Project-Id-Version", "Last-Translator", "Language-Team", "PO-Revision-Date", "Content-Type", "Content-Transfer-Encoding", "Plural-Forms"): + # want to carry these from the template so we ignore them + pass + elif key == "POT-Creation-Date": + pot_creation_date = value + elif key == "MIME-Version": + mime_version = value + else: + kwargs[key] = value + + output_header = output_store.makeheader(charset=charset, encoding=encoding, project_id_version=project_id_version, + pot_creation_date=pot_creation_date, po_revision_date=po_revision_date, last_translator=last_translator, + language_team=language_team, mime_version=mime_version, plural_forms=plural_forms, **kwargs) + + # Get the header comments and fuzziness state + if template_store is not None and len(template_store.units) > 0: + if template_store.units[0].isheader(): + if template_store.units[0].getnotes("translator"): + output_header.addnote(template_store.units[0].getnotes("translator"), "translator") + if input_store.units[0].getnotes("developer"): + output_header.addnote(input_store.units[0].getnotes("developer"), "developer") + output_header.markfuzzy(template_store.units[0].isfuzzy()) + elif len(input_store.units) > 0 and input_store.units[0].isheader(): + output_header.addnote(input_store.units[0].getnotes()) + + output_store.addunit(output_header) + + +def main(argv=None): + from translate.convert import convert + formats = {"pot": ("po", convertpot), ("pot", "po"): ("po", convertpot), + "xlf": ("xlf", convertpot), ("xlf", "xlf"): ("xlf", convertpot), + } + parser = convert.ConvertOptionParser(formats, usepots=True, usetemplates=True, + allowmissingtemplate=True, description=__doc__) + parser.add_option("", "--tm", dest="tm", default=None, + help="The file to use as translation memory when fuzzy matching") + parser.passthrough.append("tm") + defaultsimilarity = 75 + parser.add_option("-s", "--similarity", dest="min_similarity", default=defaultsimilarity, + type="float", help="The minimum similarity for inclusion (default: %d%%)" % defaultsimilarity) + parser.passthrough.append("min_similarity") + parser.add_option("--nofuzzymatching", dest="fuzzymatching", action="store_false", + default=True, help="Disable fuzzy matching") + parser.passthrough.append("fuzzymatching") + parser.run(argv) + + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/prop2mozfunny.py b/translate-toolkit-1.5.1/translate/convert/prop2mozfunny.py new file mode 100644 index 0000000..f938cee --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/prop2mozfunny.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2005, 2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""converts properties files back to funny mozilla files""" + +from translate.storage import properties +from translate.convert import po2prop +from translate.convert import mozfunny2prop +from translate.misc.wStringIO import StringIO + +def prop2inc(pf): + """convert a properties file back to a .inc file with #defines in it""" + # any leftover blanks will not be included at the end + pendingblanks = [] + for unit in pf.units: + for comment in unit.comments: + if comment.startswith("# converted from") and "#defines" in comment: + pass + else: + for blank in pendingblanks: + yield blank + # TODO: could convert commented # x=y back to # #define x y + yield comment + if unit.isblank(): + pendingblanks.append("\n") + else: + definition = "#define %s %s\n" % (unit.name, unit.value.replace("\n", "\\n")) + if isinstance(definition, unicode): + definition = definition.encode("UTF-8") + for blank in pendingblanks: + yield blank + yield definition + +def prop2it(pf): + """convert a properties file back to a pseudo-properties .it file""" + for unit in pf.units: + for comment in unit.comments: + if comment.startswith("# converted from") and "pseudo-properties" in comment: + pass + elif comment.startswith("# section: "): + yield comment.replace("# section: ", "", 1) + "\n" + else: + yield comment.replace("#", ";", 1) + "\n" + if unit.isblank(): + yield "" + else: + definition = "%s=%s\n" % (unit.name, unit.value) + if isinstance(definition, unicode): + definition = definition.encode("UTF-8") + yield definition + +def prop2funny(src, itencoding="cp1252"): + lines = src.split("\n") + header = lines[0] + if not header.startswith("# converted from "): + waspseudoprops = len([line for line in lines if line.startswith("# section:")]) + wasdefines = len([line for line in lines if line.startswith("#filter") or line.startswith("#unfilter")]) + else: + waspseudoprops = "pseudo-properties" in header + wasdefines = "#defines" in header + lines = lines[1:] + if not (waspseudoprops ^ wasdefines): + raise ValueError("could not determine file type as pseudo-properties or defines file") + pf = properties.propfile(personality="mozilla") + pf.parse("\n".join(lines)) + if wasdefines: + for line in prop2inc(pf): + yield line + "\n" + elif waspseudoprops: + for line in prop2it(pf): + yield line.decode("utf-8").encode(itencoding) + "\n" + +def po2inc(inputfile, outputfile, templatefile, encoding=None, includefuzzy=False): + """wraps po2prop but converts outputfile to properties first""" + outputpropfile = StringIO() + if templatefile is not None: + templatelines = templatefile.readlines() + templateproplines = [line for line in mozfunny2prop.inc2prop(templatelines)] + templatepropfile = StringIO("".join(templateproplines)) + else: + templatepropfile = None + result = po2prop.convertmozillaprop(inputfile, outputpropfile, templatepropfile, includefuzzy=includefuzzy) + if result: + outputpropfile.seek(0) + pf = properties.propfile(outputpropfile, personality="mozilla") + outputlines = prop2inc(pf) + outputfile.writelines(outputlines) + return result + +def po2it(inputfile, outputfile, templatefile, encoding="cp1252", includefuzzy=False): + """wraps po2prop but converts outputfile to properties first""" + outputpropfile = StringIO() + if templatefile is not None: + templatelines = templatefile.readlines() + templateproplines = [line for line in mozfunny2prop.it2prop(templatelines, encoding=encoding)] + templatepropfile = StringIO("".join(templateproplines)) + else: + templatepropfile = None + result = po2prop.convertmozillaprop(inputfile, outputpropfile, templatepropfile, includefuzzy=includefuzzy) + if result: + outputpropfile.seek(0) + pf = properties.propfile(outputpropfile, personality="mozilla") + outputlines = prop2it(pf) + for line in outputlines: + line = line.decode("utf-8").encode(encoding) + outputfile.write(line) + return result + +def po2ini(inputfile, outputfile, templatefile, encoding="UTF-8", includefuzzy=False): + """wraps po2prop but converts outputfile to properties first using UTF-8 encoding""" + return po2it(inputfile=inputfile, outputfile=outputfile, templatefile=templatefile, encoding=encoding, includefuzzy=includefuzzy) + +def main(argv=None): + import sys + # TODO: get encoding from charset.mk, using parameter + src = sys.stdin.read() + for line in prop2funny(src): + sys.stdout.write(line) + +if __name__ == "__main__": + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/prop2po b/translate-toolkit-1.5.1/translate/convert/prop2po new file mode 100755 index 0000000..b5d3678 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/prop2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2002, 2003 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a .properties file to a gettext .po localization file""" + +from translate.convert import prop2po + +if __name__ == '__main__': + prop2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/prop2po.py b/translate-toolkit-1.5.1/translate/convert/prop2po.py new file mode 100644 index 0000000..d557667 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/prop2po.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert Java/Mozilla .properties files to Gettext PO localization files + +See: http://translate.sourceforge.net/wiki/toolkit/prop2po for examples and +usage instructions +""" + +import sys +from translate.storage import po +from translate.storage import properties + +class prop2po: + """convert a .properties file to a .po file for handling the translation...""" + def convertstore(self, thepropfile, personality="java", duplicatestyle="msgctxt"): + """converts a .properties file to a .po file...""" + self.personality = personality + thetargetfile = po.pofile() + if self.personality == "mozilla": + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit", x_accelerator_marker="&") + else: + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit") + targetheader.addnote("extracted from %s" % thepropfile.filename, "developer") + # we try and merge the header po with any comments at the start of the properties file + appendedheader = False + waitingcomments = [] + for propunit in thepropfile.units: + pounit = self.convertunit(propunit, "developer") + if pounit is None: + waitingcomments.extend(propunit.comments) + # FIXME the storage class should not be creating blank units + if pounit is "discard": + continue + if not appendedheader: + if propunit.isblank(): + pounit = targetheader + else: + thetargetfile.addunit(targetheader) + appendedheader = True + if pounit is not None: + pounit.addnote("\n".join(waitingcomments).rstrip(), "developer", position="prepend") + waitingcomments = [] + thetargetfile.addunit(pounit) + thetargetfile.removeduplicates(duplicatestyle) + return thetargetfile + + def mergestore(self, origpropfile, translatedpropfile, personality="java", blankmsgstr=False, duplicatestyle="msgctxt"): + """converts two .properties files to a .po file...""" + self.personality = personality + thetargetfile = po.pofile() + if self.personality == "mozilla": + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit", x_accelerator_marker="&") + else: + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit") + targetheader.addnote("extracted from %s, %s" % (origpropfile.filename, translatedpropfile.filename), "developer") + translatedpropfile.makeindex() + # we try and merge the header po with any comments at the start of the properties file + appendedheader = False + waitingcomments = [] + # loop through the original file, looking at units one by one + for origprop in origpropfile.units: + origpo = self.convertunit(origprop, "developer") + if origpo is None: + waitingcomments.extend(origprop.comments) + # FIXME the storage class should not be creating blank units + if origpo is "discard": + continue + # handle the header case specially... + if not appendedheader: + if origprop.isblank(): + origpo = targetheader + else: + thetargetfile.addunit(targetheader) + appendedheader = True + # try and find a translation of the same name... + if origprop.name in translatedpropfile.locationindex: + translatedprop = translatedpropfile.locationindex[origprop.name] + # Need to check that this comment is not a copy of the developer comments + translatedpo = self.convertunit(translatedprop, "translator") + if translatedpo is "discard": + continue + else: + translatedpo = None + # if we have a valid po unit, get the translation and add it... + if origpo is not None: + if translatedpo is not None and not blankmsgstr: + origpo.target = translatedpo.source + origpo.addnote(u"".join(waitingcomments).rstrip(), "developer", position="prepend") + waitingcomments = [] + thetargetfile.addunit(origpo) + elif translatedpo is not None: + print >> sys.stderr, "error converting original properties definition %s" % origprop.name + thetargetfile.removeduplicates(duplicatestyle) + return thetargetfile + + def convertunit(self, propunit, commenttype): + """Converts a .properties unit to a .po unit. Returns None if empty + or not for translation.""" + if propunit is None: + return None + # escape unicode + pounit = po.pounit(encoding="UTF-8") + if hasattr(propunit, "comments"): + for comment in propunit.comments: + if "DONT_TRANSLATE" in comment: + return "discard" + pounit.addnote(u"".join(propunit.getnotes()).rstrip(), commenttype) + # TODO: handle multiline msgid + if propunit.isblank(): + return None + pounit.addlocation(propunit.name) + pounit.source = propunit.source + pounit.target = u"" + return pounit + +def convertmozillaprop(inputfile, outputfile, templatefile, pot=False, duplicatestyle="msgctxt"): + """Mozilla specific convertor function""" + return convertprop(inputfile, outputfile, templatefile, personality="mozilla", pot=pot, duplicatestyle=duplicatestyle) + +def convertprop(inputfile, outputfile, templatefile, personality="java", pot=False, duplicatestyle="msgctxt"): + """reads in inputfile using properties, converts using prop2po, writes to outputfile""" + inputstore = properties.propfile(inputfile, personality) + convertor = prop2po() + if templatefile is None: + outputstore = convertor.convertstore(inputstore, personality, duplicatestyle=duplicatestyle) + else: + templatestore = properties.propfile(templatefile, personality) + outputstore = convertor.mergestore(templatestore, inputstore, personality, blankmsgstr=pot, duplicatestyle=duplicatestyle) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"properties": ("po", convertprop), ("properties", "properties"): ("po", convertprop)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + parser.add_option("", "--personality", dest="personality", default="java", type="choice", + choices=["java", "mozilla"], + help="set the input behaviour: java (default), mozilla", metavar="TYPE") + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.passthrough.append("personality") + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/rc2po b/translate-toolkit-1.5.1/translate/convert/rc2po new file mode 100755 index 0000000..119cfd8 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/rc2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2002, 2003, 2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a .rc file to a gettext .po localization file""" + +from translate.convert import rc2po + +if __name__ == '__main__': + rc2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/rc2po.py b/translate-toolkit-1.5.1/translate/convert/rc2po.py new file mode 100644 index 0000000..3763270 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/rc2po.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2007-2009 Zuza Software Foundation +# +# This file is part of the Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""convert .rc files to Gettext PO localization files""" + +import sys +from translate.storage import po +from translate.storage import rc + +class rc2po: + """Convert a .rc file to a .po file for handling the translation.""" + def __init__(self, charset=None): + self.charset = charset + + def convert_store(self, input_store, duplicatestyle="msgctxt"): + """converts a .rc file to a .po file...""" + output_store = po.pofile() + output_header = output_store.makeheader(charset="UTF-8", encoding="8bit") + output_header.addnote("extracted from %s" % input_store.filename, "developer") + output_store.addunit(output_header) + for input_unit in input_store.units: + output_unit = self.convert_unit(input_unit, "developer") + if output_unit is not None: + output_store.addunit(output_unit) + output_store.removeduplicates(duplicatestyle) + return output_store + + def merge_store(self, template_store, input_store, blankmsgstr=False, duplicatestyle="msgctxt"): + """converts two .rc files to a .po file...""" + output_store = po.pofile() + output_header = output_store.makeheader(charset="UTF-8", encoding="8bit") + output_header.addnote("extracted from %s, %s" % (template_store.filename, input_store.filename), "developer") + output_store.addunit(output_header) + input_store.makeindex() + for template_unit in template_store.units: + origpo = self.convert_unit(template_unit, "developer") + # try and find a translation of the same name... + template_unit_name = "".join(template_unit.getlocations()) + if template_unit_name in input_store.locationindex: + translatedrc = input_store.locationindex[template_unit_name] + translatedpo = self.convert_unit(translatedrc, "translator") + else: + translatedpo = None + # if we have a valid po unit, get the translation and add it... + if origpo is not None: + if translatedpo is not None and not blankmsgstr: + origpo.target = translatedpo.source + output_store.addunit(origpo) + elif translatedpo is not None: + print >> sys.stderr, "error converting original rc definition %s" % origrc.name + output_store.removeduplicates(duplicatestyle) + return output_store + + def convert_unit(self, input_unit, commenttype): + """Converts a .rc unit to a .po unit. Returns None if empty + or not for translation.""" + if input_unit is None: + return None + # escape unicode + output_unit = po.pounit(encoding="UTF-8") + output_unit.addlocation("".join(input_unit.getlocations())) + output_unit.source = input_unit.source.decode(self.charset) + output_unit.target = "" + return output_unit + +def convertrc(input_file, output_file, template_file, pot=False, duplicatestyle="msgctxt", charset=None, lang=None, sublang=None): + """reads in input_file using rc, converts using rc2po, writes to output_file""" + input_store = rc.rcfile(input_file, lang, sublang) + convertor = rc2po(charset=charset) + if template_file is None: + output_store = convertor.convert_store(input_store, duplicatestyle=duplicatestyle) + else: + template_store = rc.rcfile(template_file, lang, sublang) + output_store = convertor.merge_store(template_store, input_store, blankmsgstr=pot, duplicatestyle=duplicatestyle) + if output_store.isempty(): + return 0 + output_file.write(str(output_store)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"rc": ("po", convertrc), ("rc", "rc"): ("po", convertrc), + "nls": ("po", convertrc), ("nls", "nls"): ("po", convertrc)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + DEFAULTCHARSET = "cp1252" + parser.add_option("", "--charset", dest="charset", default=DEFAULTCHARSET, + help="charset to use to decode the RC files (default: %s)" % DEFAULTCHARSET, metavar="CHARSET") + DEFAULTLANG = "LANG_ENGLISH" + parser.add_option("-l", "--lang", dest="lang", default=DEFAULTLANG, + help="LANG entry (default: %s)" % DEFAULTLANG, metavar="LANG") + DEFAULTSUBLANG = "SUBLANG_DEFAULT" + parser.add_option("", "--sublang", dest="sublang", default=DEFAULTSUBLANG, + help="SUBLANG entry (default: %s)" % DEFAULTSUBLANG, metavar="SUBLANG") + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.passthrough.append("charset") + parser.passthrough.append("lang") + parser.passthrough.append("sublang") + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/sub2po b/translate-toolkit-1.5.1/translate/convert/sub2po new file mode 100755 index 0000000..37cf865 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/sub2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a supported subtitle file to a Gettext PO localization file""" + +from translate.convert import sub2po + +if __name__ == '__main__': + sub2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/sub2po.py b/translate-toolkit-1.5.1/translate/convert/sub2po.py new file mode 100644 index 0000000..0cef322 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/sub2po.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2008-2009 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""convert subtitle files to Gettext PO localization files""" + +import sys +from translate.storage import po +from translate.storage import subtitles + +def convert_store(input_store, duplicatestyle="msgctxt"): + """converts a subtitle file to a .po file...""" + output_store = po.pofile() + output_header = output_store.makeheader(charset="UTF-8", encoding="8bit") + output_header.addnote("extracted from %s" % input_store.filename, "developer") + output_store.addunit(output_header) + for input_unit in input_store.units: + output_unit = convert_unit(input_unit, "developer") + if output_unit is not None: + output_store.addunit(output_unit) + output_store.removeduplicates(duplicatestyle) + return output_store + +def merge_store(template_store, input_store, blankmsgstr=False, duplicatestyle="msgctxt"): + """converts two subtitle files to a .po file...""" + output_store = po.pofile() + output_header = output_store.makeheader(charset="UTF-8", encoding="8bit") + output_header.addnote("extracted from %s, %s" % (template_store.filename, input_store.filename), "developer") + output_store.addunit(output_header) + input_store.makeindex() + for template_unit in template_store.units: + origpo = convert_unit(template_unit, "developer") + # try and find a translation of the same name... + template_unit_name = "".join(template_unit.getlocations()) + if template_unit_name in input_store.locationindex: + translatedini = input_store.locationindex[template_unit_name] + translatedpo = convert_unit(translatedini, "translator") + else: + translatedpo = None + # if we have a valid po unit, get the translation and add it... + if origpo is not None: + if translatedpo is not None and not blankmsgstr: + origpo.target = translatedpo.source + output_store.addunit(origpo) + elif translatedpo is not None: + print >> sys.stderr, "error converting original subtitle definition %s" % origini.name + output_store.removeduplicates(duplicatestyle) + return output_store + +def convert_unit(input_unit, commenttype): + """Converts a subtitle unit to a .po unit. Returns None if empty + or not for translation.""" + if input_unit is None: + return None + # escape unicode + output_unit = po.pounit(encoding="UTF-8") + output_unit.addlocation("".join(input_unit.getlocations())) + output_unit.addnote(input_unit.getnotes("developer"), "developer") + output_unit.source = input_unit.source + output_unit.target = "" + return output_unit + +def convertsub(input_file, output_file, template_file=None, pot=False, duplicatestyle="msgctxt"): + """Reads in L{input_file} using translate.subtitles, converts using L{sub2po}, writes to L{output_file}""" + input_store = subtitles.SubtitleFile(input_file) + if template_file is None: + output_store = convert_store(input_store, duplicatestyle=duplicatestyle) + else: + template_store = subtitles.SubtitleFile(template_file) + output_store = merge_store(template_store, input_store, blankmsgstr=pot, duplicatestyle=duplicatestyle) + if output_store.isempty(): + return 0 + output_file.write(str(output_store)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"srt": ("po", convertsub), ("srt", "srt"): ("po", convertsub)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/symb2po b/translate-toolkit-1.5.1/translate/convert/symb2po new file mode 100755 index 0000000..066d766 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/symb2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of the Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""Convert a Symbian translation file to a gettext .po localization file.""" + +from translate.convert import symb2po + +if __name__ == '__main__': + symb2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/symb2po.py b/translate-toolkit-1.5.1/translate/convert/symb2po.py new file mode 100644 index 0000000..a965656 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/symb2po.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2008-2009 Zuza Software Foundation +# +# This file is part of the Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""Convert Symbian localisation files to Gettext PO localization files.""" + +from translate.storage import factory +from translate.storage.pypo import extractpoline +from translate.storage.symbian import * + +def read_header_items(ps): + match = read_while(ps, header_item_or_end_re.match, lambda match: match is None) + if match.groupdict()['end_comment'] is not None: + return {} + + results = {} + while match: + match_chunks = match.groupdict() + ps.read_line() + results[match_chunks['key']] = match_chunks['value'] + match = header_item_re.match(ps.current_line) + + match = read_while(ps, identity, lambda line: not line.startswith('*/')) + ps.read_line() + return results + +def parse(ps): + header = read_header_items(ps) + units = [] + try: + while True: + eat_whitespace(ps) + skip_no_translate(ps) + match = string_entry_re.match(ps.current_line) + if match is not None: + units.append((match.groupdict()['id'], extractpoline(match.groupdict()['str']))) + ps.read_line() + except StopIteration: + pass + return header, units + +def read_symbian(f): + lines = list(f) + charset = read_charset(lines) + return parse(ParseState(iter(lines), charset)) + +def get_template_dict(template_file): + if template_file is not None: + template_header, template_units = read_symbian(template_file) + return template_header, dict(template_units) + else: + return {}, {} + +def build_output(units, template_header, template_dict): + output_store = factory.classes['po']() + ignore = set(['r_string_languagegroup_name']) + header_entries = { + 'Last-Translator': template_header.get('Author', ''), + 'Language-Team': template_dict.get('r_string_languagegroup_name', ''), + 'Content-Transfer-Encoding': '8bit', + 'Content-Type': 'text/plain; charset=UTF-8', + } + output_store.updateheader(add=True, **header_entries) + for id, source in units: + if id in ignore: + continue + unit = output_store.UnitClass(source) + unit.target = template_dict.get(id, '') + unit.addlocation(id) + output_store.addunit(unit) + return output_store + +def convert_symbian(input_file, output_file, template_file, pot=False, duplicatestyle="msgctxt"): + header, units = read_symbian(input_file) + template_header, template_dict = get_template_dict(template_file) + output_store = build_output(units, template_header, template_dict) + + if output_store.isempty(): + return 0 + else: + output_file.write(str(output_store)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"r01": ("po", convert_symbian)} + parser = convert.ConvertOptionParser(formats, usetemplates=True, usepots=True, description=__doc__) + parser.add_duplicates_option() + parser.passthrough.append("pot") + parser.run(argv) + +if __name__ == '__main__': + main() + diff --git a/translate-toolkit-1.5.1/translate/convert/test_accesskey.py b/translate-toolkit-1.5.1/translate/convert/test_accesskey.py new file mode 100644 index 0000000..a632b73 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_accesskey.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2008 Zuza Software Foundation +# +# This file is part of The Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""Test the various functions for combining and extracting accesskeys and +labels""" + +from translate.convert import accesskey + +def test_get_label_and_accesskey(): + """test that we can extract the label and accesskey components from an + accesskey+label string""" + assert accesskey.extract(u"&File") == (u"File", u"F") + assert accesskey.extract(u"~File", u"~") == (u"File", u"F") + assert accesskey.extract(u"~File", u"~") == (u"File", u"F") + +def test_ignore_entities(): + """test that we don't get confused with entities and a & access key + marker""" + assert accesskey.extract(u"Set &browserName; as &Default") != (u"Set &browserName; as &Default", u"b") + assert accesskey.extract(u"Set &browserName; as &Default") == (u"Set &browserName; as Default", u"D") + +def test_alternate_accesskey_marker(): + """check that we can identify the accesskey if the marker is different""" + assert accesskey.extract(u"~File", u"~") == (u"File", u"F") + assert accesskey.extract(u"&File", u"~") == (u"&File", u"") + +def test_unicode(): + """test that we can do the same with unicode strings""" + assert accesskey.extract(u"Eḓiṱ") == (u"Eḓiṱ", u"") + assert accesskey.extract(u"E&ḓiṱ") == (u"Eḓiṱ", u"ḓ") + assert accesskey.extract(u"E_ḓiṱ", u"_") == (u"Eḓiṱ", u"ḓ") + label, akey = accesskey.extract(u"E&ḓiṱ") + assert label, akey == (u"Eḓiṱ", u"ḓ") + assert isinstance(label, unicode) and isinstance(akey, unicode) + +def test_empty_string(): + """test that we can handle and empty label+accesskey string""" + assert accesskey.extract(u"") == (u"", u"") + assert accesskey.extract(u"", u"~") == (u"", u"") + +def test_combine_label_accesskey(): + """test that we can combine accesskey and label to create a label+accesskey + string""" + assert accesskey.combine(u"File", u"F") == u"&File" + assert accesskey.combine(u"File", u"F", u"~") == u"~File" + +def test_uncombinable(): + """test our behaviour when we cannot combine label and accesskey""" + assert accesskey.combine(u"File", u"D") is None diff --git a/translate-toolkit-1.5.1/translate/convert/test_convert.py b/translate-toolkit-1.5.1/translate/convert/test_convert.py new file mode 100644 index 0000000..31c1b66 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_convert.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import convert +import os +import sys +from py import test +try: + import psyco +except Exception: + psyco = None + +class TestConvertCommand: + """Tests running actual commands on files""" + convertmodule = convert + defaultoptions = {"progress": "none"} + if psyco: + defaultoptions["psyco"] = "none" + + def setup_method(self, method): + """creates a clean test directory for the given method""" + self.testdir = "%s_%s" % (self.__class__.__name__, method.__name__) + self.cleardir() + os.mkdir(self.testdir) + self.rundir = os.path.abspath(os.getcwd()) + + def teardown_method(self, method): + """removes the test directory for the given method""" + os.chdir(self.rundir) + self.cleardir() + + def cleardir(self): + """removes the test directory""" + if os.path.exists(self.testdir): + for dirpath, subdirs, filenames in os.walk(self.testdir, topdown=False): + for name in filenames: + os.remove(os.path.join(dirpath, name)) + for name in subdirs: + os.rmdir(os.path.join(dirpath, name)) + if os.path.exists(self.testdir): os.rmdir(self.testdir) + assert not os.path.exists(self.testdir) + + def run_command(self, *argv, **kwargs): + """runs the command via the main function, passing self.defaultoptions and keyword arguments as --long options and argv arguments straight""" + os.chdir(self.testdir) + argv = list(argv) + kwoptions = getattr(self, "defaultoptions", {}).copy() + kwoptions.update(kwargs) + for key, value in kwoptions.iteritems(): + if value is True: + argv.append("--%s" % key) + else: + argv.append("--%s=%s" % (key, value)) + try: + self.convertmodule.main(argv) + finally: + os.chdir(self.rundir) + + def get_testfilename(self, filename): + """gets the path to the test file""" + return os.path.join(self.testdir, filename) + + def open_testfile(self, filename, mode="r"): + """opens the given filename in the testdirectory in the given mode""" + filename = self.get_testfilename(filename) + if not mode.startswith("r"): + subdir = os.path.dirname(filename) + currentpath = "" + if not os.path.isdir(subdir): + for part in subdir.split(os.sep): + currentpath = os.path.join(currentpath, part) + if not os.path.isdir(currentpath): + os.mkdir(currentpath) + return open(filename, mode) + + def create_testfile(self, filename, contents): + """creates the given file in the testdirectory with the given contents""" + testfile = self.open_testfile(filename, "w") + testfile.write(contents) + + def read_testfile(self, filename): + """reads the given file in the testdirectory and returns the contents""" + testfile = open(self.get_testfilename(filename)) + return testfile.read() + + def help_check(self, options, option, last=False): + """check that a help string occurs and remove it""" + assert option in options + newoptions = [] + for line in options.splitlines(): + if option in line or not line.lstrip().startswith("-"): + continue + newoptions.append(line) + if last: + assert newoptions == [] + return "\n".join(newoptions) + + def test_help(self): + """tests getting help (returning the help_string so further tests can be done)""" + stdout = sys.stdout + helpfile = self.open_testfile("help.txt", "w") + sys.stdout = helpfile + try: + test.raises(SystemExit, self.run_command, help=True) + finally: + sys.stdout = stdout + helpfile.close() + help_string = self.read_testfile("help.txt") + print help_string + convertsummary = self.convertmodule.__doc__.split("\n")[0] + # the convertsummary might be wrapped. this will probably unwrap it + assert convertsummary in help_string.replace("\n", " ") + usageline = help_string[:help_string.find("\n")] + # Different versions of optparse might contain either upper or + # lowercase versions of 'Usage:' and 'Options:', so we need to take + # that into account + assert (usageline.startswith("Usage: ") or usageline.startswith("usage: ")) \ + and "[--version] [-h|--help]" in usageline + options = help_string[help_string.find("ptions:\n"):] + options = options[options.find("\n")+1:] + options = self.help_check(options, "--progress=PROGRESS") + options = self.help_check(options, "--version") + options = self.help_check(options, "-h, --help") + options = self.help_check(options, "--manpage") + options = self.help_check(options, "--errorlevel=ERRORLEVEL") + if psyco: + options = self.help_check(options, "--psyco=MODE") + options = self.help_check(options, "-i INPUT, --input=INPUT") + options = self.help_check(options, "-x EXCLUDE, --exclude=EXCLUDE") + options = self.help_check(options, "-o OUTPUT, --output=OUTPUT") + return options + diff --git a/translate-toolkit-1.5.1/translate/convert/test_csv2po.py b/translate-toolkit-1.5.1/translate/convert/test_csv2po.py new file mode 100644 index 0000000..dfa74f4 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_csv2po.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python + +from translate.convert import csv2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +from translate.storage import csvl10n + +class TestCSV2PO: + def csv2po(self, csvsource, template=None): + """helper that converts csv source to po source without requiring files""" + inputfile = wStringIO.StringIO(csvsource) + inputcsv = csvl10n.csvfile(inputfile) + if template: + templatefile = wStringIO.StringIO(template) + inputpot = po.pofile(templatefile) + else: + inputpot = None + convertor = csv2po.csv2po(templatepo=inputpot) + outputpo = convertor.convertstore(inputcsv) + return outputpo + + def singleelement(self, storage): + """checks that the pofile contains a single non-header element, and returns it""" + print str(storage) + assert len(storage.units) == 1 + return storage.units[0] + + def test_simpleentity(self): + """checks that a simple csv entry definition converts properly to a po entry""" + csvheader = 'location,source,target\n' + csvsource = 'intl.charset.default,ISO-8859-1,UTF-16' + # Headerless + pofile = self.csv2po(csvsource) + pounit = self.singleelement(pofile) + # With header + pofile = self.csv2po(csvheader + csvsource) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ['intl.charset.default'] + assert pounit.source == "ISO-8859-1" + assert pounit.target == "UTF-16" + + def test_simpleentity_with_template(self): + """checks that a simple csv entry definition converts properly to a po entry""" + csvsource = '''source,original,translation +intl.charset.default,ISO-8859-1,UTF-16''' + potsource = '''#: intl.charset.default +msgid "ISO-8859-1" +msgstr "" +''' + pofile = self.csv2po(csvsource, potsource) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ['intl.charset.default'] + assert pounit.source == "ISO-8859-1" + assert pounit.target == "UTF-16" + + def test_newlines(self): + """tests multiline po entries""" + minicsv = r'''"Random comment +with continuation","Original text","Langdradige teks +wat lank aanhou" +''' + pofile = self.csv2po(minicsv) + unit = self.singleelement(pofile) + assert unit.getlocations() == ['Random', 'comment', 'with', 'continuation'] + assert unit.source == "Original text" + print unit.target + assert unit.target == "Langdradige teks\nwat lank aanhou" + + def test_tabs(self): + """Test the escaping of tabs""" + minicsv = ',"First column\tSecond column","Twee kolomme gesky met \t"' + pofile = self.csv2po(minicsv) + unit = self.singleelement(pofile) + print unit.source + assert unit.source == "First column\tSecond column" + assert not pofile.findunit("First column\tSecond column").target == "Twee kolomme gesky met \\t" + + def test_quotes(self): + """Test the escaping of quotes (and slash)""" + minicsv = r''',"Hello ""Everyone""","Good day ""All""" +,"Use \"".","Gebruik \""."''' + print minicsv + csvfile = csvl10n.csvfile(wStringIO.StringIO(minicsv)) + print str(csvfile) + pofile = self.csv2po(minicsv) + unit = pofile.units[0] + assert unit.source == 'Hello "Everyone"' + assert pofile.findunit('Hello "Everyone"').target == 'Good day "All"' + print str(pofile) + for unit in pofile.units: + print unit.source + print unit.target + print +# assert pofile.findunit('Use \\".').target == 'Gebruik \\".' + + def test_empties(self): + """Tests that things keep working with empty entries""" + minicsv = ',Source,' + pofile = self.csv2po(minicsv) + assert pofile.findunit("Source") is not None + assert pofile.findunit("Source").target == "" + assert len(pofile.units) == 1 + + def test_kdecomment(self): + """checks that we can merge into KDE comment entries""" + csvsource = '''location,source,target +simple.c,Source,Target''' + potsource = r'''#: simple.c +msgid "_: KDE comment\n" +"Source" +msgstr "" +''' + pofile = self.csv2po(csvsource, potsource) + pounit = self.singleelement(pofile) + assert pounit._extract_msgidcomments() == 'KDE comment' + assert pounit.source == "Source" + assert pounit.target == "Target" + +class TestCSV2POCommand(test_convert.TestConvertCommand, TestCSV2PO): + """Tests running actual csv2po commands on files""" + convertmodule = csv2po + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--charset=CHARSET") + options = self.help_check(options, "--columnorder=COLUMNORDER") + options = self.help_check(options, "--duplicates=DUPLICATESTYLE", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_dtd2po.py b/translate-toolkit-1.5.1/translate/convert/test_dtd2po.py new file mode 100644 index 0000000..9e13a14 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_dtd2po.py @@ -0,0 +1,365 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import dtd2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +from translate.storage import dtd + +class TestDTD2PO: + def dtd2po(self, dtdsource, dtdtemplate=None): + """helper that converts dtd source to po source without requiring files""" + inputfile = wStringIO.StringIO(dtdsource) + inputdtd = dtd.dtdfile(inputfile) + convertor = dtd2po.dtd2po() + if dtdtemplate is None: + outputpo = convertor.convertstore(inputdtd) + else: + templatefile = wStringIO.StringIO(dtdtemplate) + templatedtd = dtd.dtdfile(templatefile) + outputpo = convertor.mergestore(templatedtd, inputdtd) + return outputpo + + def convertdtd(self, dtdsource): + """call the convertdtd, return the outputfile""" + inputfile = wStringIO.StringIO(dtdsource) + outputfile = wStringIO.StringIO() + templatefile = None + assert dtd2po.convertdtd(inputfile, outputfile, templatefile) + return outputfile.getvalue() + + def singleelement(self, pofile): + """checks that the pofile contains a single non-header element, and returns it""" + assert len(pofile.units) == 2 + assert pofile.units[0].isheader() + print pofile.units[1] + return pofile.units[1] + + def countelements(self, pofile): + """returns the number of non-header items""" + if pofile.units[0].isheader(): + return len(pofile.units) - 1 + else: + return len(pofile.units) + + def test_simpleentity(self): + """checks that a simple dtd entity definition converts properly to a po entry""" + dtdsource = '<!ENTITY test.me "bananas for sale">\n' + pofile = self.dtd2po(dtdsource) + pounit = self.singleelement(pofile) + assert pounit.source == "bananas for sale" + assert pounit.target == "" + # Now with a template language + dtdtemplate = '<!ENTITY test.me "bananas for sale">\n' + dtdtranslated = '<!ENTITY test.me "piesangs te koop">\n' + pofile = self.dtd2po(dtdtranslated, dtdtemplate) + pounit = self.singleelement(pofile) + assert pounit.source == "bananas for sale" + assert pounit.target == "piesangs te koop" + + def test_convertdtd(self): + """checks that the convertdtd function is working""" + dtdsource = '<!ENTITY saveas.label "Save As...">\n' + posource = self.convertdtd(dtdsource) + pofile = po.pofile(wStringIO.StringIO(posource)) + unit = self.singleelement(pofile) + assert unit.source == "Save As..." + assert unit.target == "" + + def test_apos(self): + """apostrophe should not break a single-quoted entity definition, bug 69""" + dtdsource = "<!ENTITY test.me 'bananas ' for sale'>\n" + pofile = self.dtd2po(dtdsource) + pounit = self.singleelement(pofile) + assert pounit.source == "bananas ' for sale" + + def test_quotes(self): + """quotes should be handled in a single-quoted entity definition""" + dtdsource = """<!ENTITY test.metoo '"Bananas" for sale'>\n""" + pofile = self.dtd2po(dtdsource) + pounit = self.singleelement(pofile) + print str(pounit) + assert pounit.source == '"Bananas" for sale' + + def test_emptyentity(self): + """checks that empty entity definitions survive into po file, bug 15""" + dtdsource = '<!ENTITY credit.translation "">\n' + pofile = self.dtd2po(dtdsource) + pounit = self.singleelement(pofile) + assert "credit.translation" in str(pounit) + assert 'msgctxt "credit.translation"' in str(pounit) + + def test_emptyentity_translated(self): + """checks that if we translate an empty entity it makes it into the PO, bug 101""" + dtdtemplate = '<!ENTITY credit.translation "">\n' + dtdsource = '<!ENTITY credit.translation "Translators Names">\n' + pofile = self.dtd2po(dtdsource, dtdtemplate) + unit = self.singleelement(pofile) + print unit + assert "credit.translation" in str(unit) + # We don't want this to simply be seen as a header: + assert len(unit.getid()) != 0 + assert unit.target == "Translators Names" + + def test_localisaton_note_simple(self): + """test the simple localisation more becomes a #. comment""" + dtdsource = '''<!-- LOCALIZATION NOTE (alwaysCheckDefault.height): + There's some sort of bug which makes wrapping checkboxes not properly reflow, + causing the bottom border of the groupbox to be cut off; set this + appropriately if your localization causes this checkbox to wrap. +--> +<!ENTITY alwaysCheckDefault.height "3em"> +''' + pofile = self.dtd2po(dtdsource) + posource = str(pofile) + print posource + assert posource.count('#.') == 5 # 1 Header extracted from, 3 comment lines, 1 autoinserted comment + + def test_localisation_note_merge(self): + """test that LOCALIZATION NOTES are added properly as #. comments and disambiguated with msgctxt entries""" + dtdtemplate = '<!--LOCALIZATION NOTE (%s): Some note -->\n' + \ + '<!ENTITY %s "Source text">\n' + dtdsource = dtdtemplate % ("note1.label", "note1.label") + dtdtemplate % ("note2.label", "note2.label") + pofile = self.dtd2po(dtdsource) + posource = str(pofile.units[1]) + str(pofile.units[2]) + print posource + assert posource.count('#.') == 2 + assert posource.count('msgctxt') == 2 + + def test_donttranslate_simple(self): + """check that we handle DONT_TRANSLATE messages properly""" + dtdsource = '''<!-- LOCALIZATION NOTE (region.Altitude): DONT_TRANSLATE --> +<!ENTITY region.Altitude "Very High">''' + pofile = self.dtd2po(dtdsource) + assert self.countelements(pofile) == 0 + dtdsource = '''<!-- LOCALIZATION NOTE (exampleOpenTag.label): DONT_TRANSLATE: they are text for HTML tagnames: "<i>" and "</i>" --> +<!ENTITY exampleOpenTag.label "<i>">''' + pofile = self.dtd2po(dtdsource) + assert self.countelements(pofile) == 0 + dtdsource = '''<!-- LOCALIZATION NOTE (imapAdvanced.label): Do not translate "IMAP" --> +<!ENTITY imapAdvanced.label "Advanced IMAP Server Settings">''' + pofile = self.dtd2po(dtdsource) + assert self.countelements(pofile) == 1 + + def test_donttranslate_label(self): + """test strangeness when label entity is marked DONT_TRANSLATE and accesskey is not, bug 30""" + dtdsource = '<!--LOCALIZATION NOTE (editorCheck.label): DONT_TRANSLATE -->\n' + \ + '<!ENTITY editorCheck.label "Composer">\n<!ENTITY editorCheck.accesskey "c">\n' + pofile = self.dtd2po(dtdsource) + posource = str(pofile) + # we need to decided what we're going to do here - see the comments in bug 30 + # this tests the current implementation which is that the DONT_TRANSLATE string is removed, but the other remains + assert 'editorCheck.label' not in posource + assert 'editorCheck.accesskey' in posource + + def test_donttranslate_onlyentity(self): + """if the entity is itself just another entity then it shouldn't appear in the output PO file""" + dtdsource = '''<!-- LOCALIZATION NOTE (mainWindow.title): DONT_TRANSLATE --> +<!ENTITY mainWindow.title "&brandFullName;">''' + pofile = self.dtd2po(dtdsource) + assert self.countelements(pofile) == 0 + + def test_donttranslate_commentedout(self): + """check that we don't process messages in <!-- comments -->: bug 102""" + dtdsource = '''<!-- commenting out until bug 38906 is fixed +<!ENTITY messagesHeader.label "Messages"> -->''' + pofile = self.dtd2po(dtdsource) + assert self.countelements(pofile) == 0 + + def test_spaces_at_start_of_dtd_lines(self): + """test that pretty print spaces at the start of subsequent DTD element lines are removed from the PO file, bug 79""" + # Space at the end of the line + dtdsource = '<!ENTITY noupdatesfound.intro "First line then \n' + \ + ' next lines.">\n' + pofile = self.dtd2po(dtdsource) + pounit = self.singleelement(pofile) + # We still need to decide how we handle line line breaks in the DTD entities. It seems that we should actually + # drop the line break but this has not been implemented yet. + assert pounit.source == "First line then \nnext lines." + # No space at the end of the line + dtdsource = '<!ENTITY noupdatesfound.intro "First line then\n' + \ + ' next lines.">\n' + pofile = self.dtd2po(dtdsource) + pounit = self.singleelement(pofile) + assert pounit.source == "First line then \nnext lines." + + def test_accesskeys_folding(self): + """test that we fold accesskeys into message strings""" + dtdsource_template = '<!ENTITY fileSaveAs.%s "Save As...">\n<!ENTITY fileSaveAs.%s "S">\n' + lang_template = '<!ENTITY fileSaveAs.%s "Gcina ka...">\n<!ENTITY fileSaveAs.%s "G">\n' + for label in ("label", "title"): + for accesskey in ("accesskey", "accessKey", "akey"): + pofile = self.dtd2po(dtdsource_template % (label, accesskey)) + pounit = self.singleelement(pofile) + assert pounit.source == "&Save As..." + # Test with template (bug 155) + pofile = self.dtd2po(lang_template % (label, accesskey), dtdsource_template % (label, accesskey)) + pounit = self.singleelement(pofile) + assert pounit.source == "&Save As..." + assert pounit.target == "&Gcina ka..." + + def test_accesskeys_mismatch(self): + """check that we can handle accesskeys that don't match and thus can't be folded into the .label entry""" + dtdsource = '<!ENTITY fileSave.label "Save">\n' + \ + '<!ENTITY fileSave.accesskey "z">\n' + pofile = self.dtd2po(dtdsource) + assert self.countelements(pofile) == 2 + + def test_carriage_return_in_multiline_dtd(self): + """test that we create nice PO files when we find a \r\n in a multiline DTD element""" + dtdsource = '<!ENTITY noupdatesfound.intro "First line then \r\n' + \ + ' next lines.">\n' + pofile = self.dtd2po(dtdsource) + unit = self.singleelement(pofile) + assert unit.source == "First line then \nnext lines." + + def test_multiline_with_blankline(self): + """test that we can process a multiline entity that has a blank line in it, bug 331""" + dtdsource = ''' +<!ENTITY multiline.text " +Some text + +Some other text +">''' + pofile = self.dtd2po(dtdsource) + unit = self.singleelement(pofile) + assert unit.source == "Some text \n \nSome other text" + + def test_mulitline_closing_quotes(self): + """test that we support various styles and spaces after closing quotes on multiline entities""" + dtdsource = ''' +<!ENTITY pref.plural '<span>opsies</span><span + class="noWin">preferences</span>' > +''' + pofile = self.dtd2po(dtdsource) + unit = self.singleelement(pofile) + assert unit.source == '<span>opsies</span><span \nclass="noWin">preferences</span>' + + def test_preserving_spaces(self): + """test that we preserve space that appear at the start of the first line of a DTD entity""" + # Space before first character + dtdsource = '<!ENTITY mainWindow.titlemodifiermenuseparator " - ">' + pofile = self.dtd2po(dtdsource) + unit = self.singleelement(pofile) + assert unit.source == " - " + # Double line and spaces + dtdsource = '<!ENTITY mainWindow.titlemodifiermenuseparator " - with a newline\n and more text">' + pofile = self.dtd2po(dtdsource) + unit = self.singleelement(pofile) + print repr(unit.source) + assert unit.source == " - with a newline \nand more text" + + def test_escaping_newline_tabs(self): + """test that we handle all kinds of newline permutations""" + dtdsource = '<!ENTITY noupdatesfound.intro "A hard coded newline.\\nAnd tab\\t and a \\r carriage return.">\n' + converter = dtd2po.dtd2po() + thedtd = dtd.dtdunit() + thedtd.parse(dtdsource) + thepo = po.pounit() + converter.convertstrings(thedtd, thepo) + print thedtd + print thepo.source + # \n in a dtd should also appear as \n in the PO file + assert thepo.source == r"A hard coded newline.\nAnd tab\t and a \r carriage return." + + def test_abandoned_accelerator(self): + """test that when a language DTD has an accelerator but the template DTD does not that we abandon the accelerator""" + dtdtemplate = '<!ENTITY test.label "Test">\n' + dtdlanguage = '<!ENTITY test.label "Toets">\n<!ENTITY test.accesskey "T">\n' + pofile = self.dtd2po(dtdlanguage, dtdtemplate) + unit = self.singleelement(pofile) + assert unit.source == "Test" + assert unit.target == "Toets" + + def test_unassociable_accelerator(self): + """test to see that we can handle accelerator keys that cannot be associated correctly""" + dtdsource = '<!ENTITY managecerts.button "Manage Certificates...">\n<!ENTITY managecerts.accesskey "M">' + pofile = self.dtd2po(dtdsource) + assert pofile.units[1].source == "Manage Certificates..." + assert pofile.units[2].source == "M" + pofile = self.dtd2po(dtdsource, dtdsource) + assert pofile.units[1].target == "Manage Certificates..." + assert pofile.units[2].target == "M" + + def test_changed_labels_and_accelerators(self): + """test to ensure that when the template changes an entity name we can still manage the accelerators""" + dtdtemplate = '''<!ENTITY managecerts.caption "Manage Certificates"> +<!ENTITY managecerts.text "Use the Certificate Manager to manage your personal certificates, as well as those of other people and certificate authorities."> +<!ENTITY managecerts.button "Manage Certificates..."> +<!ENTITY managecerts.accesskey "M">''' + dtdlanguage = '''<!ENTITY managecerts.label "ﺇﺩﺍﺭﺓ ﺎﻠﺸﻫﺍﺩﺎﺗ"> +<!ENTITY managecerts.text "ﺎﺴﺘﺧﺪﻣ ﻡﺪﻳﺭ ﺎﻠﺸﻫﺍﺩﺎﺗ ﻹﺩﺍﺭﺓ ﺶﻫﺍﺩﺎﺘﻛ ﺎﻠﺸﺨﺼﻳﺓ، ﺏﺍﻺﺿﺎﻓﺓ ﻞﺘﻠﻛ ﺎﻠﺧﺎﺻﺓ ﺏﺍﻶﺧﺮﻴﻧ ﻭ ﺲﻠﻃﺎﺗ ﺎﻠﺸﻫﺍﺩﺎﺗ."> +<!ENTITY managecerts.button "ﺇﺩﺍﺭﺓ ﺎﻠﺸﻫﺍﺩﺎﺗ..."> +<!ENTITY managecerts.accesskey "ﺩ">''' + pofile = self.dtd2po(dtdlanguage, dtdtemplate) + print pofile + assert pofile.units[3].source == "Manage Certificates..." + assert pofile.units[3].target == u"ﺇﺩﺍﺭﺓ ﺎﻠﺸﻫﺍﺩﺎﺗ..." + assert pofile.units[4].source == "M" + assert pofile.units[4].target == u"ﺩ" + + def wtest_accelerator_keys_not_in_sentence(self): + """tests to ensure that we can manage accelerator keys that are not part of the transated sentence eg in Chinese""" + dtdtemplate = '''<!ENTITY useAutoScroll.label "Use autoscrolling"> +<!ENTITY useAutoScroll.accesskey "a">''' + dtdlanguage = '''<!ENTITY useAutoScroll.label "使用自動捲動(Autoscrolling)"> +<!ENTITY useAutoScroll.accesskey "a">''' + pofile = self.dtd2po(dtdlanguage, dtdtemplate) + print pofile + assert pofile.units[1].target == "使用自動捲動(&Autoscrolling)" + # We assume that accesskeys with no associated key should be done as follows "XXXX (&A)" + # TODO - check that we can unfold this from PO -> DTD + dtdlanguage = '''<!ENTITY useAutoScroll.label "使用自動捲動"> +<!ENTITY useAutoScroll.accesskey "a">''' + pofile = self.dtd2po(dtdlanguage, dtdtemplate) + print pofile + assert pofile.units[1].target == "使用自動捲動 (&A)" + + def test_exclude_entity_includes(self): + """test that we don't turn an include into a translatable string""" + dtdsource = '<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">' + pofile = self.dtd2po(dtdsource) + assert self.countelements(pofile) == 0 + + def test_linewraps(self): + """check that redundant line wraps are removed from the po file""" + dtdsource = '''<!ENTITY generic.longDesc " +<p>Test me.</p> +">''' + pofile = self.dtd2po(dtdsource) + pounit = self.singleelement(pofile) + assert pounit.source == "<p>Test me.</p>" + + def test_merging_with_new_untranslated(self): + """test that when we merge in new untranslated strings with existing translations we manage the encodings properly""" + # This should probably be in test_po.py but was easier to do here + dtdtemplate = '''<!ENTITY unreadFolders.label "Unread">\n<!ENTITY viewPickerUnread.label "Unread">\n<!ENTITY unreadColumn.label "Unread">''' + dtdlanguage = '''<!ENTITY viewPickerUnread.label "Непрочетени">\n<!ENTITY unreadFolders.label "Непрочетени">''' + pofile = self.dtd2po(dtdlanguage, dtdtemplate) + print pofile + assert pofile.units[1].source == "Unread" + + def test_merge_without_template(self): + """test that we we manage the case where we merge and their is no template file""" + # If we supply a template file we should fail if the template file does not exist or is blank. We should + # not put the translation in as the source. + # TODO: this test fails, since line 16 checks for "not dtdtemplate" + # instead of checking for "dtdtemplate is None". What is correct? + dtdtemplate = '' + dtdsource = '<!ENTITY no.template "Target">' + pofile = self.dtd2po(dtdsource, dtdtemplate) + print pofile + assert self.countelements(pofile) == 0 + +class TestDTD2POCommand(test_convert.TestConvertCommand, TestDTD2PO): + """Tests running actual dtd2po commands on files""" + convertmodule = dtd2po + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--duplicates=DUPLICATESTYLE", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_html2po.py b/translate-toolkit-1.5.1/translate/convert/test_html2po.py new file mode 100644 index 0000000..a868e63 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_html2po.py @@ -0,0 +1,430 @@ +#!/usr/bin/env python + +from translate.convert import html2po +from translate.convert import po2html +from translate.convert import test_convert +from translate.misc import wStringIO + +class TestHTML2PO: + def html2po(self, markup, includeuntagged=False, duplicatestyle="msgctxt", keepcomments=False): + """Helper to convert html to po without a file.""" + inputfile = wStringIO.StringIO(markup) + convertor = html2po.html2po() + outputpo = convertor.convertfile(inputfile, "test", False, includeuntagged, duplicatestyle, keepcomments) + return outputpo + + def po2html(self, posource, htmltemplate): + """Helper to convert po to html without a file.""" + inputfile = wStringIO.StringIO(posource) + outputfile = wStringIO.StringIO() + templatefile = wStringIO.StringIO(htmltemplate) + assert po2html.converthtml(inputfile, outputfile, templatefile) + return outputfile.getvalue() + + def countunits(self, pofile, expected): + """helper to check that we got the expected number of messages""" + actual = len(pofile.units) + if actual > 0: + if pofile.units[0].isheader(): + actual = actual - 1 + print pofile + assert actual == expected + + def compareunit(self, pofile, unitnumber, expected): + """helper to validate a PO message""" + if not pofile.units[0].isheader(): + unitnumber = unitnumber - 1 + print 'unit source: ' + pofile.units[unitnumber].source.encode('utf-8') + '|' + print 'expected: ' + expected.encode('utf-8') + '|' + assert unicode(pofile.units[unitnumber].source) == unicode(expected) + + def check_single(self, markup, itemtext): + """checks that converting this markup produces a single element with value itemtext""" + pofile = self.html2po(markup) + self.countunits(pofile, 1) + self.compareunit(pofile, 1, itemtext) + + def check_null(self, markup): + """checks that converting this markup produces no elements""" + pofile = self.html2po(markup) + self.countunits(pofile, 0) + + def check_phpsnippet(self, php): + """Given a snippet of php, put it into an HTML shell and see + if the results are as expected""" + self.check_single('<html><head></head><body><p><a href="'+php+'/site.html">Body text</a></p></body></html>', "Body text") + self.check_single('<html><head></head><body><p>More things in <a href="'+php+'/site.html">Body text</a></p></body></html>', 'More things in <a href="'+php+'/site.html">Body text</a>') + self.check_null('<html><head></head><body><p>'+php+'</p></body></html>') + + def test_htmllang(self): + """test to ensure that we no longer use the lang attribure""" + markup = '''<html lang="en"><head><title>My title</title></head><body></body></html>''' + pofile = self.html2po(markup) + self.countunits(pofile, 1) + # Check that the first item is the <title> not <head> + self.compareunit(pofile, 1, "My title") + + def test_title(self): + """test that we can extract the <title> tag""" + self.check_single("<html><head><title>My title</title></head><body></body></html>", "My title") + + def test_title_with_linebreak(self): + """Test a linebreak in the <title> tag""" + htmltext = '''<html> +<head> + <title>My +title</title> +</head> +<body> +</body> +</html> +''' + self.check_single(htmltext, "My title") + + def test_meta(self): + """Test that we can extract certain <meta> info from <head>.""" + self.check_single('''<html><head><meta name="keywords" content="these are keywords"></head><body></body></html>''', "these are keywords") + + def test_tag_p(self): + """test that we can extract the <p> tag""" + self.check_single("<html><head></head><body><p>A paragraph.</p></body></html>", "A paragraph.") + markup = "<p>First line.<br>Second line.</p>" + pofile = self.html2po(markup) + self.compareunit(pofile, 1, "First line.<br>Second line.") + + def test_tag_p_with_linebreak(self): + """Test newlines within the <p> tag.""" + htmltext = '''<html> +<head> +</head> +<body> +<p> +A paragraph is a section in a piece of writing, usually highlighting a +particular point or topic. It always begins on a new line and usually +with indentation, and it consists of at least one sentence. +</p> +</body> +</html> +''' + self.check_single(htmltext, "A paragraph is a section in a piece of writing, usually highlighting a particular point or topic. It always begins on a new line and usually with indentation, and it consists of at least one sentence.") + markup = "<p>First\nline.<br>Second\nline.</p>" + pofile = self.html2po(markup) + self.compareunit(pofile, 1, "First line.<br>Second line.") + + def test_tag_div(self): + """test that we can extract the <div> tag""" + self.check_single("<html><head></head><body><div>A paragraph.</div></body></html>", "A paragraph.") + markup = "<div>First line.<br>Second line.</div>" + pofile = self.html2po(markup) + self.compareunit(pofile, 1, "First line.<br>Second line.") + + def test_tag_div_with_linebreaks(self): + """Test linebreaks within a <div> tag.""" + htmltext = '''<html> +<head> +</head> +<body> +<div> +A paragraph is a section in a piece of writing, usually highlighting a +particular point or topic. It always begins on a new line and usually +with indentation, and it consists of at least one sentence. +</div> +</body> +</html> +''' + self.check_single(htmltext, "A paragraph is a section in a piece of writing, usually highlighting a particular point or topic. It always begins on a new line and usually with indentation, and it consists of at least one sentence.") + markup = "<div>First\nline.<br>Second\nline.</div>" + pofile = self.html2po(markup) + self.compareunit(pofile, 1, "First line.<br>Second line.") + + def test_tag_a(self): + """test that we can extract the <a> tag""" + self.check_single('<html><head></head><body><p>A paragraph with <a href="http://translate.org.za/">hyperlink</a>.</p></body></html>', 'A paragraph with <a href="http://translate.org.za/">hyperlink</a>.') + + def test_tag_a_with_linebreak(self): + """Test that we can extract the <a> tag with newlines in it.""" + htmltext = '''<html> +<head> +</head> +<body> +<p>A +paragraph +with <a +href="http://translate.org.za/">hyperlink</a> +and +newlines.</p></body></html> +''' + self.check_single(htmltext, 'A paragraph with <a href="http://translate.org.za/">hyperlink</a> and newlines.') + + def test_tag_img(self): + """Test that we can extract the alt attribute from the <img> tag.""" + self.check_single('''<html><head></head><body><img src="picture.png" alt="A picture"></body></html>''', "A picture") + + def test_img_empty(self): + """Test that we can extract the alt attribute from the <img> tag.""" + htmlsource = '''<html><head></head><body><img src="images/topbar.jpg" width="750" height="80"></body></html>''' + self.check_null(htmlsource) + + def test_tag_table_summary(self): + """Test that we can extract the summary attribute.""" + self.check_single('''<html><head></head><body><table summary="Table summary"></table></body></html>''', "Table summary") + + def test_table_simple(self): + """Test that we can fully extract a simple table.""" + markup = '''<html><head></head><body><table><tr><th>Heading One</th><th>Heading Two</th><tr><td>One</td><td>Two</td></tr></table></body></html>''' + pofile = self.html2po(markup) + self.countunits(pofile, 4) + self.compareunit(pofile, 1, "Heading One") + self.compareunit(pofile, 2, "Heading Two") + self.compareunit(pofile, 3, "One") + self.compareunit(pofile, 4, "Two") + + def test_table_complex(self): + markup = '''<table summary="This is the summary"><caption>A caption</caption><thead><tr><th abbr="Head 1">Heading One</th><th>Heading Two</th></thead><tfoot><tr><td>Foot One</td><td>Foot Two</td></tr></tfoot><tbody><tr><td>One</td><td>Two</td></tr></tbody></table>''' + pofile = self.html2po(markup) + self.countunits(pofile, 9) + self.compareunit(pofile, 1, "This is the summary") + self.compareunit(pofile, 2, "A caption") + self.compareunit(pofile, 3, "Head 1") + self.compareunit(pofile, 4, "Heading One") + self.compareunit(pofile, 5, "Heading Two") + self.compareunit(pofile, 6, "Foot One") + self.compareunit(pofile, 7, "Foot Two") + self.compareunit(pofile, 8, "One") + self.compareunit(pofile, 9, "Two") + + def test_table_empty(self): + """Test that we ignore tables that are empty. + + A table is deemed empty if it has no translatable content. + """ + + self.check_null('''<html><head></head><body><table><tr><td><img src="bob.png"></td></tr></table></body></html>''') + self.check_null('''<html><head></head><body><table><tr><td> </td></tr></table></body></html>''') + self.check_null('''<html><head></head><body><table><tr><td><strong></strong></td></tr></table></body></html>''') + + def test_address(self): + """Test to see if the address element is extracted""" + self.check_single("<body><address>My address</address></body>", "My address") + + def test_headings(self): + """Test to see if the h* elements are extracted""" + markup = "<html><head></head><body><h1>Heading One</h1><h2>Heading Two</h2><h3>Heading Three</h3><h4>Heading Four</h4><h5>Heading Five</h5><h6>Heading Six</h6></body></html>" + pofile = self.html2po(markup) + self.countunits(pofile, 6) + self.compareunit(pofile, 1, "Heading One") + self.compareunit(pofile, 2, "Heading Two") + self.compareunit(pofile, 3, "Heading Three") + self.compareunit(pofile, 4, "Heading Four") + self.compareunit(pofile, 5, "Heading Five") + self.compareunit(pofile, 6, "Heading Six") + + def test_headings_with_linebreaks(self): + """Test to see if h* elements with newlines can be extracted""" + markup = "<html><head></head><body><h1>Heading\nOne</h1><h2>Heading\nTwo</h2><h3>Heading\nThree</h3><h4>Heading\nFour</h4><h5>Heading\nFive</h5><h6>Heading\nSix</h6></body></html>" + pofile = self.html2po(markup) + self.countunits(pofile, 6) + self.compareunit(pofile, 1, "Heading One") + self.compareunit(pofile, 2, "Heading Two") + self.compareunit(pofile, 3, "Heading Three") + self.compareunit(pofile, 4, "Heading Four") + self.compareunit(pofile, 5, "Heading Five") + self.compareunit(pofile, 6, "Heading Six") + + def test_dt(self): + """Test to see if the definition list title (dt) element is extracted""" + self.check_single("<html><head></head><body><dl><dt>Definition List Item Title</dt></dl></body></html>", "Definition List Item Title") + + def test_dd(self): + """Test to see if the definition list description (dd) element is extracted""" + self.check_single("<html><head></head><body><dl><dd>Definition List Item Description</dd></dl></body></html>", "Definition List Item Description") + + def test_span(self): + """test to check that we don't double extract a span item""" + self.check_single("<html><head></head><body><p>You are a <span>Spanish</span> sentence.</p></body></html>", "You are a <span>Spanish</span> sentence.") + + def test_ul(self): + """Test to see if the list item <li> is exracted""" + markup = "<html><head></head><body><ul><li>Unordered One</li><li>Unordered Two</li></ul><ol><li>Ordered One</li><li>Ordered Two</li></ol></body></html>" + pofile = self.html2po(markup) + self.countunits(pofile, 4) + self.compareunit(pofile, 1, "Unordered One") + self.compareunit(pofile, 2, "Unordered Two") + self.compareunit(pofile, 3, "Ordered One") + self.compareunit(pofile, 4, "Ordered Two") + + def test_duplicates(self): + """check that we use the default style of msgctxt to disambiguate duplicate messages""" + markup = "<html><head></head><body><p>Duplicate</p><p>Duplicate</p></body></html>" + pofile = self.html2po(markup) + self.countunits(pofile, 2) + # FIXME change this so that we check that the msgctxt is correctly added + self.compareunit(pofile, 1, "Duplicate") + self.compareunit(pofile, 2, "Duplicate") + + def wtest_multiline_reflow(self): + """check that we reflow multiline content to make it more readable for translators""" + self.check_single('''<td valign="middle" width="96%"><font class="headingwhite">South + Africa</font></td>''', '''<font class="headingwhite">South Africa</font>''') + + def wtest_nested_tags(self): + """check that we can extract items within nested tags""" + markup = "<div><p>Extract this</p>And this</div>" + pofile = self.html2po(markup) + self.countunits(pofile, 2) + self.compareunit(pofile, 1, "Extract this") + self.compareunit(pofile, 2, "And this") + + def test_carriage_return(self): + """Remove carriage returns from files in dos format.""" + htmlsource = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\r +<html><!-- InstanceBegin template="/Templates/masterpage.dwt" codeOutsideHTMLIsLocked="false" -->\r +<head>\r +<!-- InstanceBeginEditable name="doctitle" -->\r +<link href="fmfi.css" rel="stylesheet" type="text/css">\r +</head>\r +\r +<body>\r +<p>The rapid expansion of telecommunications infrastructure in recent\r +years has helped to bridge the digital divide to a limited extent.</p> \r +</body>\r +<!-- InstanceEnd --></html>\r +''' + + self.check_single(htmlsource, 'The rapid expansion of telecommunications infrastructure in recent years has helped to bridge the digital divide to a limited extent.') + + def test_encoding_latin1(self): + """Convert HTML input in iso-8859-1 correctly to unicode.""" + htmlsource = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><!-- InstanceBegin template="/Templates/masterpage.dwt" codeOutsideHTMLIsLocked="false" --> +<head> +<!-- InstanceBeginEditable name="doctitle" --> +<title>FMFI - South Africa - CSIR Openphone - Overview</title> +<!-- InstanceEndEditable --> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> +<meta name="keywords" content="fmfi, first mile, first inch, wireless, rural development, access devices, mobile devices, wifi, connectivity, rural connectivty, ict, low cost, cheap, digital divide, csir, idrc, community"> + +<!-- InstanceBeginEditable name="head" --> +<!-- InstanceEndEditable --> +<link href="../../../fmfi.css" rel="stylesheet" type="text/css"> +</head> + +<body> +<p>We aim to please \x96 will you aim too, please?</p> +<p>South Africa\x92s language diversity can be challenging.</p> +</body> +</html> +''' + pofile = self.html2po(htmlsource) + + self.countunits(pofile, 4) + self.compareunit(pofile, 3, u'We aim to please \x96 will you aim too, please?') + self.compareunit(pofile, 4, u'South Africa\x92s language diversity can be challenging.') + + def test_strip_html(self): + """Ensure that unnecessary html is stripped from the resulting unit.""" + + htmlsource = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +<title>FMFI - Contact</title> +</head> +<body> +<table width="100%" border="0" cellpadding="0" cellspacing="0"> + <tr align="left" valign="top"> + <td width="150" height="556"> + <table width="157" height="100%" border="0" cellspacing="0" id="leftmenubg-color"> + <tr> + <td align="left" valign="top" height="555"> + <table width="100%" border="0" cellspacing="0" cellpadding="2"> + <tr align="left" valign="top" bgcolor="#660000"> + <td width="4%"><strong></strong></td> + <td width="96%"><strong><font class="headingwhite">Projects</font></strong></td> + </tr> + <tr align="left" valign="top"> + <td valign="middle" width="4%"><img src="images/arrow.gif" width="8" height="8"></td> + <td width="96%"><a href="index.html">Home Page</a></td> + </tr> + </table> + </td> + </tr> + </table></td> +</table> +</body> +</html> +''' + pofile = self.html2po(htmlsource) + self.countunits(pofile, 3) + self.compareunit(pofile, 2, u'Projects') + self.compareunit(pofile, 3, u'Home Page') + + # Translate and convert back: + pofile.units[1].target = 'Projekte' + pofile.units[2].target = 'Tuisblad' + htmlresult = self.po2html(str(pofile), htmlsource).replace('\n', ' ').replace('= "', '="').replace('> <', '><') + snippet = '<td width="96%"><strong><font class="headingwhite">Projekte</font></strong></td>' + assert snippet in htmlresult + snippet = '<td width="96%"><a href="index.html">Tuisblad</a></td>' + assert snippet in htmlresult + + def test_php(self): + """Test that PHP snippets don't interfere""" + + # A simple string + self.check_phpsnippet('''<?=$phpvariable?>''') + + # Contains HTML tag charcters (< and >) + self.check_phpsnippet('''<?=($a < $b ? $foo : ($b > c ? $bar : $cat))?>''') + + # Make sure basically any symbol can be handled + self.check_phpsnippet(''' <? asdfghjkl qwertyuiop 1234567890!@#$%^&*()-=_+[]\{}|;':",./<>? ?> ''') + + def test_multiple_php(self): + """Test multiple PHP snippets in a string to make sure they get restored properly""" + php1 = '''<?=$phpvariable?>''' + php2 = '''<?=($a < $b ? $foo : ($b > c ? $bar : $cat))?>''' + php3 = '''<? asdfghjklqwertyuiop1234567890!@#$%^&*()-=_+[]\{}|;':",./<>? ?>''' + + # Put 3 different strings into an html string + innertext = '<a href="'+php1+'/site.html">Body text</a> and some '+php2+' more text '+php2+php3 + htmlsource = '<html><head></head><body><p>'+innertext+'</p></body></html>' + self.check_single(htmlsource, innertext) + + def test_php_multiline(self): + + # A multi-line php string to test + php1 = '''<? abc +def +ghi ?>''' + + # Scatter the php strings throughout the file, and show what the translation should be + innertext = '<a href="'+php1+'/site.html">Body text</a> and some '+php1+' more text '+php1+php1 + innertrans = '<a href="'+php1+'/site.html">Texte de corps</a> et encore de '+php1+' plus de texte '+php1+php1 + + htmlsource = '<html><head></head><body><p>'+innertext+'</p></body></html>' # Current html file + transsource = '<html><head></head><body><p>'+innertrans+'</p></body></html>' # Expected translation + + pofile = self.html2po(htmlsource) + pofile.units[0].target = innertrans # Register the translation in the PO file + htmlresult = self.po2html(pofile, htmlsource) + assert htmlresult == transsource + + def test_comments(self): + """Test that HTML comments are converted to translator notes in output""" + pofile = self.html2po('<!-- comment outside block --><p><!-- a comment -->A paragraph<!-- with another comment -->.</p>', keepcomments=True) + self.compareunit(pofile, 1, 'A paragraph.') + notes = pofile.getunits()[0].getnotes() + assert unicode(notes) == ' a comment \n with another comment ' + +class TestHTML2POCommand(test_convert.TestConvertCommand, TestHTML2PO): + """Tests running actual html2po commands on files""" + convertmodule = html2po + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "--duplicates=DUPLICATESTYLE") + options = self.help_check(options, "--keepcomments") + options = self.help_check(options, "-u, --untagged", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_moz2po.py b/translate-toolkit-1.5.1/translate/convert/test_moz2po.py new file mode 100644 index 0000000..059ae30 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_moz2po.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +from translate.convert import moz2po +from translate.convert import test_convert + +class TestMoz2PO: + pass + +class TestMoz2POCommand(test_convert.TestConvertCommand, TestMoz2PO): + """Tests running actual moz2po commands on files""" + convertmodule = moz2po + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "--duplicates=DUPLICATESTYLE") + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_mozfunny2prop.py b/translate-toolkit-1.5.1/translate/convert/test_mozfunny2prop.py new file mode 100644 index 0000000..8184456 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_mozfunny2prop.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import mozfunny2prop +from translate.misc import wStringIO +from translate.storage import po + +class TestInc2PO: + def inc2po(self, incsource, inctemplate=None): + """helper that converts .inc source to po source without requiring files""" + inputfile = wStringIO.StringIO(incsource) + if inctemplate: + templatefile = wStringIO.StringIO(inctemplate) + else: + templatefile = None + outputfile = wStringIO.StringIO() + result = mozfunny2prop.inc2po(inputfile, outputfile, templatefile) + outputpo = outputfile.getvalue() + outputpofile = po.pofile(wStringIO.StringIO(outputpo)) + return outputpofile + + def singleelement(self, pofile): + """checks that the pofile contains a single non-header element, and returns it""" + assert len(pofile.units) == 2 + assert pofile.units[0].isheader() + print pofile + return pofile.units[1] + + def countelements(self, pofile): + """counts the number of non-header entries""" + assert pofile.units[0].isheader() + print pofile + return len(pofile.units) - 1 + + def test_simpleentry(self): + """checks that a simple inc entry converts properly to a po entry""" + incsource = '#define MOZ_LANGPACK_CREATOR mozilla.org\n' + pofile = self.inc2po(incsource) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ["MOZ_LANGPACK_CREATOR"] + assert pounit.source == "mozilla.org" + assert pounit.target == "" + + def test_uncomment_contributors(self): + """checks that the contributors entry is automatically uncommented""" + incsource = '''# If non-English locales wish to credit multiple contributors, uncomment this +# variable definition and use the format specified. +# #define MOZ_LANGPACK_CONTRIBUTORS <em:contributor>Joe Solon</em:contributor> <em:contributor>Suzy Solon</em:contributor>''' + pofile = self.inc2po(incsource) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ["MOZ_LANGPACK_CONTRIBUTORS"] + assert "Joe Solon" in pounit.source + diff --git a/translate-toolkit-1.5.1/translate/convert/test_oo2po.py b/translate-toolkit-1.5.1/translate/convert/test_oo2po.py new file mode 100644 index 0000000..94f884d --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_oo2po.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import oo2po +from translate.convert import po2oo +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +from translate.storage.poheader import poheader +from translate.storage import oo +import os + +class TestOO2PO: + target_filetype = po.pofile + conversion_module = oo2po + conversion_class = oo2po.oo2po + + def convert(self, oosource, sourcelanguage='en-US', targetlanguage='af-ZA'): + """helper that converts oo source to po source without requiring files""" + inputoo = oo.oofile(oosource) + convertor = self.conversion_class(sourcelanguage, targetlanguage) + outputpo = convertor.convertstore(inputoo) + return outputpo + + def singleelement(self, pofile): + """checks that the pofile contains a single non-header element, and returns it""" + if isinstance(pofile, poheader): + assert len(pofile.units) == 2 + assert pofile.units[0].isheader() + return pofile.units[1] + else: + assert len(pofile.units) == 1 + return pofile.units[0] + + def roundtripstring(self, filename, entitystring): + """Convert the supplied string as part of an OpenOffice.org GSI file to po and back. + + Return the string once it has been through all the conversions.""" + + ootemplate = r'helpcontent2 %s 0 help par_id3150670 35 0 en-US %s 2002-02-02 02:02:02' + + oosource = ootemplate % (filename, entitystring) + ooinputfile = wStringIO.StringIO(oosource) + ootemplatefile = wStringIO.StringIO(oosource) + pooutputfile = wStringIO.StringIO() + + self.conversion_module.convertoo(ooinputfile, pooutputfile, ootemplatefile, targetlanguage='en-US') + posource = pooutputfile.getvalue() + + poinputfile = wStringIO.StringIO(posource) + ootemplatefile = wStringIO.StringIO(oosource) + oooutputfile = wStringIO.StringIO() + po2oo.convertoo(poinputfile, oooutputfile, ootemplatefile, targetlanguage="en-US") + ooresult = oooutputfile.getvalue() + print "original oo:\n", oosource, "po version:\n", posource, "output oo:\n", ooresult + return ooresult.split('\t')[10] + + def check_roundtrip(self, filename, text): + """Checks that the text converted to po and back is the same as the original.""" + assert self.roundtripstring(filename, text) == text + + def test_simpleentity(self): + """checks that a simple oo entry converts properly to a po entry""" + oosource = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + pofile = self.convert(oosource) + pounit = self.singleelement(pofile) + assert pounit.source == "Character" + assert pounit.target == "" + + def test_escapes(self): + """checks that a simple oo entry converts escapes properly to a po entry""" + oosource = r"wizards source\formwizard\dbwizres.src 0 string RID_DB_FORM_WIZARD_START + 19 0 en-US Newline \n Newline Tab \t Tab CR \r CR 20050924 09:13:58" + pofile = self.convert(oosource) + pounit = self.singleelement(pofile) + poelementsrc = str(pounit) + print poelementsrc + assert "Newline \n Newline" in pounit.source + assert "Tab \t Tab" in pounit.source + assert "CR \r CR" in pounit.source + + def test_roundtrip_escape(self): + self.check_roundtrip('strings.src', r'The given command is not a SELECT statement.\nOnly queries are allowed.') + self.check_roundtrip('source\ui\dlg\AutoControls_tmpl.hrc', r';\t59\t,\t44\t:\t58\t{Tab}\t9\t{Space}\t32') + self.check_roundtrip('inc_openoffice\windows\msi_languages\Nsis.ulf', r'The installation files must be unpacked and copied to your hard disk in preparation for the installation. After that, the %PRODUCTNAME installation will start automatically.\r\n\r\nClick \'Next\' to continue.') + self.check_roundtrip('file.xhp', r'\<ahelp\>') + self.check_roundtrip('file.xhp', r'\<ahelp prop=\"value\"\>') + self.check_roundtrip('file.xhp', r'\<ahelp prop=\"value\"\>marked up text\</ahelp\>') + self.check_roundtrip('file.xhp', r'\<ahelp prop=\"value>>\"\>') + self.check_roundtrip('file.xhp', r'''\<ahelp prop=\"value>>\"\>'Next'>> or "<<Previous"\</ahelp\>''') + self.check_roundtrip('address_auto.xhp', r'''example, \<item type=\"literal\"\>'Harry\\'s Bar'.\</item\>''') + + def xtest_roundtrip_whitespaceonly(self): + """check items that are only special instances of whitespce""" + # FIXME We can't roundtrip this yet because of some problems in the oo class handling + self.check_roundtrip('choose_chart_type.xhp', r' ') + self.check_roundtrip('choose_chart_type.xhp', '\xc2\xa0') + + def test_double_escapes(self): + oosource = r"helpcontent2 source\text\shared\01\02100001.xhp 0 help par_id3150670 35 0 en-US \\< 2002-02-02 02:02:02" + pofile = self.convert(oosource) + pounit = self.singleelement(pofile) + poelementsrc = str(pounit) + print poelementsrc + assert pounit.source == r"\<" + + def test_escapes_helpcontent2(self): + """checks that a helpcontent2 entry converts escapes properly to a po entry""" + oosource = r"helpcontent2 source\text\smath\guide\parentheses.xhp 0 help par_id3150344 4 0 en-US size *2 \\langle x \\rangle 2002-02-02 02:02:02" + pofile = self.convert(oosource) + pounit = self.singleelement(pofile) + poelementsrc = str(pounit) + print poelementsrc + assert pounit.source == r'size *2 \langle x \rangle' + + def test_msgid_bug_error_address(self): + """tests the we have the correct url for reporting msgid bugs""" + oosource = r"wizards source\formwizard\dbwizres.src 0 string RID_DB_FORM_WIZARD_START + 19 0 en-US Newline \n Newline Tab \t Tab CR \r CR 20050924 09:13:58" + bug_url = '''http://qa.openoffice.org/issues/enter_bug.cgi''' + ('''?subcomponent=ui&comment=&short_desc=Localization issue in file: &component=l10n&form_name=enter_issue''').replace(" ", "%20").replace(":", "%3A") + pofile = self.convert(oosource) + assert pofile.units[0].isheader() + assert pofile.parseheader()["Report-Msgid-Bugs-To"] == bug_url + + def test_x_comment_inclusion(self): + """test that we can merge x-comment language entries into comment sections of the PO file""" + en_USsource = r"wizards source\formwizard\dbwizres.src 0 string RID_DB_FORM_WIZARD_START + 19 0 en-US Text Quickhelp Title 20050924 09:13:58" + xcommentsource = r"wizards source\formwizard\dbwizres.src 0 string RID_DB_FORM_WIZARD_START + 19 0 x-comment %s %s %s 20050924 09:13:58" + # Real comment + comment = "Comment" + commentsource = en_USsource + '\n' + xcommentsource % (comment, comment, comment) + pofile = self.convert(commentsource) + if isinstance(pofile, poheader): + units = pofile.units[1:] + else: + units = pofile.units + textunit = units[0] + assert textunit.source == "Text" + assert comment in textunit.getnotes("developer") + quickhelpunit = units[1] + assert quickhelpunit.source == "Quickhelp" + assert comment in quickhelpunit.getnotes("developer") + titleunit = units[2] + assert titleunit.source == "Title" + assert comment in titleunit.getnotes("developer") + # Whitespace and blank + for comment in (" ", ""): + commentsource = en_USsource + '\n' + xcommentsource % (comment, comment, comment) + pofile = self.convert(commentsource) + if isinstance(pofile, poheader): + units = pofile.units[1:] + else: + units = pofile.units + textunit = units[0] + assert textunit.source == "Text" + assert textunit.getnotes("developer") == "" + quickhelpunit = units[1] + assert quickhelpunit.source == "Quickhelp" + assert quickhelpunit.getnotes("developer") == "" + titleunit = units[2] + assert titleunit.source == "Title" + assert titleunit.getnotes("developer") == "" + +class TestOO2POCommand(test_convert.TestConvertCommand, TestOO2PO): + """Tests running actual oo2po commands on files""" + convertmodule = oo2po + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "--source-language=LANG") + options = self.help_check(options, "--language=LANG") + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "--duplicates=DUPLICATESTYLE") + options = self.help_check(options, "--multifile=MULTIFILESTYLE") + options = self.help_check(options, "--nonrecursiveinput", last=True) + + def test_preserve_filename(self): + """Ensures that the filename is preserved.""" + oosource = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + self.create_testfile("snippet.sdf", oosource) + oofile = oo.oofile(self.open_testfile("snippet.sdf")) + assert oofile.filename.endswith("snippet.sdf") + oofile.parse(oosource) + assert oofile.filename.endswith("snippet.sdf") + + def test_simple_pot(self): + """tests the simplest possible conversion to a pot file""" + oosource = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + self.create_testfile("simple.oo", oosource) + self.run_command("simple.oo", "simple.pot", pot=True, nonrecursiveinput=True) + pofile = self.target_filetype(self.open_testfile("simple.pot")) + poelement = self.singleelement(pofile) + assert poelement.source == "Character" + assert poelement.target == "" + + def test_simple_po(self): + """tests the simplest possible conversion to a po file""" + oosource1 = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + oosource2 = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 ku Karakter 20050924 09:13:58' + self.create_testfile("simple.oo", oosource1 + "\n" + oosource2) + self.run_command("simple.oo", "simple.po", lang="ku", nonrecursiveinput=True) + pofile = self.target_filetype(self.open_testfile("simple.po")) + poelement = self.singleelement(pofile) + assert poelement.source == "Character" + assert poelement.target == "Karakter" + + def test_onefile_nonrecursive(self): + """tests the --multifile=onefile option and make sure it doesn't produce a directory""" + oosource = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + self.create_testfile("simple.oo", oosource) + self.run_command("simple.oo", "simple.pot", pot=True, multifile="onefile") + assert os.path.isfile(self.get_testfilename("simple.pot")) + + def test_remove_duplicates(self): + """test that removing of duplicates works correctly (bug 171)""" + oosource = r''' +sd source\ui\animations\SlideTransitionPane.src 0 checkbox DLG_SLIDE_TRANSITION_PANE CB_AUTO_PREVIEW HID_SD_SLIDETRANSITIONPANE_CB_AUTO_PREVIEW 1 en-US Automatic preview 20060725 03:26:42 +sd source\ui\animations\AnimationSchemesPane.src 0 checkbox DLG_ANIMATION_SCHEMES_PANE CB_AUTO_PREVIEW HID_SD_ANIMATIONSCHEMESPANE_CB_AUTO_PREVIEW 1 en-US Automatic preview 20060725 03:26:42 +sd source\ui\animations\CustomAnimationCreateDialog.src 0 checkbox RID_TP_CUSTOMANIMATION_ENTRANCE CBX_PREVIEW 143 en-US Automatic preview 20060725 03:26:42 +sd source\ui\animations\CustomAnimationCreateDialog.src 0 checkbox RID_TP_CUSTOMANIMATION_ENTRANCE CBX_PREVIEW 143 fr Aperçu automatique 20060725 03:26:42 +sd source\ui\animations\CustomAnimationSchemesPane.src 0 checkbox DLG_CUSTOMANIMATION_SCHEMES_PANE 4 0 en-US Automatic preview 20060725 03:26:42 +sd source\ui\animations\CustomAnimationSchemesPane.src 0 checkbox DLG_CUSTOMANIMATION_SCHEMES_PANE 4 0 fr Aperçu automatique 20060725 03:26:42 +''' + self.create_testfile("simple.oo", oosource) + self.run_command("simple.oo", "simple.po", language="fr", multifile="onefile", error="traceback", duplicates="merge") + pofile = self.target_filetype(self.open_testfile("simple.po")) + assert len(pofile.units) == 2 + assert pofile.units[1].target == u"Aperçu automatique" + diff --git a/translate-toolkit-1.5.1/translate/convert/test_oo2xliff.py b/translate-toolkit-1.5.1/translate/convert/test_oo2xliff.py new file mode 100644 index 0000000..4e9f2c4 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_oo2xliff.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import test_oo2po +from translate.convert import oo2xliff +from translate.convert import xliff2oo +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import xliff +from translate.storage import oo +import os + +class TestOO2XLIFF(test_oo2po.TestOO2PO): + target_filetype = xliff.xlifffile + conversion_module = oo2xliff + conversion_class = oo2xliff.oo2xliff + + def test_msgid_bug_error_address(self): + pass + +class TestOO2POCommand(test_convert.TestConvertCommand, TestOO2XLIFF): + """Tests running actual oo2xliff commands on files""" + convertmodule = oo2xliff + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "--source-language=LANG") + options = self.help_check(options, "--language=LANG") + options = self.help_check(options, "--duplicates=DUPLICATESTYLE") + options = self.help_check(options, "--multifile=MULTIFILESTYLE") + options = self.help_check(options, "--nonrecursiveinput", last=True) + + def test_preserve_filename(self): + """Ensures that the filename is preserved.""" + oosource = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + self.create_testfile("snippet.sdf", oosource) + oofile = oo.oofile(self.open_testfile("snippet.sdf")) + assert oofile.filename.endswith("snippet.sdf") + oofile.parse(oosource) + assert oofile.filename.endswith("snippet.sdf") + + def test_simple_xlf(self): + """tests the simplest possible conversion to a xlf file""" + oosource = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + self.create_testfile("simple.oo", oosource) + self.run_command("simple.oo", "simple.xlf", lang="ku", nonrecursiveinput=True) + pofile = self.target_filetype(self.open_testfile("simple.xlf")) + poelement = self.singleelement(pofile) + assert poelement.source == "Character" + assert poelement.target == "" + + def test_simple_po(self): + """tests the simplest possible conversion to a po file""" + oosource1 = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + oosource2 = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 ku Karakter 20050924 09:13:58' + self.create_testfile("simple.oo", oosource1 + "\n" + oosource2) + self.run_command("simple.oo", "simple.po", lang="ku", nonrecursiveinput=True) + pofile = self.target_filetype(self.open_testfile("simple.po")) + poelement = self.singleelement(pofile) + assert poelement.source == "Character" + assert poelement.target == "Karakter" + + def test_onefile_nonrecursive(self): + """tests the --multifile=onefile option and make sure it doesn't produce a directory""" + oosource = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Character 20050924 09:13:58' + self.create_testfile("simple.oo", oosource) + self.run_command("simple.oo", "simple.xlf", lang="ku", multifile="onefile") + assert os.path.isfile(self.get_testfilename("simple.xlf")) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_php2po.py b/translate-toolkit-1.5.1/translate/convert/test_php2po.py new file mode 100644 index 0000000..0cb93b4 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_php2po.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import php2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +from translate.storage import php + +class TestPhp2PO: + def php2po(self, phpsource, phptemplate=None): + """helper that converts .phperties source to po source without requiring files""" + inputfile = wStringIO.StringIO(phpsource) + inputphp = php.phpfile(inputfile) + convertor = php2po.php2po() + if phptemplate: + templatefile = wStringIO.StringIO(phptemplate) + templatephp = php.phpfile(templatefile) + outputpo = convertor.mergestore(templatephp, inputphp) + else: + outputpo = convertor.convertstore(inputphp) + return outputpo + + def convertphp(self, phpsource): + """call the convertphp, return the outputfile""" + inputfile = wStringIO.StringIO(phpsource) + outputfile = wStringIO.StringIO() + templatefile = None + assert php2po.convertphp(inputfile, outputfile, templatefile) + return outputfile.getvalue() + + def singleelement(self, pofile): + """checks that the pofile contains a single non-header element, and returns it""" + assert len(pofile.units) == 2 + assert pofile.units[0].isheader() + print pofile + return pofile.units[1] + + def countelements(self, pofile): + """counts the number of non-header entries""" + assert pofile.units[0].isheader() + print pofile + return len(pofile.units) - 1 + + def test_simpleentry(self): + """checks that a simple php entry converts properly to a po entry""" + phpsource = """$_LANG['simple'] = 'entry';""" + pofile = self.php2po(phpsource) + pounit = self.singleelement(pofile) + assert pounit.source == "entry" + assert pounit.target == "" + + def test_convertphp(self): + """checks that the convertphp function is working""" + phpsource = """$_LANG['simple'] = 'entry';""" + posource = self.convertphp(phpsource) + pofile = po.pofile(wStringIO.StringIO(posource)) + pounit = self.singleelement(pofile) + assert pounit.source == "entry" + assert pounit.target == "" + + def test_unicode(self): + """checks that unicode entries convert properly""" + unistring = u'Norsk bokm\u00E5l' + phpsource = """$lang['nb'] = '%s';""" % unistring + pofile = self.php2po(phpsource) + pounit = self.singleelement(pofile) + print repr(pofile.units[0].target) + print repr(pounit.source) + assert pounit.source == u'Norsk bokm\u00E5l' + + def test_multiline(self): + """checks that multiline enties can be parsed""" + phpsource = r"""$lang['5093'] = 'Unable to connect to your IMAP server. You may have exceeded the maximum number +of connections to this server. If so, use the Advanced IMAP Server Settings dialog to +reduce the number of cached connections.';""" + pofile = self.php2po(phpsource) + print repr(pofile.units[1].target) + assert self.countelements(pofile) == 1 + + def test_comments_before(self): + """test to ensure that we take comments from .php and place them in .po""" + phpsource = '''/* Comment */ +$lang['prefPanel-smime'] = 'Security';''' + pofile = self.php2po(phpsource) + pounit = self.singleelement(pofile) + assert pounit.getnotes("developer") == "/* Comment" + # TODO write test for inline comments and check for // comments that precede an entry + + def test_emptyentry(self): + """checks that empty definitions survives into po file""" + phpsource = '''/* comment */\n$lang['credit'] = '';''' + pofile = self.php2po(phpsource) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ["$lang['credit']"] + assert pounit.getcontext() == "$lang['credit']" + assert "#. /* comment" in str(pofile) + assert pounit.source == "" + + def test_emptyentry_translated(self): + """checks that if we translate an empty definition it makes it into the PO""" + phptemplate = '''$lang['credit'] = '';''' + phpsource = '''$lang['credit'] = 'Translators Names';''' + pofile = self.php2po(phpsource, phptemplate) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ["$lang['credit']"] + assert pounit.source == "" + assert pounit.target == "Translators Names" + + def test_newlines_in_value(self): + """check that we can carry newlines that appear in the entry value into the PO""" + # Single quotes - \n is not a newline + phpsource = r'''$lang['name'] = 'value1\nvalue2';''' + pofile = self.php2po(phpsource) + unit = self.singleelement(pofile) + assert unit.source == r"value1\nvalue2" + # Double quotes - \n is a newline + phpsource = r'''$lang['name'] = "value1\nvalue2";''' + pofile = self.php2po(phpsource) + unit = self.singleelement(pofile) + assert unit.source == "value1\nvalue2" + + def test_spaces_in_name(self): + """checks that if we have spaces in the name we create a good PO with no spaces""" + phptemplate = '''$lang[ 'credit' ] = 'Something';''' + phpsource = '''$lang[ 'credit' ] = ''n Ding';''' + pofile = self.php2po(phpsource, phptemplate) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ["$lang['credit']"] + +class TestPhp2POCommand(test_convert.TestConvertCommand, TestPhp2PO): + """Tests running actual php2po commands on files""" + convertmodule = php2po + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--duplicates=DUPLICATESTYLE", last=True) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2csv.py b/translate-toolkit-1.5.1/translate/convert/test_po2csv.py new file mode 100644 index 0000000..eca2896 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2csv.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python + +from translate.convert import po2csv +from translate.convert import csv2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +from translate.storage import csvl10n + +class TestPO2CSV: + def po2csv(self, posource): + """helper that converts po source to csv source without requiring files""" + inputfile = wStringIO.StringIO(posource) + inputpo = po.pofile(inputfile) + convertor = po2csv.po2csv() + outputcsv = convertor.convertstore(inputpo) + return outputcsv + + def csv2po(self, csvsource, template=None): + """helper that converts csv source to po source without requiring files""" + inputfile = wStringIO.StringIO(csvsource) + inputcsv = csvl10n.csvfile(inputfile) + if template: + templatefile = wStringIO.StringIO(template) + inputpot = po.pofile(templatefile) + else: + inputpot = None + convertor = csv2po.csv2po(templatepo=inputpot) + outputpo = convertor.convertstore(inputcsv) + return outputpo + + def singleelement(self, storage): + """checks that the pofile contains a single non-header element, and returns it""" + assert len(storage.units) == 1 + return storage.units[0] + + def test_simpleentity(self): + """checks that a simple csv entry definition converts properly to a po entry""" + minipo = r'''#: term.cpp +msgid "Term" +msgstr "asdf"''' + csvfile = self.po2csv(minipo) + unit = self.singleelement(csvfile) + assert unit.comment == "term.cpp" + assert unit.source == "Term" + assert unit.target == "asdf" + + def test_multiline(self): + """tests multiline po entries""" + minipo = r'''msgid "First part " +"and extra" +msgstr "Eerste deel " +"en ekstra"''' + csvfile = self.po2csv(minipo) + unit = self.singleelement(csvfile) + assert unit.source == "First part and extra" + assert unit.target == "Eerste deel en ekstra" + + def test_escapednewlines(self): + """Test the escaping of newlines""" + minipo = r'''msgid "First line\nSecond line" +msgstr "Eerste lyn\nTweede lyn" +''' + csvfile = self.po2csv(minipo) + unit = self.singleelement(csvfile) + assert unit.source == "First line\nSecond line" + assert unit.target == "Eerste lyn\nTweede lyn" + pofile = self.csv2po(str(csvfile)) + unit = self.singleelement(pofile) + assert unit.source == "First line\nSecond line" + assert unit.target == "Eerste lyn\nTweede lyn" + + def test_escapedtabs(self): + """Test the escaping of tabs""" + minipo = r'''msgid "First column\tSecond column" +msgstr "Eerste kolom\tTweede kolom" +''' + csvfile = self.po2csv(minipo) + unit = self.singleelement(csvfile) + assert unit.source == "First column\tSecond column" + assert unit.target == "Eerste kolom\tTweede kolom" + assert csvfile.findunit("First column\tSecond column").target == "Eerste kolom\tTweede kolom" + + def test_escapedquotes(self): + """Test the escaping of quotes (and slash)""" + minipo = r'''msgid "Hello \"Everyone\"" +msgstr "Good day \"All\"" + +msgid "Use \\\"." +msgstr "Gebruik \\\"." +''' + csvfile = self.po2csv(minipo) + assert csvfile.findunit('Hello "Everyone"').target == 'Good day "All"' + assert csvfile.findunit('Use \\".').target == 'Gebruik \\".' + + def test_escapedescape(self): + """Test the escaping of pure escapes is unaffected""" + minipo = r'''msgid "Find\\Options" +msgstr "Vind\\Opsies" +''' + csvfile = self.po2csv(minipo) + print minipo + print csvfile + assert csvfile.findunit(r'Find\Options').target == r'Vind\Opsies' + + def test_singlequotes(self): + """Tests that single quotes are preserved correctly""" + minipo = '''msgid "source 'source'"\nmsgstr "target 'target'"\n''' + csvfile = self.po2csv(minipo) + print str(csvfile) + assert csvfile.findunit("source 'source'").target == "target 'target'" + # Make sure we don't mess with start quotes until writing + minipo = '''msgid "'source'"\nmsgstr "'target'"\n''' + csvfile = self.po2csv(minipo) + print str(csvfile) + assert csvfile.findunit(r"'source'").target == r"'target'" + # TODO check that we escape on writing not in the internal representation + + def test_empties(self): + """Tests that things keep working with empty entries""" + minipo = 'msgid "Source"\nmsgstr ""\n\nmsgid ""\nmsgstr ""' + csvfile = self.po2csv(minipo) + assert csvfile.findunit("Source") is not None + assert csvfile.findunit("Source").target == "" + assert len(csvfile.units) == 1 + + def test_kdecomments(self): + """test that we don't carry KDE comments to CSV""" + minipo = '#: simple.c\nmsgid "_: KDE comment\\n"\n"Same"\nmsgstr "Same"\n' + csvfile = self.po2csv(minipo) + unit = self.singleelement(csvfile) + assert unit.source == "Same" + assert unit.target == "Same" + +class TestPO2CSVCommand(test_convert.TestConvertCommand, TestPO2CSV): + """Tests running actual po2csv commands on files""" + convertmodule = po2csv + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "--columnorder=COLUMNORDER", last=True) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2dtd.py b/translate-toolkit-1.5.1/translate/convert/test_po2dtd.py new file mode 100644 index 0000000..2ce297c --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2dtd.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import po2dtd +from translate.convert import dtd2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +from translate.storage import dtd +from py import test +import warnings + +class TestPO2DTD: + def setup_method(self, method): + warnings.resetwarnings() + + def teardown_method(self, method): + warnings.resetwarnings() + + def po2dtd(self, posource): + """helper that converts po source to dtd source without requiring files""" + inputfile = wStringIO.StringIO(posource) + inputpo = po.pofile(inputfile) + convertor = po2dtd.po2dtd() + outputdtd = convertor.convertstore(inputpo) + return outputdtd + + def merge2dtd(self, dtdsource, posource): + """helper that merges po translations to dtd source without requiring files""" + inputfile = wStringIO.StringIO(posource) + inputpo = po.pofile(inputfile) + templatefile = wStringIO.StringIO(dtdsource) + templatedtd = dtd.dtdfile(templatefile) + convertor = po2dtd.redtd(templatedtd) + outputdtd = convertor.convertstore(inputpo) + return outputdtd + + def convertdtd(self, posource, dtdtemplate): + """helper to exercise the command line function""" + inputfile = wStringIO.StringIO(posource) + outputfile = wStringIO.StringIO() + templatefile = wStringIO.StringIO(dtdtemplate) + assert po2dtd.convertdtd(inputfile, outputfile, templatefile) + return outputfile.getvalue() + + def roundtripsource(self, dtdsource): + """converts dtd source to po and back again, returning the resulting source""" + dtdinputfile = wStringIO.StringIO(dtdsource) + dtdinputfile2 = wStringIO.StringIO(dtdsource) + pooutputfile = wStringIO.StringIO() + dtd2po.convertdtd(dtdinputfile, pooutputfile, dtdinputfile2) + posource = pooutputfile.getvalue() + poinputfile = wStringIO.StringIO(posource) + dtdtemplatefile = wStringIO.StringIO(dtdsource) + dtdoutputfile = wStringIO.StringIO() + po2dtd.convertdtd(poinputfile, dtdoutputfile, dtdtemplatefile) + dtdresult = dtdoutputfile.getvalue() + print "original dtd:\n", dtdsource, "po version:\n", posource, "output dtd:\n", dtdresult + return dtdresult + + def roundtripstring(self, entitystring): + """Just takes the contents of a ENTITY definition (with quotes) and does a roundtrip on that""" + dtdintro, dtdoutro = '<!ENTITY Test.RoundTrip ', '>\n' + dtdsource = dtdintro + entitystring + dtdoutro + dtdresult = self.roundtripsource(dtdsource) + assert dtdresult.startswith(dtdintro) and dtdresult.endswith(dtdoutro) + return dtdresult[len(dtdintro):-len(dtdoutro)] + + def check_roundtrip(self, dtdsource): + """Checks that the round-tripped string is the same as the original""" + assert self.roundtripstring(dtdsource) == dtdsource + + def test_joinlines(self): + """tests that po lines are joined seamlessly (bug 16)""" + multilinepo = '''#: pref.menuPath\nmsgid ""\n"<span>Tools > Options</"\n"span>"\nmsgstr ""\n''' + dtdfile = self.po2dtd(multilinepo) + dtdsource = str(dtdfile) + assert "</span>" in dtdsource + + def test_escapedstr(self): + """tests that \n in msgstr is escaped correctly in dtd""" + multilinepo = '''#: pref.menuPath\nmsgid "Hello\\nEveryone"\nmsgstr "Good day\\nAll"\n''' + dtdfile = self.po2dtd(multilinepo) + dtdsource = str(dtdfile) + assert "Good day\nAll" in dtdsource + + def test_missingaccesskey(self): + """tests that proper warnings are given if access key is missing""" + simplepo = '''#: simple.label\n#: simple.accesskey\nmsgid "Simple &String"\nmsgstr "Dimpled Ring"\n''' + simpledtd = '''<!ENTITY simple.label "Simple String">\n<!ENTITY simple.accesskey "S">''' + warnings.simplefilter("error") + assert test.raises(Warning, self.merge2dtd, simpledtd, simplepo) + + def test_accesskeycase(self): + """tests that access keys come out with the same case as the original, regardless""" + simplepo_template = '''#: simple.label\n#: simple.accesskey\nmsgid "%s"\nmsgstr "%s"\n''' + simpledtd_template = '''<!ENTITY simple.label "Simple %s">\n<!ENTITY simple.accesskey "%s">''' + possibilities = [ + #(en label, en akey, en po, af po, af label, expected af akey) + ("Sis", "S", "&Sis", "&Sies", "Sies", "S"), + ("Sis", "s", "Si&s", "&Sies", "Sies", "S"), + ("Sis", "S", "&Sis", "Sie&s", "Sies", "s"), + ("Sis", "s", "Si&s", "Sie&s", "Sies", "s"), + # untranslated strings should have the casing of the source + ("Sis", "S", "&Sis", "", "Sis", "S"), + ("Sis", "s", "Si&s", "", "Sis", "s"), + ("Suck", "S", "&Suck", "", "Suck", "S"), + ("Suck", "s", "&Suck", "", "Suck", "s"), + ] + for (en_label, en_akey, po_source, po_target, target_label, target_akey) in possibilities: + simplepo = simplepo_template % (po_source, po_target) + simpledtd = simpledtd_template % (en_label, en_akey) + dtdfile = self.merge2dtd(simpledtd, simplepo) + dtdfile.makeindex() + accel = dtd.unquotefromdtd(dtdfile.index["simple.accesskey"].definition) + assert accel == target_akey + + def test_accesskey_types(self): + """tests that we can detect the various styles of accesskey""" + simplepo_template = '''#: simple.%s\n#: simple.%s\nmsgid "&File"\nmsgstr "F&aele"\n''' + simpledtd_template = '''<!ENTITY simple.%s "File">\n<!ENTITY simple.%s "a">''' + for label in ("label", "title"): + for accesskey in ("accesskey", "accessKey", "akey"): + simplepo = simplepo_template % (label, accesskey) + simpledtd = simpledtd_template % (label, accesskey) + dtdfile = self.merge2dtd(simpledtd, simplepo) + dtdfile.makeindex() + assert dtd.unquotefromdtd(dtdfile.index["simple.%s" % accesskey].definition) == "a" + + def test_ampersandfix(self): + """tests that invalid ampersands are fixed in the dtd""" + simplestring = '''#: simple.string\nmsgid "Simple String"\nmsgstr "Dimpled &Ring"\n''' + dtdfile = self.po2dtd(simplestring) + dtdsource = str(dtdfile) + assert "Dimpled Ring" in dtdsource + + po_snippet = r'''#: searchIntegration.label +#: searchIntegration.accesskey +msgid "Allow &searchIntegration.engineName; to &search messages" +msgstr "&searchIntegration.engineName; &ileti aramasına izin ver" +''' + dtd_snippet = r'''<!ENTITY searchIntegration.accesskey "s"> +<!ENTITY searchIntegration.label "Allow &searchIntegration.engineName; to search messages">''' + dtdfile = self.merge2dtd(dtd_snippet, po_snippet) + dtdsource = str(dtdfile) + print dtdsource + assert '"&searchIntegration.engineName; ileti aramasına izin ver"' in dtdsource + + def test_entities_two(self): + """test the error ouput when we find two entities""" + simplestring = '''#: simple.string second.string\nmsgid "Simple String"\nmsgstr "Dimpled Ring"\n''' + dtdfile = self.po2dtd(simplestring) + dtdsource = str(dtdfile) + assert "CONVERSION NOTE - multiple entities" in dtdsource + + def test_entities(self): + """tests that entities are correctly idnetified in the dtd""" + simplestring = '''#: simple.string\nmsgid "Simple String"\nmsgstr "Dimpled Ring"\n''' + dtdfile = self.po2dtd(simplestring) + dtdsource = str(dtdfile) + assert dtdsource.startswith("<!ENTITY simple.string") + + def test_comments_translator(self): + """tests for translator comments""" + simplestring = '''# Comment1\n# Comment2\n#: simple.string\nmsgid "Simple String"\nmsgstr "Dimpled Ring"\n''' + dtdfile = self.po2dtd(simplestring) + dtdsource = str(dtdfile) + assert dtdsource.startswith("<!-- Comment1 -->") + + def test_retains_hashprefix(self): + """tests that hash prefixes in the dtd are retained""" + hashpo = '''#: lang.version\nmsgid "__MOZILLA_LOCALE_VERSION__"\nmsgstr "__MOZILLA_LOCALE_VERSION__"\n''' + hashdtd = '#expand <!ENTITY lang.version "__MOZILLA_LOCALE_VERSION__">\n' + dtdfile = self.merge2dtd(hashdtd, hashpo) + regendtd = str(dtdfile) + assert regendtd == hashdtd + + def test_convertdtd(self): + """checks that the convertdtd function is working""" + posource = '''#: simple.label\n#: simple.accesskey\nmsgid "Simple &String"\nmsgstr "Dimpled &Ring"\n''' + dtdtemplate = '''<!ENTITY simple.label "Simple String">\n<!ENTITY simple.accesskey "S">\n''' + dtdexpected = '''<!ENTITY simple.label "Dimpled Ring">\n<!ENTITY simple.accesskey "R">\n''' + newdtd = self.convertdtd(posource, dtdtemplate) + print newdtd + assert newdtd == dtdexpected + + def test_newlines_escapes(self): + """check that we can handle a \n in the PO file""" + posource = '''#: simple.label\n#: simple.accesskey\nmsgid "A hard coded newline.\\n"\nmsgstr "Hart gekoeerde nuwe lyne\\n"\n''' + dtdtemplate = '<!ENTITY simple.label "A hard coded newline.\n">\n' + dtdexpected = '''<!ENTITY simple.label "Hart gekoeerde nuwe lyne\n">\n''' + dtdfile = self.merge2dtd(dtdtemplate, posource) + print dtdfile + assert str(dtdfile) == dtdexpected + + def test_roundtrip_simple(self): + """checks that simple strings make it through a dtd->po->dtd roundtrip""" + self.check_roundtrip('"Hello"') + self.check_roundtrip('"Hello Everybody"') + + def test_roundtrip_escape(self): + """checks that escapes in strings make it through a dtd->po->dtd roundtrip""" + self.check_roundtrip(r'"Simple Escape \ \n \\ \: \t \r "') + self.check_roundtrip(r'"End Line Escape \"') + + def test_roundtrip_quotes(self): + """checks that (escaped) quotes in strings make it through a dtd->po->dtd roundtrip""" + self.check_roundtrip(r"""'Quote Escape "" '""") + self.check_roundtrip(r'''"Single-Quote ' "''') + self.check_roundtrip(r'''"Single-Quote Escape \' "''') + # NOTE: if both quote marks are present, than ' is converted to ' + self.check_roundtrip(r"""'Both Quotes "" '' '""") + + def test_merging_entries_with_spaces_removed(self): + """dtd2po removes pretty printed spaces, this tests that we can merge this back into the pretty printed dtd""" + posource = '''#: simple.label\nmsgid "First line then "\n"next lines."\nmsgstr "Eerste lyne en dan volgende lyne."\n''' + dtdtemplate = '<!ENTITY simple.label "First line then\n' + \ + ' next lines.">\n' + dtdexpected = '<!ENTITY simple.label "Eerste lyne en dan volgende lyne.">\n' + dtdfile = self.merge2dtd(dtdtemplate, posource) + print dtdfile + assert str(dtdfile) == dtdexpected + + def test_comments(self): + """test that we preserve comments, bug 351""" + posource = '''#: name\nmsgid "Text"\nmsgstr "Teks"''' + dtdtemplate = '''<!ENTITY name "%s">\n<!-- \n\nexample -->\n''' + dtdfile = self.merge2dtd(dtdtemplate % "Text", posource) + print dtdfile + assert str(dtdfile) == dtdtemplate % "Teks" + + def test_duplicates(self): + """test that we convert duplicates back correctly to their respective entries.""" + posource = r'''#: bookmarksMenu.label bookmarksMenu.accesskey +msgctxt "bookmarksMenu.label bookmarksMenu.accesskey" +msgid "&Bookmarks" +msgstr "Dipu&kutshwayo1" + +#: bookmarksItem.title +msgctxt "bookmarksItem.title +msgid "Bookmarks" +msgstr "Dipukutshwayo2" + +#: bookmarksButton.label +msgctxt "bookmarksButton.label" +msgid "Bookmarks" +msgstr "Dipukutshwayo3" +''' + dtdtemplate = r'''<!ENTITY bookmarksMenu.label "Bookmarks"> +<!ENTITY bookmarksMenu.accesskey "B"> +<!ENTITY bookmarksItem.title "Bookmarks"> +<!ENTITY bookmarksButton.label "Bookmarks"> +''' + dtdexpected = r'''<!ENTITY bookmarksMenu.label "Dipukutshwayo1"> +<!ENTITY bookmarksMenu.accesskey "k"> +<!ENTITY bookmarksItem.title "Dipukutshwayo2"> +<!ENTITY bookmarksButton.label "Dipukutshwayo3"> +''' + dtdfile = self.merge2dtd(dtdtemplate, posource) + print dtdfile + assert str(dtdfile) == dtdexpected + + +class TestPO2DTDCommand(test_convert.TestConvertCommand, TestPO2DTD): + """Tests running actual po2dtd commands on files""" + convertmodule = po2dtd + defaultoptions = {"progress": "none"} + # TODO: because of having 2 base classes, we need to call all their setup and teardown methods + # (otherwise we won't reset the warnings etc) + def setup_method(self, method): + """call both base classes setup_methods""" + test_convert.TestConvertCommand.setup_method(self, method) + TestPO2DTD.setup_method(self, method) + def teardown_method(self, method): + """call both base classes teardown_methods""" + test_convert.TestConvertCommand.teardown_method(self, method) + TestPO2DTD.teardown_method(self, method) + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--fuzzy") + options = self.help_check(options, "--nofuzzy", last=True) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2html.py b/translate-toolkit-1.5.1/translate/convert/test_po2html.py new file mode 100644 index 0000000..c7024c7 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2html.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +from translate.convert import po2html +from translate.convert import test_convert +from translate.misc import wStringIO + +class TestPO2Html: + def converthtml(self, posource, htmltemplate): + """helper to exercise the command line function""" + inputfile = wStringIO.StringIO(posource) + print inputfile.getvalue() + outputfile = wStringIO.StringIO() + templatefile = wStringIO.StringIO(htmltemplate) + assert po2html.converthtml(inputfile, outputfile, templatefile) + print outputfile.getvalue() + return outputfile.getvalue() + + def test_simple(self): + """simple po to html test""" + htmlsource = '<p>A sentence.</p>' + posource = '''#: html:3\nmsgid "A sentence."\nmsgstr "'n Sin."\n''' + htmlexpected = '''<p>'n Sin.</p>''' + assert htmlexpected in self.converthtml(posource, htmlsource) + + def test_linebreaks(self): + """Test that a po file can be merged into a template with linebreaks in it.""" + htmlsource = '''<html> +<head> +</head> +<body> +<div> +A paragraph is a section in a piece of writing, usually highlighting a +particular point or topic. It always begins on a new line and usually +with indentation, and it consists of at least one sentence. +</div> +</body> +</html> +''' + posource = '''#: None:1 +msgid "" +"A paragraph is a section in a piece of writing, usually highlighting a " +"particular point or topic. It always begins on a new line and usually with " +"indentation, and it consists of at least one sentence." +msgstr "" +"'n Paragraaf is 'n afdeling in 'n geskrewe stuk wat gewoonlik 'n spesifieke " +"punt uitlig. Dit begin altyd op 'n nuwe lyn (gewoonlik met indentasie) en " +"dit bestaan uit ten minste een sin." +''' + htmlexpected = '''<body> +<div> +'n Paragraaf is 'n afdeling in 'n geskrewe stuk wat gewoonlik +'n spesifieke punt uitlig. Dit begin altyd op 'n nuwe lyn +(gewoonlik met indentasie) en dit bestaan uit ten minste een +sin. +</div> +</body>''' + assert htmlexpected.replace("\n", " ") in self.converthtml(posource, htmlsource).replace("\n", " ") + + def xtest_entities(self): + """Tests that entities are handled correctly""" + htmlsource = '<p>5 less than 6</p>' + posource = '#:html:3\nmsgid "5 less than 6"\nmsgstr "5 < 6"\n' + htmlexpected = '<p>5 < 6</p>' + assert htmlexpected in self.converthtml(posource, htmlsource) + + htmlsource = '<p>Fish & chips</p>' + posource = '#: html:3\nmsgid "Fish & chips"\nmsgstr "Vis & skyfies"\n' + htmlexpected = '<p>Vis & skyfies</p>' + assert htmlexpected in self.converthtml(posource, htmlsource) + + def xtest_escapes(self): + """Tests that PO escapes are correctly handled""" + htmlsource = '<div>Row 1<br />Row 2</div>' + posource = '#: html:3\nmsgid "Row 1\\n"\n"Row 2"\nmsgstr "Ry 1\\n"\n"Ry 2"\n' + htmlexpected = '<div>Ry 1<br />Ry 2</div>' + assert htmlexpected in self.converthtml(posource, htmlsource) + + htmlsource = '<p>"leverage"</p>' + posource = '#: html3\nmsgid "\\"leverage\\""\nmsgstr "\\"ek is dom\\""\n' + htmlexpected = '<p>"ek is dom"</p>' + assert htmlexpected in self.converthtml(posource, htmlsource) + + +class TestPO2HtmlCommand(test_convert.TestConvertCommand, TestPO2Html): + """Tests running actual po2oo commands on files""" + convertmodule = po2html + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "-w WRAP, --wrap=WRAP") + options = self.help_check(options, "--fuzzy") + options = self.help_check(options, "--nofuzzy", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2moz.py b/translate-toolkit-1.5.1/translate/convert/test_po2moz.py new file mode 100644 index 0000000..9c022f1 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2moz.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +from translate.convert import po2moz +from translate.convert import test_convert + +class TestPO2Moz: + pass + +class TestPO2MozCommand(test_convert.TestConvertCommand, TestPO2Moz): + """Tests running actual po2moz commands on files""" + convertmodule = po2moz + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "-l LOCALE, --locale=LOCALE") + options = self.help_check(options, "--clonexpi=CLONEXPI") + options = self.help_check(options, "--fuzzy") + options = self.help_check(options, "--nofuzzy", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2oo.py b/translate-toolkit-1.5.1/translate/convert/test_po2oo.py new file mode 100644 index 0000000..15fcb45 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2oo.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python + +from translate.convert import po2oo +from translate.convert import oo2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +import warnings +import os + +class TestPO2OO: + def setup_method(self, method): + warnings.resetwarnings() + + def teardown_method(self, method): + warnings.resetwarnings() + + def convertoo(self, posource, ootemplate, language="en-US"): + """helper to exercise the command line function""" + inputfile = wStringIO.StringIO(posource) + outputfile = wStringIO.StringIO() + templatefile = wStringIO.StringIO(ootemplate) + assert po2oo.convertoo(inputfile, outputfile, templatefile, targetlanguage=language, timestamp=0) + return outputfile.getvalue() + + def roundtripstring(self, entitystring): + oointro, oooutro = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US ', ' 2002-02-02 02:02:02' + '\r\n' + oosource = oointro + entitystring + oooutro + ooinputfile = wStringIO.StringIO(oosource) + ooinputfile2 = wStringIO.StringIO(oosource) + pooutputfile = wStringIO.StringIO() + oo2po.convertoo(ooinputfile, pooutputfile, ooinputfile2, targetlanguage='en-US') + posource = pooutputfile.getvalue() + poinputfile = wStringIO.StringIO(posource) + ootemplatefile = wStringIO.StringIO(oosource) + oooutputfile = wStringIO.StringIO() + po2oo.convertoo(poinputfile, oooutputfile, ootemplatefile, targetlanguage="en-US") + ooresult = oooutputfile.getvalue() + print "original oo:\n", oosource, "po version:\n", posource, "output oo:\n", ooresult + assert ooresult.startswith(oointro) and ooresult.endswith(oooutro) + return ooresult[len(oointro):-len(oooutro)] + + def check_roundtrip(self, oosource): + """Checks that the round-tripped string is the same as the original""" + assert self.roundtripstring(oosource) == oosource + + def test_convertoo(self): + """checks that the convertoo function is working""" + oobase = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 %s %s 20050924 09:13:58' + '\r\n' + posource = '''#: numpages.src#RID_SVXPAGE_NUM_OPTIONS.STR_BULLET.string.text\nmsgid "Simple String"\nmsgstr "Dimpled Ring"\n''' + ootemplate = oobase % ('en-US', 'Simple String') + ooexpected = oobase % ('zu', 'Dimpled Ring') + newoo = self.convertoo(posource, ootemplate, language="zu") + assert newoo == ootemplate + ooexpected + + def test_pofilter(self): + """Tests integration with pofilter""" + #Some bad po with a few errors: + posource = '#: sourcefile.bla#ID_NUMBER.txet.gnirts\nmsgid "<tag cow=\\"3\\">Mistake."\nmsgstr " <etiket koei=\\"3\\">(fout) "' + filter = po2oo.filter + pofile = po.pofile() + pofile.parse(posource) + assert not filter.validelement(pofile.units[0], "dummyname.po", "exclude-all") + + def test_roundtrip_simple(self): + """checks that simple strings make it through a oo->po->oo roundtrip""" + self.check_roundtrip('Hello') + self.check_roundtrip('"Hello"') + self.check_roundtrip('"Hello Everybody"') + + def test_roundtrip_escape(self): + """checks that escapes in strings make it through a oo->po->oo roundtrip""" + self.check_roundtrip(r'"Simple Escape \ \n \\ \: \t \r "') + self.check_roundtrip(r'"More escapes \\n \\t \\r \\: "') + self.check_roundtrip(r'"More escapes \\\n \\\t \\\r \\\: "') + self.check_roundtrip(r'"More escapes \\\\n \\\\t \\\\r \\\\: "') + self.check_roundtrip(r'"End Line Escape \"') + self.check_roundtrip(r'"\\rangle \\langle') + self.check_roundtrip(r'\\\\<') + self.check_roundtrip(r'\\\<') + self.check_roundtrip(r'\\<') + self.check_roundtrip(r'\<') + + def test_roundtrip_quotes(self): + """checks that (escaped) quotes in strings make it through a oo->po->oo roundtrip""" + self.check_roundtrip(r"""'Quote Escape "" '""") + self.check_roundtrip(r'''"Single-Quote ' "''') + self.check_roundtrip(r'''"Single-Quote Escape \' "''') + self.check_roundtrip(r"""'Both Quotes "" '' '""") + + def xtest_roundtrip_spaces(self): + # FIXME: this test fails because the resultant PO file returns as po.isempty since .isblank returns true + # which is caused by isblankmsgtr returning True. Its a complete mess which would mean unravelling lots + # of yuch in pypo. Until we have time to do that unravelling we're diabling this test. You can reenable + # once we've fixed that. + """checks that (escaped) quotes in strings make it through a oo->po->oo roundtrip""" + self.check_roundtrip(" ") + self.check_roundtrip(u"\u00a0") + + def test_default_timestamp(self): + """test to ensure that we revert to the default timestamp""" + oointro, oooutro = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Text ', '\r\n' + posource = '''#: numpages.src#RID_SVXPAGE_NUM_OPTIONS.STR_BULLET.string.text\nmsgid "Text"\nmsgstr "Text"\n''' + inputfile = wStringIO.StringIO(posource) + outputfile = wStringIO.StringIO() + templatefile = wStringIO.StringIO(oointro + '20050924 09:13:58' + oooutro) + assert po2oo.convertoo(inputfile, outputfile, templatefile, targetlanguage="en-US") + assert outputfile.getvalue() == oointro + '2002-02-02 02:02:02' + oooutro + + def test_escape_conversion(self): + """test to ensure that we convert escapes correctly""" + oosource = r'svx source\dialog\numpages.src 0 string RID_SVXPAGE_NUM_OPTIONS STR_BULLET 0 en-US Column1\tColumn2\r\n 2002-02-02 02:02:02' + '\r\n' + posource = '''#: numpages.src#RID_SVXPAGE_NUM_OPTIONS.STR_BULLET.string.text\nmsgid "Column1\\tColumn2\\r\\n"\nmsgstr "Kolom1\\tKolom2\\r\\n"\n''' + inputfile = wStringIO.StringIO(posource) + outputfile = wStringIO.StringIO() + templatefile = wStringIO.StringIO(oosource) + assert po2oo.convertoo(inputfile, outputfile, templatefile, targetlanguage="af-ZA") + assert "\tKolom1\\tKolom2\\r\\n\t" in outputfile.getvalue() + + def test_helpcontent_escapes(self): + """test to ensure that we convert helpcontent escapes correctly""" + # Note how this test specifically uses incorrect spacing in the + # translation. The extra space before 'hid' and an extra space before + # the closing tag should not confuse us. + oosource = r'helpcontent2 source\text\shared\3dsettings_toolbar.xhp 0 help par_idN1056A 0 en-US \<ahelp hid=\".\"\>The 3D-Settings toolbar controls properties of selected 3D objects.\</ahelp\> 2002-02-02 02:02:02' + '\r\n' + posource = r'''#: 3dsettings_toolbar.xhp#par_idN1056A.help.text +msgid "" +"<ahelp hid=\".\">The 3D-Settings toolbar controls properties of selected 3D " +"ob jects.</ahelp>" +msgstr "" +"<ahelp hid=\".\" >Zeee 3DDDD-Settings toolbar controls properties of selected 3D " +"objects.</ahelp>" +''' + inputfile = wStringIO.StringIO(posource) + outputfile = wStringIO.StringIO() + templatefile = wStringIO.StringIO(oosource) + assert po2oo.convertoo(inputfile, outputfile, templatefile, targetlanguage="af-ZA") + assert r"\<ahelp hid=\".\" \>Zeee 3DDDD-Settings toolbar controls properties of selected 3D objects.\</ahelp\>" in outputfile.getvalue() + + def test_helpcontent_escapes2(self): + """test to ensure that we convert helpcontent escapes correctly""" + oosource = r'helpcontent2 source\text\scalc\05\empty_cells.xhp 0 help par_id2629474 0 en-US A1: <empty> 2002-02-02 02:02:02' + '\r\n' + posource = r'''#: empty_cells.xhp#par_id2629474.help.text +msgid "A1: <empty>" +msgstr "Aa1: <empty>" +''' + inputfile = wStringIO.StringIO(posource) + outputfile = wStringIO.StringIO() + templatefile = wStringIO.StringIO(oosource) + assert po2oo.convertoo(inputfile, outputfile, templatefile, targetlanguage="af-ZA") + assert r"Aa1: <empty>" in outputfile.getvalue() + +class TestPO2OOCommand(test_convert.TestConvertCommand, TestPO2OO): + """Tests running actual po2oo commands on files""" + convertmodule = po2oo + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "--source-language=LANG") + options = self.help_check(options, "--language=LANG") + options = self.help_check(options, "-T, --keeptimestamp") + options = self.help_check(options, "--nonrecursiveoutput") + options = self.help_check(options, "--nonrecursivetemplate") + options = self.help_check(options, "--filteraction") + options = self.help_check(options, "--fuzzy") + options = self.help_check(options, "--nofuzzy") + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--multifile=MULTIFILESTYLE", last=True) + + def merge2oo(self, oosource, posource): + """helper that merges po translations to oo source through files""" + outputoo = convertor.convertstore(inputpo) + return outputoo + + def convertoo(self, posource, ootemplate, language="en-US"): + """helper to exercise the command line function""" + self.create_testfile(os.path.join("input", "svx", "source", "dialog.po"), posource) + self.create_testfile("input.oo", ootemplate) + self.run_command("input", "output.oo", template="input.oo", language=language, keeptimestamp=True) + return self.read_testfile("output.oo") + diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2php.py b/translate-toolkit-1.5.1/translate/convert/test_po2php.py new file mode 100644 index 0000000..015549d --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2php.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import po2php +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po + +class TestPO2Php: + def po2php(self, posource): + """helper that converts po source to .php source without requiring files""" + inputfile = wStringIO.StringIO(posource) + inputpo = po.pofile(inputfile) + convertor = po2php.po2php() + outputphp = convertor.convertstore(inputpo) + return outputphp + + def merge2php(self, phpsource, posource): + """helper that merges po translations to .php source without requiring files""" + inputfile = wStringIO.StringIO(posource) + inputpo = po.pofile(inputfile) + templatefile = wStringIO.StringIO(phpsource) + #templatephp = php.phpfile(templatefile) + convertor = po2php.rephp(templatefile) + outputphp = convertor.convertstore(inputpo) + print outputphp + return outputphp + + def test_merging_simple(self): + """check the simplest case of merging a translation""" + posource = '''#: $lang['name']\nmsgid "value"\nmsgstr "waarde"\n''' + phptemplate = '''$lang['name'] = 'value';\n''' + phpexpected = '''$lang['name'] = 'waarde';\n''' + phpfile = self.merge2php(phptemplate, posource) + print phpfile + assert phpfile == [phpexpected] + + def test_space_preservation(self): + """check that we preserve any spacing in php files when merging""" + posource = '''#: $lang['name']\nmsgid "value"\nmsgstr "waarde"\n''' + phptemplate = '''$lang['name'] = 'value';\n''' + phpexpected = '''$lang['name'] = 'waarde';\n''' + phpfile = self.merge2php(phptemplate, posource) + print phpfile + assert phpfile == [phpexpected] + + def test_merging_blank_entries(self): + """check that we can correctly merge entries that are blank in the template""" + posource = r'''#: accesskey-accept +msgid "" +"_: accesskey-accept\n" +"" +msgstr ""''' + phptemplate = '''$lang['accesskey-accept'] = '';\n''' + phpexpected = '''$lang['accesskey-accept'] = '';\n''' + phpfile = self.merge2php(phptemplate, posource) + print phpfile + assert phpfile == [phpexpected] + + def test_merging_fuzzy(self): + """check merging a fuzzy translation""" + posource = '''#: $lang['name']\n#, fuzzy\nmsgid "value"\nmsgstr "waarde"\n''' + phptemplate = '''$lang['name'] = 'value';\n''' + phpexpected = '''$lang['name'] = 'value';\n''' + phpfile = self.merge2php(phptemplate, posource) + print phpfile + assert phpfile == [phpexpected] + + def test_locations_with_spaces(self): + """check that a location with spaces in php but spaces removed in PO is used correctly""" + posource = '''#: $lang['name']\nmsgid "value"\nmsgstr "waarde"\n''' + phptemplate = '''$lang[ 'name' ] = 'value';\n''' + phpexpected = '''$lang[ 'name' ] = 'waarde';\n''' + phpfile = self.merge2php(phptemplate, posource) + print phpfile + assert phpfile == [phpexpected] + + def test_inline_comments(self): + """check that we include inline comments from the template. Bug 590""" + posource = '''#: $lang['name']\nmsgid "value"\nmsgstr "waarde"\n''' + phptemplate = '''$lang[ 'name' ] = 'value'; //inline comment\n''' + phpexpected = '''$lang[ 'name' ] = 'waarde'; //inline comment\n''' + phpfile = self.merge2php(phptemplate, posource) + print phpfile + assert phpfile == [phpexpected] + + def test_named_variables(self): + """check that we convert correctly if using named variables.""" + posource = '''#: $dictYear +msgid "Year" +msgstr "Jaar" +''' + phptemplate = '''$dictYear = 'Year';\n''' + phpexpected = '''$dictYear = 'Jaar';\n''' + phpfile = self.merge2php(phptemplate, posource) + print phpfile + assert phpfile == [phpexpected] + +# def test_merging_propertyless_template(self): +# """check that when merging with a template with no property values that we copy the template""" +# posource = "" +# proptemplate = "# A comment\n" +# propexpected = proptemplate +# propfile = self.merge2prop(proptemplate, posource) +# print propfile +# assert propfile == [propexpected] + +class TestPO2PhpCommand(test_convert.TestConvertCommand, TestPO2Php): + """Tests running actual po2php commands on files""" + convertmodule = po2php + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--fuzzy") + options = self.help_check(options, "--nofuzzy", last=True) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2prop.py b/translate-toolkit-1.5.1/translate/convert/test_po2prop.py new file mode 100644 index 0000000..e590608 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2prop.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import po2prop +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po + +class TestPO2Prop: + def po2prop(self, posource): + """helper that converts po source to .properties source without requiring files""" + inputfile = wStringIO.StringIO(posource) + inputpo = po.pofile(inputfile) + convertor = po2prop.po2prop() + outputprop = convertor.convertstore(inputpo) + return outputprop + + def merge2prop(self, propsource, posource, personality="java"): + """helper that merges po translations to .properties source without requiring files""" + inputfile = wStringIO.StringIO(posource) + inputpo = po.pofile(inputfile) + templatefile = wStringIO.StringIO(propsource) + #templateprop = properties.propfile(templatefile) + convertor = po2prop.reprop(templatefile) + outputprop = convertor.convertstore(inputpo, personality=personality) + print outputprop + return outputprop + + def test_merging_simple(self): + """check the simplest case of merging a translation""" + posource = '''#: prop\nmsgid "value"\nmsgstr "waarde"\n''' + proptemplate = '''prop=value\n''' + propexpected = '''prop=waarde\n''' + propfile = self.merge2prop(proptemplate, posource) + print propfile + assert propfile == [propexpected] + + def test_hard_newlines_preserved(self): + """check that we preserver hard coded newlines at the start and end of sentence""" + posource = '''#: prop\nmsgid "\\nvalue\\n\\n"\nmsgstr "\\nwaarde\\n\\n"\n''' + proptemplate = '''prop=\\nvalue\\n\\n\n''' + propexpected = '''prop=\\nwaarde\\n\\n\n''' + propfile = self.merge2prop(proptemplate, posource) + print propfile + assert propfile == [propexpected] + + def test_space_preservation(self): + """check that we preserve any spacing in properties files when merging""" + posource = '''#: prop\nmsgid "value"\nmsgstr "waarde"\n''' + proptemplate = '''prop = value\n''' + propexpected = '''prop = waarde\n''' + propfile = self.merge2prop(proptemplate, posource) + print propfile + assert propfile == [propexpected] + + def test_merging_blank_entries(self): + """check that we can correctly merge entries that are blank in the template""" + posource = r'''#: accesskey-accept +msgid "" +"_: accesskey-accept\n" +"" +msgstr ""''' + proptemplate = 'accesskey-accept=\n' + propexpected = 'accesskey-accept=\n' + propfile = self.merge2prop(proptemplate, posource) + print propfile + assert propfile == [propexpected] + + def test_merging_fuzzy(self): + """check merging a fuzzy translation""" + posource = '''#: prop\n#, fuzzy\nmsgid "value"\nmsgstr "waarde"\n''' + proptemplate = '''prop=value\n''' + propexpected = '''prop=value\n''' + propfile = self.merge2prop(proptemplate, posource) + print propfile + assert propfile == [propexpected] + + def test_merging_propertyless_template(self): + """check that when merging with a template with no property values that we copy the template""" + posource = "" + proptemplate = "# A comment\n" + propexpected = proptemplate + propfile = self.merge2prop(proptemplate, posource) + print propfile + assert propfile == [propexpected] + + def test_personalities(self): + """test that we output correctly for Java and Mozilla style property files. Mozilla uses Unicode, while Java uses escaped Unicode""" + posource = '''#: prop\nmsgid "value"\nmsgstr "ṽḁḽṻḝ"\n''' + proptemplate = '''prop = value\n''' + propexpectedjava = '''prop = \\u1E7D\\u1E01\\u1E3D\\u1E7B\\u1E1D\n''' + propfile = self.merge2prop(proptemplate, posource) + print propfile + assert propfile == [propexpectedjava] + propexpectedmozilla = '''prop = ṽḁḽṻḝ\n''' + propfile = self.merge2prop(proptemplate, posource, personality="mozilla") + print propfile + assert propfile == [propexpectedmozilla] + +class TestPO2PropCommand(test_convert.TestConvertCommand, TestPO2Prop): + """Tests running actual po2prop commands on files""" + convertmodule = po2prop + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--fuzzy") + options = self.help_check(options, "--personality=TYPE") + options = self.help_check(options, "--nofuzzy", last=True) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2tiki.py b/translate-toolkit-1.5.1/translate/convert/test_po2tiki.py new file mode 100644 index 0000000..6041393 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2tiki.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# po2tiki unit tests +# Author: Wil Clouser <wclouser@mozilla.com> +# Date: 2008-12-01 + +from translate.convert import po2tiki +from translate.storage import tiki +from translate.convert import test_convert +from translate.misc import wStringIO + +class TestPo2Tiki: + def test_convertpo(self): + inputfile = """ +#: translated +msgid "zero_source" +msgstr "zero_target" + +#: unused +msgid "one_source" +msgstr "one_target" + """ + outputfile = wStringIO.StringIO() + po2tiki.convertpo(inputfile, outputfile) + + output = outputfile.getvalue() + + assert '"one_source" => "one_target",' in output + assert '"zero_source" => "zero_target",' in output + + +class TestPo2TikiCommand(test_convert.TestConvertCommand, TestPo2Tiki): + """Tests running actual po2tiki commands on files""" + convertmodule = po2tiki + defaultoptions = {} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2tmx.py b/translate-toolkit-1.5.1/translate/convert/test_po2tmx.py new file mode 100644 index 0000000..a13d5d5 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2tmx.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import po2tmx +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import tmx +from translate.storage import lisa + +class TestPO2TMX: + + def po2tmx(self, posource, sourcelanguage='en', targetlanguage='af'): + """helper that converts po source to tmx source without requiring files""" + inputfile = wStringIO.StringIO(posource) + outputfile = wStringIO.StringIO() + outputfile.tmxfile = tmx.tmxfile(inputfile=None, sourcelanguage=sourcelanguage) + po2tmx.convertpo(inputfile, outputfile, templatefile=None, sourcelanguage=sourcelanguage, targetlanguage=targetlanguage) + return outputfile.tmxfile + + def test_basic(self): + minipo = """# Afrikaans translation of program ABC +# +msgid "" +msgstr "" +"Project-Id-Version: program 2.1-branch\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-01-09 07:15+0100\n" +"PO-Revision-Date: 2004-03-30 17:02+0200\n" +"Last-Translator: Zuza Software Foundation <xxx@translate.org.za>\n" +"Language-Team: Afrikaans <translate-discuss-xxx@lists.sourceforge.net>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# Please remember to do something +#: ../dir/file.xml.in.h:1 ../dir/file2.xml.in.h:4 +msgid "Applications" +msgstr "Toepassings" +""" + tmx = self.po2tmx(minipo) + print "The generated xml:" + print str(tmx) + assert tmx.translate("Applications") == "Toepassings" + assert tmx.translate("bla") is None + xmltext = str(tmx) + assert xmltext.index('creationtool="Translate Toolkit - po2tmx"') + assert xmltext.index('adminlang') + assert xmltext.index('creationtoolversion') + assert xmltext.index('datatype') + assert xmltext.index('o-tmf') + assert xmltext.index('segtype') + assert xmltext.index('srclang') + + def test_sourcelanguage(self): + minipo = 'msgid "String"\nmsgstr "String"\n' + tmx = self.po2tmx(minipo, sourcelanguage="xh") + print "The generated xml:" + print str(tmx) + header = tmx.document.find("header") + assert header.get("srclang") == "xh" + + def test_targetlanguage(self): + minipo = 'msgid "String"\nmsgstr "String"\n' + tmx = self.po2tmx(minipo, targetlanguage="xh") + print "The generated xml:" + print str(tmx) + tuv = tmx.document.findall(".//%s" % tmx.namespaced("tuv"))[1] + #tag[0] will be the source, we want the target tuv + assert tuv.get("{%s}lang" % lisa.XML_NS) == "xh" + + def test_multiline(self): + """Test multiline po entry""" + minipo = r'''msgid "First part " +"and extra" +msgstr "Eerste deel " +"en ekstra"''' + tmx = self.po2tmx(minipo) + print "The generated xml:" + print str(tmx) + assert tmx.translate('First part and extra') == 'Eerste deel en ekstra' + + + def test_escapednewlines(self): + """Test the escaping of newlines""" + minipo = r'''msgid "First line\nSecond line" +msgstr "Eerste lyn\nTweede lyn" +''' + tmx = self.po2tmx(minipo) + print "The generated xml:" + print str(tmx) + assert tmx.translate("First line\nSecond line") == "Eerste lyn\nTweede lyn" + + def test_escapedtabs(self): + """Test the escaping of tabs""" + minipo = r'''msgid "First column\tSecond column" +msgstr "Eerste kolom\tTweede kolom" +''' + tmx = self.po2tmx(minipo) + print "The generated xml:" + print str(tmx) + assert tmx.translate("First column\tSecond column") == "Eerste kolom\tTweede kolom" + + def test_escapedquotes(self): + """Test the escaping of quotes (and slash)""" + minipo = r'''msgid "Hello \"Everyone\"" +msgstr "Good day \"All\"" + +msgid "Use \\\"." +msgstr "Gebruik \\\"." +''' + tmx = self.po2tmx(minipo) + print "The generated xml:" + print str(tmx) + assert tmx.translate('Hello "Everyone"') == 'Good day "All"' + assert tmx.translate(r'Use \".') == r'Gebruik \".' + + def test_exclusions(self): + """Test that empty and fuzzy messages are excluded""" + minipo = r'''#, fuzzy +msgid "One" +msgstr "Een" + +msgid "Two" +msgstr "" + +msgid "" +msgstr "Drie" +''' + tmx = self.po2tmx(minipo) + print "The generated xml:" + print str(tmx) + assert "<tu" not in str(tmx) + assert len(tmx.units) == 0 + + def test_nonascii(self): + """Tests that non-ascii conversion works.""" + minipo = r'''msgid "Bézier curve" +msgstr "Bézier-kurwe" +''' + tmx = self.po2tmx(minipo) + print str(tmx) + assert tmx.translate(u"Bézier curve") == u"Bézier-kurwe" + + +class TestPO2TMXCommand(test_convert.TestConvertCommand, TestPO2TMX): + """Tests running actual po2tmx commands on files""" + convertmodule = po2tmx + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-l LANG, --language=LANG") + options = self.help_check(options, "--source-language=LANG", last=True) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2ts.py b/translate-toolkit-1.5.1/translate/convert/test_po2ts.py new file mode 100644 index 0000000..7213a48 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2ts.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +from translate.convert import po2ts +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po + +class TestPO2TS: + def po2ts(self, posource): + """helper that converts po source to ts source without requiring files""" + inputfile = wStringIO.StringIO(posource) + inputpo = po.pofile(inputfile) + convertor = po2ts.po2ts() + outputts = convertor.convertstore(inputpo) + return outputts + + def singleelement(self, storage): + """checks that the pofile contains a single non-header element, and returns it""" + assert len(storage.units) == 1 + return storage.units[0] + + def test_simpleunit(self): + """checks that a simple po entry definition converts properly to a ts entry""" + minipo = r'''#: term.cpp +msgid "Term" +msgstr "asdf"''' + tsfile = self.po2ts(minipo) + print tsfile + assert "<name>term.cpp</name>" in tsfile + assert "<source>Term</source>" in tsfile + assert "<translation>asdf</translation>" in tsfile + assert "<comment>" not in tsfile + + def test_fullunit(self): + """check that an entry with various settings is converted correctly""" + posource = '''# Translator comment +#. Automatic comment +#: location.cpp:100 +msgid "Source" +msgstr "Target" +''' + tsfile = self.po2ts(posource) + print tsfile + # The other section are a duplicate of test_simplentry + # FIXME need to think about auto vs trans comments maybe in TS v1.1 + assert "<comment>Translator comment</comment>" in tsfile + + def test_fuzzyunit(self): + """check that we handle fuzzy units correctly""" + posource = '''#: term.cpp +#, fuzzy +msgid "Source" +msgstr "Target"''' + tsfile = self.po2ts(posource) + print tsfile + assert '''<translation type="unfinished">Target</translation>''' in tsfile + + def test_obsolete(self): + """test that we can take back obsolete messages""" + posource = '''#. (obsolete) +#: term.cpp +msgid "Source" +msgstr "Target"''' + tsfile = self.po2ts(posource) + print tsfile + assert '''<translation type="obsolete">Target</translation>''' in tsfile + + def test_duplicates(self): + """test that we can handle duplicates in the same context block""" + posource = '''#: @@@#1 +msgid "English" +msgstr "a" + +#: @@@#3 +msgid "English" +msgstr "b" +''' + tsfile = self.po2ts(posource) + print tsfile + assert tsfile.find("English") != tsfile.rfind("English") + + +class TestPO2TSCommand(test_convert.TestConvertCommand, TestPO2TS): + """Tests running actual po2ts commands on files""" + convertmodule = po2ts + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-c CONTEXT, --context=CONTEXT") + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2txt.py b/translate-toolkit-1.5.1/translate/convert/test_po2txt.py new file mode 100644 index 0000000..20e8451 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2txt.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import po2txt +from translate.convert import test_convert +from translate.misc import wStringIO + +class TestPO2Txt: + def po2txt(self, posource, txttemplate=None): + """helper that converts po source to txt source without requiring files""" + inputfile = wStringIO.StringIO(posource) + print inputfile.getvalue() + outputfile = wStringIO.StringIO() + if txttemplate: + templatefile = wStringIO.StringIO(txttemplate) + else: + templatefile = None + assert po2txt.converttxt(inputfile, outputfile, templatefile) + print outputfile.getvalue() + return outputfile.getvalue() + + def test_basic(self): + """test basic conversion""" + txttemplate = "Heading\n\nBody text" + posource = 'msgid "Heading"\nmsgstr "Opskrif"\n\nmsgid "Body text"\nmsgstr "Lyfteks"\n' + assert self.po2txt(posource, txttemplate) == "Opskrif\n\nLyfteks" + + def test_nonascii(self): + """test conversion with non-ascii text""" + txttemplate = "Heading\n\nFile content" + posource = 'msgid "Heading"\nmsgstr "Opskrif"\n\nmsgid "File content"\nmsgstr "Lêerinhoud"\n' + assert self.po2txt(posource, txttemplate) == "Opskrif\n\nLêerinhoud" + + def test_blank_handling(self): + """check that we discard blank messages""" + txttemplate = "Heading\n\nBody text" + posource = 'msgid "Heading"\nmsgstr "Opskrif"\n\nmsgid "Body text"\nmsgstr ""\n' + assert self.po2txt(posource) == "Opskrif\n\nBody text" + assert self.po2txt(posource, txttemplate) == "Opskrif\n\nBody text" + + def test_fuzzy_handling(self): + """check that we handle fuzzy message correctly""" + txttemplate = "Heading\n\nBody text" + posource = '#, fuzzy\nmsgid "Heading"\nmsgstr "Opskrif"\n\nmsgid "Body text"\nmsgstr "Lyfteks"\n' + assert self.po2txt(posource) == "Heading\n\nLyfteks" + assert self.po2txt(posource, txttemplate) == "Heading\n\nLyfteks" + +class TestPO2TxtCommand(test_convert.TestConvertCommand, TestPO2Txt): + """Tests running actual po2txt commands on files""" + convertmodule = po2txt + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--fuzzy") + options = self.help_check(options, "--nofuzzy") + options = self.help_check(options, "--encoding") + options = self.help_check(options, "-w WRAP, --wrap=WRAP", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_po2xliff.py b/translate-toolkit-1.5.1/translate/convert/test_po2xliff.py new file mode 100644 index 0000000..1ee0555 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_po2xliff.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python + +from translate.convert import po2xliff +from translate.storage import po +from translate.storage import poxliff +from translate.storage import lisa + +class TestPO2XLIFF: + + def po2xliff(self, posource, sourcelanguage='en', targetlanguage=None): + """helper that converts po source to xliff source without requiring files""" + postore = po.pofile(posource) + convertor = po2xliff.po2xliff() + outputxliff = convertor.convertstore(postore, None, sourcelanguage=sourcelanguage, targetlanguage=targetlanguage) + return poxliff.PoXliffFile(outputxliff) + + def getnode(self, xliff): + """Retrieves the trans-unit node from the dom""" + assert len(xliff.units) == 1 + unit = xliff.units[0] + return unit + + def test_minimal(self): + minipo = '''msgid "red"\nmsgstr "rooi"\n''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + print str(xliff) + assert len(xliff.units) == 1 + assert xliff.translate("red") == "rooi" + assert xliff.translate("bla") is None + + def test_basic(self): + minipo = """# Afrikaans translation of program ABC +# +msgid "" +msgstr "" +"Project-Id-Version: program 2.1-branch\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-01-09 07:15+0100\n" +"PO-Revision-Date: 2004-03-30 17:02+0200\n" +"Last-Translator: Zuza Software Foundation <xxx@translate.org.za>\n" +"Language-Team: Afrikaans <translate-discuss-xxx@lists.sourceforge.net>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# Please remember to do something +#: ../dir/file.xml.in.h:1 ../dir/file2.xml.in.h:4 +msgid "Applications" +msgstr "Toepassings" +""" + xliff = self.po2xliff(minipo) + print "The generated xml:" + print str(xliff) + assert xliff.translate("Applications") == "Toepassings" + assert xliff.translate("bla") is None + xmltext = str(xliff) + assert xmltext.index('<xliff ') >= 0 + assert xmltext.index(' version="1.1"') >= 0 + assert xmltext.index('<file') + assert xmltext.index('source-language') + assert xmltext.index('datatype') + + def test_multiline(self): + """Test multiline po entry""" + minipo = r'''msgid "First part " +"and extra" +msgstr "Eerste deel " +"en ekstra"''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + print str(xliff) + assert xliff.translate('First part and extra') == 'Eerste deel en ekstra' + + + def test_escapednewlines(self): + """Test the escaping of newlines""" + minipo = r'''msgid "First line\nSecond line" +msgstr "Eerste lyn\nTweede lyn" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert xliff.translate("First line\nSecond line") == "Eerste lyn\nTweede lyn" + assert xliff.translate("First line\\nSecond line") is None + assert xmltext.find("line\\nSecond") == -1 + assert xmltext.find("lyn\\nTweede") == -1 + assert xmltext.find("line\nSecond") > 0 + assert xmltext.find("lyn\nTweede") > 0 + + def test_escapedtabs(self): + """Test the escaping of tabs""" + minipo = r'''msgid "First column\tSecond column" +msgstr "Eerste kolom\tTweede kolom" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert xliff.translate("First column\tSecond column") == "Eerste kolom\tTweede kolom" + assert xliff.translate("First column\\tSecond column") is None + assert xmltext.find("column\\tSecond") == -1 + assert xmltext.find("kolom\\tTweede") == -1 + assert xmltext.find("column\tSecond") > 0 + assert xmltext.find("kolom\tTweede") > 0 + + def test_escapedquotes(self): + """Test the escaping of quotes (and slash)""" + minipo = r'''msgid "Hello \"Everyone\"" +msgstr "Good day \"All\"" + +msgid "Use \\\"." +msgstr "Gebruik \\\"." +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert xliff.translate('Hello "Everyone"') == 'Good day "All"' + assert xliff.translate(r'Use \".') == r'Gebruik \".' + assert xmltext.find(r'\"') > 0 or xmltext.find(r'\"') > 0 + assert xmltext.find(r"\\") == -1 + + def getcontexttuples(self, node, namespace): + """Returns all the information in the context nodes as a list of tuples + of (type, text)""" + contexts = node.findall(".//{%s}context" % namespace) + return [(context.get("context-type"), lisa.getText(context)) for context in contexts] + + def test_locationcomments(self): + minipo = r'''#: file.c:123 asdf.c +msgid "one" +msgstr "kunye" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert xliff.translate("one") == "kunye" + assert len(xliff.units) == 1 + node = xliff.units[0].xmlelement + contextgroups = node.findall(".//%s" % xliff.namespaced("context-group")) + assert len(contextgroups) == 2 + for group in contextgroups: + assert group.get("name") == "po-reference" + assert group.get("purpose") == "location" + tuples = self.getcontexttuples(node, xliff.namespace) + assert tuples == [("sourcefile", "file.c"), ("linenumber", "123"), ("sourcefile", "asdf.c")] + + def test_othercomments(self): + minipo = r'''# Translate? +# How? +msgid "one" +msgstr "kunye" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert xliff.translate("one") == "kunye" + assert len(xliff.units) == 1 + node = xliff.units[0].xmlelement + contextgroups = node.findall(".//%s" % xliff.namespaced("context-group")) + assert len(contextgroups) == 1 + for group in contextgroups: + assert group.get("name") == "po-entry" + assert group.get("purpose") == "information" + tuples = self.getcontexttuples(node, xliff.namespace) + assert tuples == [("x-po-trancomment", "Translate?\nHow?")] + + assert xliff.units[0].getnotes("translator") == "Translate?\nHow?" + + + def test_automaticcomments(self): + minipo = r'''#. Don't translate. +#. Please +msgid "one" +msgstr "kunye" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert xliff.translate("one") == "kunye" + assert len(xliff.units) == 1 + node = xliff.units[0].xmlelement + contextgroups = node.findall(".//%s" % xliff.namespaced("context-group")) + assert len(contextgroups) == 1 + for group in contextgroups: + assert group.get("name") == "po-entry" + assert group.get("purpose") == "information" + tuples = self.getcontexttuples(node, xliff.namespace) + assert tuples == [("x-po-autocomment", "Don't translate.\nPlease")] + + def test_header(self): + minipo = r'''# Pulana Translation for bla +# Hallo Ma! +#, fuzzy +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert len(xliff.units) == 1 + unit = xliff.units[0] + assert unit.source == unit.target == "MIME-Version: 1.0\n" + assert unit.xmlelement.get("restype") == "x-gettext-domain-header" + assert unit.xmlelement.get("approved") != "yes" + assert unit.xmlelement.get("{%s}space" % lisa.XML_NS) == "preserve" + assert unit.getnotes("po-translator") == "Pulana Translation for bla\nHallo Ma!" + + def test_fuzzy(self): + minipo = r'''#, fuzzy +msgid "two" +msgstr "pedi" + +msgid "three" +msgstr "raro" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert len(xliff.units) == 2 + assert xliff.units[0].isfuzzy() + assert not xliff.units[1].isfuzzy() + + def test_germanic_plurals(self): + minipo = r'''msgid "cow" +msgid_plural "cows" +msgstr[0] "inkomo" +msgstr[1] "iinkomo" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert len(xliff.units) == 1 + assert xliff.translate("cow") == "inkomo" + + def test_funny_plurals(self): + minipo = r'''msgid "cow" +msgid_plural "cows" +msgstr[0] "inkomo" +msgstr[1] "iinkomo" +msgstr[2] "iiinkomo" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert len(xliff.units) == 1 + assert xliff.translate("cow") == "inkomo" + + def test_language_tags(self): + minipo = r'''msgid "Een" +msgstr "Uno" +''' + xliff = self.po2xliff(minipo, "af", "es") + assert xliff.sourcelanguage == "af" + assert xliff.targetlanguage == "es" + + def test_variables(self): + minipo = r'''msgid "%s%s%s%s has made %s his or her buddy%s%s" +msgstr "%s%s%s%s het %s sy/haar vriend/vriendin gemaak%s%s"''' + xliff = self.po2xliff(minipo) + print xliff.units[0].source + assert xliff.units[0].source == "%s%s%s%s has made %s his or her buddy%s%s" + + def test_approved(self): + minipo = r'''#, fuzzy +msgid "two" +msgstr "pedi" + +msgid "three" +msgstr "raro" + +msgid "four" +msgstr "" +''' + xliff = self.po2xliff(minipo) + print "The generated xml:" + xmltext = str(xliff) + print xmltext + assert len(xliff.units) == 3 + assert xliff.units[0].xmlelement.get("approved") != "yes" + assert not xliff.units[0].isapproved() + assert xliff.units[1].xmlelement.get("approved") == "yes" + assert xliff.units[1].isapproved() + assert xliff.units[2].xmlelement.get("approved") != "yes" + assert not xliff.units[2].isapproved() + diff --git a/translate-toolkit-1.5.1/translate/convert/test_pot2po.py b/translate-toolkit-1.5.1/translate/convert/test_pot2po.py new file mode 100644 index 0000000..5404449 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_pot2po.py @@ -0,0 +1,609 @@ +#!/usr/bin/env python + +from translate.convert import pot2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +import warnings + +class TestPOT2PO: + def setup_method(self, method): + warnings.resetwarnings() + + def teardown_method(self, method): + warnings.resetwarnings() + + def convertpot(self, potsource, posource=None): + """helper that converts pot source to po source without requiring files""" + potfile = wStringIO.StringIO(potsource) + if posource: + pofile = wStringIO.StringIO(posource) + else: + pofile = None + pooutfile = wStringIO.StringIO() + pot2po.convertpot(potfile, pooutfile, pofile) + pooutfile.seek(0) + return po.pofile(pooutfile.read()) + + def singleunit(self, pofile): + """checks that the pofile contains a single non-header unit, and returns it""" + assert len(pofile.units) == 2 + assert pofile.units[0].isheader() + print pofile.units[1] + return pofile.units[1] + + def test_convertpot_blank(self): + """checks that the convertpot function is working for a simple file initialisation""" + potsource = '''#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr ""\n''' % po.lsep + newpo = self.convertpot(potsource) + assert str(self.singleunit(newpo)) == potsource + + def test_convertpot_blank_plurals(self): + """checks that the convertpot function is working for initialising plurals correctly""" + potsource = r'''msgid "" +msgstr"" + +msgid "%d manual" +msgid_plural "%d manuals" +msgstr[0] "" +msgstr[1] "" +''' + posource = r'''msgid "" +msgstr"" +"Plural-Forms: nplurals=1; plural=0;\n" +''' + + poexpected = r'''msgid "%d manual" +msgid_plural "%d manuals" +msgstr[0] "" +''' + newpo = self.convertpot(potsource, posource) + assert str(self.singleunit(newpo)) == poexpected + + def test_merging_simple(self): + """checks that the convertpot function is working for a simple merge""" + potsource = '''#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr ""\n''' % po.lsep + posource = '''#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n''' % po.lsep + newpo = self.convertpot(potsource, posource) + assert str(self.singleunit(newpo)) == posource + + def test_merging_messages_marked_fuzzy(self): + """test that when we merge PO files with a fuzzy message that it remains fuzzy""" + potsource = '''#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr ""\n''' % po.lsep + posource = '''#: simple.label%ssimple.accesskey\n#, fuzzy\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n''' % po.lsep + newpo = self.convertpot(potsource, posource) + assert str(self.singleunit(newpo)) == posource + + def test_merging_plurals_with_fuzzy_matching(self): + """test that when we merge PO files with a fuzzy message that it remains fuzzy""" + potsource = r'''#: file.cpp:2 +msgid "%d manual" +msgid_plural "%d manuals" +msgstr[0] "" +msgstr[1] "" +''' + posource = r'''#: file.cpp:3 +#, fuzzy +msgid "%d manual" +msgid_plural "%d manuals" +msgstr[0] "%d handleiding." +msgstr[1] "%d handleidings." +''' + # The #: comment and msgid's are different between the pot and the po + poexpected = r'''#: file.cpp:2 +#, fuzzy +msgid "%d manual" +msgid_plural "%d manuals" +msgstr[0] "%d handleiding." +msgstr[1] "%d handleidings." +''' + newpo = self.convertpot(potsource, posource) + assert str(self.singleunit(newpo)) == poexpected + + def xtest_merging_msgid_change(self): + """tests that if the msgid changes but the location stays the same that we merge""" + potsource = '''#: simple.label\n#: simple.accesskey\nmsgid "Its &hard coding a newline.\\n"\nmsgstr ""\n''' + posource = '''#: simple.label\n#: simple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n''' + poexpected = '''#: simple.label\n#: simple.accesskey\n#, fuzzy\nmsgid "Its &hard coding a newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n''' + newpo = self.convertpot(potsource, posource) + print newpo + assert str(self.singleunit(newpo)) == poexpected + + def test_merging_location_change(self): + """tests that if the location changes but the msgid stays the same that we merge""" + potsource = '''#: new_simple.label%snew_simple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr ""\n''' % po.lsep + posource = '''#: simple.label%ssimple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n''' % po.lsep + poexpected = '''#: new_simple.label%snew_simple.accesskey\nmsgid "A &hard coded newline.\\n"\nmsgstr "&Hart gekoeerde nuwe lyne\\n"\n''' % po.lsep + newpo = self.convertpot(potsource, posource) + print newpo + assert str(self.singleunit(newpo)) == poexpected + + def test_merging_location_and_whitespace_change(self): + """test that even if the location changes that if the msgid only has whitespace changes we can still merge""" + potsource = '''#: singlespace.label%ssinglespace.accesskey\nmsgid "&We have spaces"\nmsgstr ""\n''' % po.lsep + posource = '''#: doublespace.label%sdoublespace.accesskey\nmsgid "&We have spaces"\nmsgstr "&One het spasies"\n''' % po.lsep + poexpected = '''#: singlespace.label%ssinglespace.accesskey\n#, fuzzy\nmsgid "&We have spaces"\nmsgstr "&One het spasies"\n''' % po.lsep + newpo = self.convertpot(potsource, posource) + print newpo + assert str(self.singleunit(newpo)) == poexpected + + def test_merging_location_ambiguous_with_disambiguous(self): + """test that when we have a PO in ambiguous (Gettext form) and merge with disamabiguous (KDE comment form) + that we don't duplicate the location #: comments""" + potsource = '''#: location.c:1\nmsgid ""\n"_: location.c:1\\n"\n"Source"\nmsgstr ""\n\n''' + \ + '''#: location.c:10\nmsgid ""\n"_: location.c:10\\n"\n"Source"\nmsgstr ""\n''' + posource = '''#: location.c:1\n#: location.c:10\nmsgid "Source"\nmsgstr "Target"\n\n''' + poexpected1 = '''#: location.c:1\n#, fuzzy\nmsgid ""\n"_: location.c:1\\n"\n"Source"\nmsgstr "Target"\n''' + poexpected2 = '''#: location.c:10\n#, fuzzy\nmsgid ""\n"_: location.c:10\\n"\n"Source"\nmsgstr "Target"\n''' + newpo = self.convertpot(potsource, posource) + print "Expected:\n", poexpected1, "Actual:\n", newpo.units[1] + assert str(newpo.units[1]) == poexpected1 + assert str(newpo.units[2]) == poexpected2 + + def wtest_merging_accelerator_changes(self): + """test that a change in the accelerator localtion still allows merging""" + potsource = '''#: someline.c\nmsgid "A&bout"\nmsgstr ""\n''' + posource = '''#: someline.c\nmsgid "&About"\nmsgstr "&Info"\n''' + poexpected = '''#: someline.c\nmsgid "A&bout"\nmsgstr "&Info"\n''' + newpo = self.convertpot(potsource, posource) + print newpo + assert str(self.singleunit(newpo)) == poexpected + + def xtest_lines_cut_differently(self): + """Checks that the correct formatting is preserved when pot an po lines differ.""" + potsource = '''#: simple.label\nmsgid "Line split "\n"differently"\nmsgstr ""\n''' + posource = '''#: simple.label\nmsgid "Line"\n" split differently"\nmsgstr "Lyne verskillend gesny"\n''' + newpo = self.convertpot(potsource, posource) + newpounit = self.singleunit(newpo) + assert str(newpounit) == posource + + def test_merging_automatic_comments_dont_duplicate(self): + """ensure that we can merge #. comments correctly""" + potsource = '''#. Row 35\nmsgid "&About"\nmsgstr ""\n''' + posource = '''#. Row 35\nmsgid "&About"\nmsgstr "&Info"\n''' + newpo = self.convertpot(potsource, posource) + newpounit = self.singleunit(newpo) + assert str(newpounit) == posource + + def test_merging_automatic_comments_new_overides_old(self): + """ensure that new #. comments override the old comments""" + potsource = '''#. new comment\n#: someline.c\nmsgid "&About"\nmsgstr ""\n''' + posource = '''#. old comment\n#: someline.c\nmsgid "&About"\nmsgstr "&Info"\n''' + poexpected = '''#. new comment\n#: someline.c\nmsgid "&About"\nmsgstr "&Info"\n''' + newpo = self.convertpot(potsource, posource) + newpounit = self.singleunit(newpo) + assert str(newpounit) == poexpected + + def test_merging_comments_with_blank_comment_lines(self): + """test that when we merge a comment that has a blank line we keep the blank line""" + potsource = '''#: someline.c\nmsgid "About"\nmsgstr ""\n''' + posource = '''# comment1\n#\n# comment2\n#: someline.c\nmsgid "About"\nmsgstr "Omtrent"\n''' + poexpected = posource + newpo = self.convertpot(potsource, posource) + newpounit = self.singleunit(newpo) + assert str(newpounit) == poexpected + + def test_empty_commentlines(self): + potsource = '''#: paneSecurity.title +msgid "Security" +msgstr "" +''' + posource = '''# - Contributor(s): +# - +# - Alternatively, the +# - +#: paneSecurity.title +msgid "Security" +msgstr "Sekuriteit" +''' + poexpected = posource + newpo = self.convertpot(potsource, posource) + newpounit = self.singleunit(newpo) + print "expected" + print poexpected + print "got:" + print str(newpounit) + assert str(newpounit) == poexpected + + def test_merging_msgidcomments(self): + """ensure that we can merge msgidcomments messages""" + potsource = r'''#: window.width +msgid "" +"_: Do not translate this.\n" +"36em" +msgstr "" +''' + posource = r'''#: window.width +msgid "" +"_: Do not translate this.\n" +"36em" +msgstr "36em" +''' + newpo = self.convertpot(potsource, posource) + newpounit = self.singleunit(newpo) + assert str(newpounit) == posource + + def test_merging_msgid_with_msgidcomment(self): + """test that we can merge an otherwise identical string that has a different msgid""" + potsource = r'''#: pref.certs.title +msgid "" +"_: pref.certs.title\n" +"Certificates" +msgstr "" + +#: certs.label +msgid "" +"_: certs.label\n" +"Certificates" +msgstr "" +''' + posource = r'''#: pref.certs.title +msgid "" +"_: pref.certs.title\n" +"Certificates" +msgstr "" + +#: certs.label +msgid "" +"_: certs.label\n" +"Certificates" +msgstr "Sertifikate" +''' + expected = r'''#: pref.certs.title +#, fuzzy +msgid "" +"_: pref.certs.title\n" +"Certificates" +msgstr "Sertifikate" +''' + newpo = self.convertpot(potsource, posource) + newpounit = newpo.units[1] + assert str(newpounit) == expected + + def test_merging_plurals(self): + """ensure that we can merge plural messages""" + potsource = '''msgid "One"\nmsgid_plural "Two"\nmsgstr[0] ""\nmsgstr[1] ""\n''' + posource = '''msgid "One"\nmsgid_plural "Two"\nmsgstr[0] "Een"\nmsgstr[1] "Twee"\nmsgstr[2] "Drie"\n''' + newpo = self.convertpot(potsource, posource) + print newpo + newpounit = self.singleunit(newpo) + assert str(newpounit) == posource + + def test_merging_obsoleting_messages(self): + """check that we obsolete messages no longer present in the new file""" + #add emtpy msgid line to help factory identify format + potsource = 'msgid ""\nmsgstr ""\n' + posource = '# Some comment\n#. Extracted comment\n#: obsoleteme:10\nmsgid "One"\nmsgstr "Een"\n' + expected = '# Some comment\n#~ msgid "One"\n#~ msgstr "Een"\n' + newpo = self.convertpot(potsource, posource) + print str(newpo) + newpounit = self.singleunit(newpo) + assert str(newpounit) == expected + + def test_not_obsoleting_empty_messages(self): + """check that we don't obsolete (and keep) untranslated messages""" + #add emtpy msgid line to help factory identify format + potsource = 'msgid ""\nmsgstr ""\n' + posource = '#: obsoleteme:10\nmsgid "One"\nmsgstr ""\n' + newpo = self.convertpot(potsource, posource) + print str(newpo) + # We should only have the header + assert len(newpo.units) == 1 + + def test_merging_new_before_obsolete(self): + """test to check that we place new blank message before obsolete messages""" + potsource = '''#: newline.c\nmsgid "&About"\nmsgstr ""\n''' + posource = '''#~ msgid "Old"\n#~ msgstr "Oud"\n''' + newpo = self.convertpot(potsource, posource) + assert len(newpo.units) == 3 + assert newpo.units[0].isheader() + assert newpo.units[2].isobsolete() + assert str(newpo.units[1]) == potsource + assert str(newpo.units[2]) == posource + + # Now test with real units present in posource + posource2 = '''msgid "Old"\nmsgstr "Oud"\n''' + newpo = self.convertpot(potsource, posource) + assert len(newpo.units) == 3 + assert newpo.units[0].isheader() + assert newpo.units[2].isobsolete() + assert str(newpo.units[1]) == potsource + assert str(newpo.units[2]) == posource + + def test_merging_resurect_obsolete_messages(self): + """check that we can reuse old obsolete messages if the message comes back""" + potsource = '''#: resurect.c\nmsgid "&About"\nmsgstr ""\n''' + posource = '''#~ msgid "&About"\n#~ msgstr "&Omtrent"\n''' + expected = '''#: resurect.c\nmsgid "&About"\nmsgstr "&Omtrent"\n''' + newpo = self.convertpot(potsource, posource) + print newpo + assert len(newpo.units) == 2 + assert newpo.units[0].isheader() + newpounit = self.singleunit(newpo) + assert str(newpounit) == expected + + def test_merging_resurect_obsolete_messages_into_msgidcomment(self): + """check that we can reuse old obsolete messages even if the recipient has a msgidcomment""" + potsource = '''#: resurect1.c\nmsgid "About"\nmsgstr ""\n\n''' + \ + '''#: resurect2.c\nmsgid ""\n"_: resurect2.c\\n"\n"About"\nmsgstr ""\n''' + posource = '''#~ msgid "About"\n#~ msgstr "Omtrent"\n''' + expected1 = '''#: resurect1.c\nmsgid "About"\nmsgstr "Omtrent"\n''' + expected2 = '''#: resurect2.c\n#, fuzzy\nmsgid ""\n"_: resurect2.c\\n"\n"About"\nmsgstr "Omtrent"\n''' + newpo = self.convertpot(potsource, posource) + print newpo + assert len(newpo.units) == 3 + assert newpo.units[0].isheader() + assert str(newpo.units[1]) == expected1 + assert str(newpo.units[2]) == expected2 + + def test_header_initialisation(self): + """test to check that we initialise the header correctly""" + potsource = r'''#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: new@example.com\n" +"POT-Creation-Date: 2006-11-11 11:11+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" +"X-Generator: Translate Toolkit 0.10rc2\n" +''' + posource = r'''msgid "" +msgstr "" +"Project-Id-Version: Pootle 0.10\n" +"Report-Msgid-Bugs-To: old@example.com\n" +"POT-Creation-Date: 2006-01-01 01:01+0100\n" +"PO-Revision-Date: 2006-09-09 09:09+0900\n" +"Last-Translator: Joe Translate <joe@example.com>\n" +"Language-Team: Pig Latin <piglatin@example.com>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Translate Toolkit 0.9\n" +''' + expected = r'''msgid "" +msgstr "" +"Project-Id-Version: Pootle 0.10\n" +"Report-Msgid-Bugs-To: new@example.com\n" +"POT-Creation-Date: 2006-11-11 11:11+0000\n" +"PO-Revision-Date: 2006-09-09 09:09+0900\n" +"Last-Translator: Joe Translate <joe@example.com>\n" +"Language-Team: Pig Latin <piglatin@example.com>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Translate Toolkit 0.10rc2\n" +''' + newpo = self.convertpot(potsource, posource) + print 'Output Header:\n%s' % newpo + print 'Expected Header:\n%s' % expected + assert str(newpo) == expected + + def test_merging_comments(self): + """Test that we can merge comments correctly""" + potsource = '''#. Don't do it!\n#: file.py:1\nmsgid "One"\nmsgstr ""\n''' + posource = '''#. Don't do it!\n#: file.py:2\nmsgid "One"\nmsgstr "Een"\n''' + poexpected = '''#. Don't do it!\n#: file.py:1\nmsgid "One"\nmsgstr "Een"\n''' + newpo = self.convertpot(potsource, posource) + print newpo + newpounit = self.singleunit(newpo) + assert str(newpounit) == poexpected + + def test_merging_typecomments(self): + """Test that we can merge with typecomments""" + potsource = '''#: file.c:1\n#, c-format\nmsgid "%d pipes"\nmsgstr ""\n''' + posource = '''#: file.c:2\nmsgid "%d pipes"\nmsgstr "%d pype"\n''' + poexpected = '''#: file.c:1\n#, c-format\nmsgid "%d pipes"\nmsgstr "%d pype"\n''' + newpo = self.convertpot(potsource, posource) + newpounit = self.singleunit(newpo) + print newpounit + assert str(newpounit) == poexpected + + potsource = '''#: file.c:1\n#, c-format\nmsgid "%d computers"\nmsgstr ""\n''' + posource = '''#: file.c:2\n#, c-format\nmsgid "%s computers "\nmsgstr "%s-rekenaars"\n''' + poexpected = '''#: file.c:1\n#, fuzzy, c-format\nmsgid "%d computers"\nmsgstr "%s-rekenaars"\n''' + newpo = self.convertpot(potsource, posource) + newpounit = self.singleunit(newpo) + assert newpounit.isfuzzy() + assert newpounit.hastypecomment("c-format") + + def test_msgctxt(self): + """Test that msgctxt is migrated correctly""" + potsource = """ +#: something.h:5 +msgctxt "context1" +msgid "text" +msgstr "" + +#: something.h:6 +msgctxt "context2" +msgid "text" +msgstr "" +""" + posource = """ +#: something.h:3 +msgctxt "context0" +msgid "text" +msgstr "teks" + +#: something.h:4 +msgctxt "context1" +msgid "text" +msgstr "sms" +""" + poexpected = """ +#: something.h:5 +msgctxt "context1" +msgid "text" +msgstr "sms" + +#: something.h:6 +#, fuzzy +msgctxt "context2" +msgid "text" +msgstr "teks" +""" + newpo = self.convertpot(potsource, posource) + print newpo + assert poexpected in str(newpo) + + def test_empty_msgid(self): + """Test that we handle empty msgids correctly.""" + #TODO: this test will fail if we don't have the gettext location + # comment in the pot file + potsource = '#: file:1\nmsgctxt "bla"\nmsgid ""\nmsgstr ""\n' + posource = r""" +msgid "" +"Project-Id-Version: Pootle 0.10\n" +msgstr "" + +msgctxt "bla" +msgid "" +msgstr "trans" +""" + newpo = self.convertpot(potsource, posource) + print newpo + assert len(newpo.units) == 2 + assert newpo.units[0].isheader() + unit = newpo.units[1] + assert unit.source == u"" + assert unit.getid() == u"bla\04" + assert unit.target == "trans" + assert not unit.isfuzzy() + + def test_migrate_msgidcomment_to_msgctxt(self): + """Test that we migrate correctly from msgidcomments to msgctxt. + + This is needed for our move away from using msgidcomments for mozilla.""" + potsource = '#: bla\nmsgctxt "bla"\nmsgid ""\nmsgstr ""' + posource = r""" +msgid "" +"Project-Id-Version: Pootle 0.10\n" +msgstr "" + +#: bla +msgid "" +"_: bla\n" +msgstr "trans" +""" + newpo = self.convertpot(potsource, posource) + print newpo + assert len(newpo.units) == 2 + assert newpo.units[0].isheader() + unit = newpo.units[1] + assert unit.source == u"" + assert unit.getid() == u"bla\04" + assert unit.target == "trans" + assert not unit.isfuzzy() + + def test_obsolete_msgctxt(self): + """Test that obsolete units' msgctxt is preserved.""" + potsource = 'msgctxt "newContext"\nmsgid "First unit"\nmsgstr ""' + posource = """ +msgctxt "newContext" +msgid "First unit" +msgstr "Eerste eenheid" + +#~ msgctxt "context" +#~ msgid "Old unit" +#~ msgstr "Ou eenheid1" + +#~ msgctxt "context2" +#~ msgid "Old unit" +#~ msgstr "Ou eenheid2" + +#~ msgid "Old unit" +#~ msgstr "Ou eenheid3" +""" + newpo = self.convertpot(potsource, posource) + print newpo + assert len(newpo.units) == 5 + assert newpo.units[1].getcontext() == 'newContext' + # Search in unit string, because obsolete units can't return a context + assert 'msgctxt "context"' in str(newpo.units[2]) + assert 'msgctxt "context2"' in str(newpo.units[3]) + + def test_small_strings(self): + """Test that units with small source strings are not incorrectly + populated by means of fuzzy matching.""" + potsource = r'''#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: new@example.com\n" +"POT-Creation-Date: 2006-11-11 11:11+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" +"X-Generator: Translate Toolkit 0.10rc2\n" + +#: new_disassociated_mozilla_accesskey +msgid "R" +msgstr "" +''' + posource = r'''msgid "" +msgstr "" +"Project-Id-Version: Pootle 0.10\n" +"Report-Msgid-Bugs-To: old@example.com\n" +"POT-Creation-Date: 2006-01-01 01:01+0100\n" +"PO-Revision-Date: 2006-09-09 09:09+0900\n" +"Last-Translator: Joe Translate <joe@example.com>\n" +"Language-Team: Pig Latin <piglatin@example.com>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Translate Toolkit 0.9\n" + +#: old_disassociated_mozilla_accesskey +msgid "R" +msgstr "S" +''' + expected = r'''msgid "" +msgstr "" +"Project-Id-Version: Pootle 0.10\n" +"Report-Msgid-Bugs-To: new@example.com\n" +"POT-Creation-Date: 2006-11-11 11:11+0000\n" +"PO-Revision-Date: 2006-09-09 09:09+0900\n" +"Last-Translator: Joe Translate <joe@example.com>\n" +"Language-Team: Pig Latin <piglatin@example.com>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Translate Toolkit 0.10rc2\n" + +#: new_disassociated_mozilla_accesskey +msgid "R" +msgstr "" +''' + newpo = self.convertpot(potsource, posource) + print 'Output:\n%s' % newpo + print 'Expected:\n%s' % expected + assert str(newpo) == expected + + +class TestPOT2POCommand(test_convert.TestConvertCommand, TestPOT2PO): + """Tests running actual pot2po commands on files""" + convertmodule = pot2po + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "--tm") + options = self.help_check(options, "-s MIN_SIMILARITY, --similarity=MIN_SIMILARITY") + options = self.help_check(options, "--nofuzzymatching", last=True) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_prop2mozfunny.py b/translate-toolkit-1.5.1/translate/convert/test_prop2mozfunny.py new file mode 100644 index 0000000..e5deb52 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_prop2mozfunny.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +from translate.convert import prop2mozfunny +from translate.misc import wStringIO + +class TestPO2Prop: + def merge2inc(self, incsource, posource): + """helper that merges po translations to .inc source without requiring files""" + inputfile = wStringIO.StringIO(posource) + templatefile = wStringIO.StringIO(incsource) + outputfile = wStringIO.StringIO() + result = prop2mozfunny.po2inc(inputfile, outputfile, templatefile) + outputinc = outputfile.getvalue() + print outputinc + assert result + return outputinc + + def test_no_endlines_added(self): + """check that we don't add newlines at the end of file""" + posource = '''# converted from #defines file\n#: MOZ_LANG_TITLE\nmsgid "English (US)"\nmsgstr "Deutsch (DE)"\n\n''' + inctemplate = '''#define MOZ_LANG_TITLE Deutsch (DE)\n''' + incexpected = inctemplate + incfile = self.merge2inc(inctemplate, posource) + print incfile + assert incfile == incexpected + + def test_uncomment_contributors(self): + """check that we handle uncommenting contributors properly""" + posource = '''# converted from #defines file +#: MOZ_LANGPACK_CONTRIBUTORS +msgid "<em:contributor>Joe Solon</em:contributor>" +msgstr "<em:contributor>Mr Fury</em:contributor>" +''' + inctemplate = '''# #define MOZ_LANGPACK_CONTRIBUTORS <em:contributor>Joe Solon</em:contributor>\n''' + incexpected = '''#define MOZ_LANGPACK_CONTRIBUTORS <em:contributor>Mr Fury</em:contributor>\n''' + incfile = self.merge2inc(inctemplate, posource) + print incfile + assert incfile == incexpected + diff --git a/translate-toolkit-1.5.1/translate/convert/test_prop2po.py b/translate-toolkit-1.5.1/translate/convert/test_prop2po.py new file mode 100644 index 0000000..0a8bb17 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_prop2po.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import prop2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import po +from translate.storage import properties + +class TestProp2PO: + def prop2po(self, propsource, proptemplate=None): + """helper that converts .properties source to po source without requiring files""" + inputfile = wStringIO.StringIO(propsource) + inputprop = properties.propfile(inputfile) + convertor = prop2po.prop2po() + if proptemplate: + templatefile = wStringIO.StringIO(proptemplate) + templateprop = properties.propfile(templatefile) + outputpo = convertor.mergestore(templateprop, inputprop) + else: + outputpo = convertor.convertstore(inputprop) + return outputpo + + def convertprop(self, propsource): + """call the convertprop, return the outputfile""" + inputfile = wStringIO.StringIO(propsource) + outputfile = wStringIO.StringIO() + templatefile = None + assert prop2po.convertprop(inputfile, outputfile, templatefile) + return outputfile.getvalue() + + def singleelement(self, pofile): + """checks that the pofile contains a single non-header element, and returns it""" + assert len(pofile.units) == 2 + assert pofile.units[0].isheader() + print pofile + return pofile.units[1] + + def countelements(self, pofile): + """counts the number of non-header entries""" + assert pofile.units[0].isheader() + print pofile + return len(pofile.units) - 1 + + def test_simpleentry(self): + """checks that a simple properties entry converts properly to a po entry""" + propsource = 'SAVEENTRY=Save file\n' + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.source == "Save file" + assert pounit.target == "" + + def test_convertprop(self): + """checks that the convertprop function is working""" + propsource = 'SAVEENTRY=Save file\n' + posource = self.convertprop(propsource) + pofile = po.pofile(wStringIO.StringIO(posource)) + pounit = self.singleelement(pofile) + assert pounit.source == "Save file" + assert pounit.target == "" + + def test_tab_at_end_of_string(self): + """check that we preserve tabs at the end of a string""" + propsource = r"TAB_AT_END=This setence has a tab at the end.\t" + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.source == "This setence has a tab at the end.\t" + + propsource = r"SPACE_THEN_TAB_AT_END=This setence has a space then tab at the end. \t" + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.source == "This setence has a space then tab at the end. \t" + + propsource = r"SPACE_AT_END=This setence will keep its 4 spaces at the end. " + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.source == "This setence will keep its 4 spaces at the end. " + + propsource = r"SPACE_AT_END_NO_TRIM=This setence will keep its 4 spaces at the end.\\ " + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.source == "This setence will keep its 4 spaces at the end. " + + def test_tab_at_start_of_value(self): + """check that tabs in a property are ignored where appropriate""" + propsource = r"property = value" + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.getlocations()[0] == "property" + assert pounit.source == "value" + + def test_unicode(self): + """checks that unicode entries convert properly""" + unistring = r'Norsk bokm\u00E5l' + propsource = 'nb = %s\n' % unistring + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + print repr(pofile.units[0].target) + print repr(pounit.source) + assert pounit.source == u'Norsk bokm\u00E5l' + + def test_multiline_escaping(self): + """checks that multiline enties can be parsed""" + propsource = r"""5093=Unable to connect to your IMAP server. You may have exceeded the maximum number \ +of connections to this server. If so, use the Advanced IMAP Server Settings dialog to \ +reduce the number of cached connections.""" + pofile = self.prop2po(propsource) + print repr(pofile.units[1].target) + assert self.countelements(pofile) == 1 + + def test_comments(self): + """test to ensure that we take comments from .properties and place them in .po""" + propsource = '''# Comment +prefPanel-smime=Security''' + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.getnotes("developer") == "# Comment" + + def test_multiline_comments(self): + """test to ensure that we handle multiline comments well""" + propsource = '''# Comment +# commenty 2 + +## @name GENERIC_ERROR +## @loc none +prefPanel-smime= +''' + pofile = self.prop2po(propsource) + print str(pofile) + #header comments: + assert "#. # Comment\n#. # commenty 2" in str(pofile) + pounit = self.singleelement(pofile) + assert pounit.getnotes("developer") == "## @name GENERIC_ERROR\n## @loc none" + + def wtest_folding_accesskeys(self): + """check that we can fold various accesskeys into their associated label (bug #115)""" + propsource = r'''cmd_addEngine = Add Engines... +cmd_addEngine_accesskey = A''' + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.target == "&Add Engines..." + + def test_dont_translate(self): + """check that we know how to ignore don't translate instructions in properties files (bug #116)""" + propsource = '''# LOCALIZATION NOTE (dont): DONT_TRANSLATE. +dont=don't translate me +do=translate me +''' + pofile = self.prop2po(propsource) + assert self.countelements(pofile) == 1 + + def test_emptyproperty(self): + """checks that empty property definitions survive into po file, bug 15""" + propsource = '# comment\ncredit=' + pofile = self.prop2po(propsource) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ["credit"] + assert pounit.getcontext() == "credit" + assert 'msgctxt "credit"' in str(pounit) + assert "#. # comment" in str(pofile) + assert pounit.source == "" + + def test_emptyproperty_translated(self): + """checks that if we translate an empty property it makes it into the PO""" + proptemplate = 'credit=' + propsource = 'credit=Translators Names' + pofile = self.prop2po(propsource, proptemplate) + pounit = self.singleelement(pofile) + assert pounit.getlocations() == ["credit"] + # FIXME we don't seem to get a _: comment but we should + #assert pounit.getcontext() == "credit" + assert pounit.source == "" + assert pounit.target == "Translators Names" + + def test_newlines_in_value(self): + """check that we can carry newlines that appear in the property value into the PO""" + propsource = '''prop=\\nvalue\\n\n''' + pofile = self.prop2po(propsource) + unit = self.singleelement(pofile) + assert unit.source == "\nvalue\n" + + def test_unassociated_comments(self): + """check that we can handle comments not directly associated with a property""" + propsource = '''# Header comment\n\n# Comment\n\nprop=value\n''' + pofile = self.prop2po(propsource) + unit = self.singleelement(pofile) + assert unit.source == "value" + assert unit.getnotes("developer") == "# Comment" + + def test_unassociated_comment_order(self): + """check that we can handle the order of unassociated comments""" + propsource = '''# Header comment\n\n# 1st Unassociated comment\n\n# 2nd Connected comment\nprop=value\n''' + pofile = self.prop2po(propsource) + unit = self.singleelement(pofile) + assert unit.source == "value" + assert unit.getnotes("developer") == "# 1st Unassociated comment\n# 2nd Connected comment" + +class TestProp2POCommand(test_convert.TestConvertCommand, TestProp2PO): + """Tests running actual prop2po commands on files""" + convertmodule = prop2po + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE") + options = self.help_check(options, "--personality=TYPE") + options = self.help_check(options, "--duplicates=DUPLICATESTYLE", last=True) + diff --git a/translate-toolkit-1.5.1/translate/convert/test_tiki2po.py b/translate-toolkit-1.5.1/translate/convert/test_tiki2po.py new file mode 100644 index 0000000..8024b41 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_tiki2po.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# tiki2po unit tests +# Author: Wil Clouser <wclouser@mozilla.com> +# Date: 2008-12-01 + +from translate.convert import tiki2po +from translate.storage import tiki +from translate.convert import test_convert +from translate.misc import wStringIO + +class TestTiki2Po: + def test_converttiki_defaults(self): + inputfile = """ +"zero_source" => "zero_target", +// ### Start of unused words +"one_source" => "one_target", +// ### end of unused words + """ + outputfile = wStringIO.StringIO() + tiki2po.converttiki(inputfile, outputfile) + + output = outputfile.getvalue() + + assert '#: translated' in output + assert 'msgid "zero_source"' in output + assert "one_source" not in output + + def test_converttiki_includeunused(self): + inputfile = """ +"zero_source" => "zero_target", +// ### Start of unused words +"one_source" => "one_target", +// ### end of unused words + """ + outputfile = wStringIO.StringIO() + tiki2po.converttiki(inputfile, outputfile, includeunused=True) + + output = outputfile.getvalue() + + assert '#: translated' in output + assert 'msgid "zero_source"' in output + assert '#: unused' in output + assert 'msgid "one_source"' in output + + +class TestTiki2PoCommand(test_convert.TestConvertCommand, TestTiki2Po): + """Tests running actual tiki2po commands on files""" + convertmodule = tiki2po + defaultoptions = {} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "--include-unused") diff --git a/translate-toolkit-1.5.1/translate/convert/test_ts2po.py b/translate-toolkit-1.5.1/translate/convert/test_ts2po.py new file mode 100644 index 0000000..c5a11dd --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_ts2po.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from translate.convert import ts2po +from translate.convert import test_convert +from translate.misc import wStringIO + +class TestTS2PO: + def ts2po(self, tssource): + converter = ts2po.ts2po() + tsfile = wStringIO.StringIO(tssource) + outputpo = converter.convertfile(tsfile) + print "The generated po:" + print str(outputpo) + return outputpo + + def test_blank(self): + """tests blank conversion""" + tssource = '''<!DOCTYPE TS><TS> +<context> + <name>MainWindowBase</name> + <message> + <source>Project:</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> +''' + pofile = self.ts2po(tssource) + assert len(pofile.units) == 2 + assert pofile.units[1].source == "Project:" + assert pofile.units[1].target == "" + assert pofile.units[1].getlocations()[0].startswith("MainWindowBase") + assert not pofile.units[1].isfuzzy() + + def test_basic(self): + """tests basic conversion""" + tssource = '''<!DOCTYPE TS><TS> +<context> + <name>AboutDialog</name> + <message> + <source>&About</source> + <translation>&Giới thiệu</translation> + </message> +</context> +</TS> +''' + pofile = self.ts2po(tssource) + assert len(pofile.units) == 2 + assert pofile.units[1].source == "&About" + assert pofile.units[1].target == u"&Giới thiệu" + assert pofile.units[1].getlocations()[0].startswith("AboutDialog") + + def test_unfinished(self): + """tests unfinished conversion""" + tssource = '''<!DOCTYPE TS><TS> +<context> + <name>MainWindowBase</name> + <message> + <source>Project:</source> + <translation type="unfinished">Projek vergardering</translation> + </message> +</context> +</TS> +''' + pofile = self.ts2po(tssource) + assert len(pofile.units) == 2 + assert pofile.units[1].source == "Project:" + assert pofile.units[1].target == "Projek vergardering" + assert pofile.units[1].getlocations()[0].startswith("MainWindowBase") + assert pofile.units[1].isfuzzy() + + def test_multiline(self): + """tests multiline message conversion""" + tssource = '''<!DOCTYPE TS><TS> +<context> + <name>@default</name> + <message> + <source>Source with +new line</source> + <translation>Test with +new line</translation> + </message> +</context> +</TS> +''' + pofile = self.ts2po(tssource) + assert len(pofile.units) == 2 + assert pofile.units[1].source == "Source with\nnew line" + assert pofile.units[1].target == "Test with\nnew line" + assert pofile.units[1].getlocations()[0].startswith("@default") + + def test_obsolete(self): + """test the handling of obsolete TS entries""" + tssource = '''<!DOCTYPE TS><TS> +<context> + <name>Obsoleted</name> + <message> + <source>Failed</source> + <translation type="obsolete">Mislukt</translation> + </message> +</context> +</TS> +''' + pofile = self.ts2po(tssource) + assert pofile.units[1].getnotes("developer") == "(obsolete)" + # Test that we aren't following the old style + assert "_ OBSOLETE" not in pofile.units[1].getnotes() + +class TestTS2POCommand(test_convert.TestConvertCommand, TestTS2PO): + """Tests running actual ts2po commands on files""" + convertmodule = ts2po + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "--duplicates=DUPLICATESTYLE") + options = self.help_check(options, "-P, --pot", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_txt2po.py b/translate-toolkit-1.5.1/translate/convert/test_txt2po.py new file mode 100644 index 0000000..5f8bb82 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_txt2po.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python + +from translate.convert import txt2po +from translate.convert import test_convert +from translate.misc import wStringIO +from translate.storage import txt + +class TestTxt2PO: + def txt2po(self, txtsource, template=None): + """helper that converts txt source to po source without requiring files""" + inputfile = wStringIO.StringIO(txtsource) + inputtxt = txt.TxtFile(inputfile) + convertor = txt2po.txt2po() + outputpo = convertor.convertstore(inputtxt) + return outputpo + + def singleelement(self, storage): + """checks that the pofile contains a single non-header element, and returns it""" + print str(storage) + assert len(storage.units) == 1 + return storage.units[0] + + def test_simple(self): + """test the most basic txt conversion""" + txtsource = "A simple string" + poexpected = '''#: :1 +msgid "A simple string" +msgstr "" +''' + poresult = self.txt2po(txtsource) + assert str(poresult.units[1]) == poexpected + + def test_miltiple_units(self): + """test that we can handle txt with multiple units""" + txtsource = """First unit +Still part of first unit + +Second unit is a heading +------------------------ + +Third unit with blank after but no more units. + +""" + poresult = self.txt2po(txtsource) + assert poresult.units[0].isheader() + assert len(poresult.units) == 4 + + def test_carriage_return(self): + """Remove carriage returns from files in dos format.""" + txtsource = '''The rapid expansion of telecommunications infrastructure in recent years has\r +helped to bridge the digital divide to a limited extent.\r +''' + + txtexpected = '''The rapid expansion of telecommunications infrastructure in recent years has +helped to bridge the digital divide to a limited extent.''' + + poresult = self.txt2po(txtsource) + pounit = poresult.units[1] + assert str(pounit.getsource()) == txtexpected + +class TestDoku2po: + def doku2po(self, txtsource, template=None): + """helper that converts dokuwiki source to po source without requiring files.""" + inputfile = wStringIO.StringIO(txtsource) + inputtxt = txt.TxtFile(inputfile, flavour="dokuwiki") + convertor = txt2po.txt2po() + outputpo = convertor.convertstore(inputtxt) + return outputpo + + def singleelement(self, storage): + """checks that the pofile contains a single non-header element, and returns it""" + print str(storage) + assert len(storage.units) == 1 + return storage.units[0] + + def test_basic(self): + """Tests that we can convert some basic things.""" + dokusource = """=====Heading===== + +This is a wiki page. +""" + poresult = self.doku2po(dokusource) + assert poresult.units[0].isheader() + assert len(poresult.units) == 3 + assert poresult.units[1].source == "Heading" + assert poresult.units[2].source == "This is a wiki page." + + def test_bullets(self): + """Tests that we can convert some basic things.""" + dokusource = """ * This is a fact. + * This is a fact. +""" + poresult = self.doku2po(dokusource) + assert poresult.units[0].isheader() + assert len(poresult.units) == 3 + assert poresult.units[1].source == "This is a fact." + assert poresult.units[2].source == "This is a fact." + + def test_numbers(self): + """Tests that we can convert some basic things.""" + dokusource = """ - This is an item. + - This is an item. +""" + poresult = self.doku2po(dokusource) + assert poresult.units[0].isheader() + assert len(poresult.units) == 3 + assert poresult.units[1].source == "This is an item." + assert poresult.units[2].source == "This is an item." + + def test_spacing(self): + """Tests that we can convert some basic things.""" + dokusource = """ ===== Heading ===== + * This is an item. + * This is a subitem. + * This is a tabbed item. +""" + poresult = self.doku2po(dokusource) + assert poresult.units[0].isheader() + assert len(poresult.units) == 5 + assert poresult.units[1].source == "Heading" + assert poresult.units[2].source == "This is an item." + assert poresult.units[3].source == "This is a subitem." + assert poresult.units[4].source == "This is a tabbed item." + +class TestTxt2POCommand(test_convert.TestConvertCommand, TestTxt2PO): + """Tests running actual txt2po commands on files""" + convertmodule = txt2po + defaultoptions = {"progress": "none"} + + def test_help(self): + """tests getting help""" + options = test_convert.TestConvertCommand.test_help(self) + options = self.help_check(options, "-P, --pot") + options = self.help_check(options, "--duplicates") + options = self.help_check(options, "--encoding") + options = self.help_check(options, "--flavour", last=True) diff --git a/translate-toolkit-1.5.1/translate/convert/test_xliff2po.py b/translate-toolkit-1.5.1/translate/convert/test_xliff2po.py new file mode 100644 index 0000000..7de6c74 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/test_xliff2po.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python + +from translate.convert import xliff2po +from translate.misc import wStringIO + +class TestXLIFF2PO: + xliffskeleton = '''<?xml version="1.0" ?> +<xliff version="1.1" xmlns="urn:oasis:names:tc:xliff:document:1.1"> + <file original="filename.po" source-language="en-US" datatype="po"> + <body> + %s + </body> + </file> +</xliff>''' + + def xliff2po(self, xliffsource): + """helper that converts xliff source to po source without requiring files""" + inputfile = wStringIO.StringIO(xliffsource) + convertor = xliff2po.xliff2po() + outputpo = convertor.convertstore(inputfile) + print "The generated po:" + print type(outputpo) + print str(outputpo) + return outputpo + + def test_minimal(self): + minixlf = self.xliffskeleton % '''<trans-unit> + <source>red</source> + <target>rooi</target> + </trans-unit>''' + pofile = self.xliff2po(minixlf) + assert len(pofile.units) == 1 + assert pofile.translate("red") == "rooi" + assert pofile.translate("bla") is None + + def test_basic(self): + headertext = '''Project-Id-Version: program 2.1-branch +Report-Msgid-Bugs-To: +POT-Creation-Date: 2006-01-09 07:15+0100 +PO-Revision-Date: 2004-03-30 17:02+0200 +Last-Translator: Zuza Software Foundation <xxx@translate.org.za> +Language-Team: Afrikaans <translate-discuss-xxx@lists.sourceforge.net> +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit''' + + minixlf = (self.xliffskeleton % '''<trans-unit id="1" restype="x-gettext-domain-header" approved="no" xml:space="preserve"> + <source>%s</source> + <target>%s</target> + <note from="po-translator">Zulu translation of program ABC</note> + </trans-unit> + <trans-unit> + <source>gras</source> + <target>utshani</target> + </trans-unit>''') % (headertext, headertext) + + print minixlf + pofile = self.xliff2po(minixlf) + assert pofile.translate("gras") == "utshani" + assert pofile.translate("bla") is None + potext = str(pofile) + assert potext.index('# Zulu translation of program ABC') == 0 + assert potext.index('msgid "gras"\n') + assert potext.index('msgstr "utshani"\n') + assert potext.index('MIME-Version: 1.0\\n') + + def test_translatorcomments(self): + """Tests translator comments""" + minixlf = self.xliffskeleton % '''<trans-unit> + <source>nonsense</source> + <target>matlhapolosa</target> + <context-group name="po-entry" purpose="information"> + <context context-type="x-po-trancomment">Couldn't do +it</context> + </context-group> + <note from="po-translator">Couldn't do +it</note> +</trans-unit>''' + pofile = self.xliff2po(minixlf) + assert pofile.translate("nonsense") == "matlhapolosa" + assert pofile.translate("bla") is None + unit = pofile.units[0] + assert unit.getnotes("translator") == "Couldn't do it" + potext = str(pofile) + assert potext.index("# Couldn't do it\n") >= 0 + + minixlf = self.xliffskeleton % '''<trans-unit xml:space="preserve"> + <source>nonsense</source> + <target>matlhapolosa</target> + <context-group name="po-entry" purpose="information"> + <context context-type="x-po-trancomment">Couldn't do +it</context> + </context-group> + <note from="po-translator">Couldn't do +it</note> +</trans-unit>''' + pofile = self.xliff2po(minixlf) + assert pofile.translate("nonsense") == "matlhapolosa" + assert pofile.translate("bla") is None + unit = pofile.units[0] + assert unit.getnotes("translator") == "Couldn't do\nit" + potext = str(pofile) + assert potext.index("# Couldn't do\n# it\n") >= 0 + + def test_autocomment(self): + """Tests automatic comments""" + minixlf = self.xliffskeleton % '''<trans-unit> + <source>nonsense</source> + <target>matlhapolosa</target> + <context-group name="po-entry" purpose="information"> + <context context-type="x-po-autocomment">Note that this is +garbage</context> + </context-group> + <note from="developer">Note that this is +garbage</note> +</trans-unit>''' + pofile = self.xliff2po(minixlf) + assert pofile.translate("nonsense") == "matlhapolosa" + assert pofile.translate("bla") is None + unit = pofile.units[0] + assert unit.getnotes("developer") == "Note that this is garbage" + potext = str(pofile) + assert potext.index("#. Note that this is garbage\n") >= 0 + + minixlf = self.xliffskeleton % '''<trans-unit xml:space="preserve"> + <source>nonsense</source> + <target>matlhapolosa</target> + <context-group name="po-entry" purpose="information"> + <context context-type="x-po-autocomment">Note that this is +garbage</context> + </context-group> + <note from="developer">Note that this is +garbage</note> +</trans-unit>''' + pofile = self.xliff2po(minixlf) + assert pofile.translate("nonsense") == "matlhapolosa" + assert pofile.translate("bla") is None + unit = pofile.units[0] + assert unit.getnotes("developer") == "Note that this is\ngarbage" + potext = str(pofile) + assert potext.index("#. Note that this is\n#. garbage\n") >= 0 + + def test_locations(self): + """Tests location comments (#:)""" + minixlf = self.xliffskeleton % '''<trans-unit id="1"> + <source>nonsense</source> + <target>matlhapolosa</target> + <context-group name="po-reference" purpose="location"> + <context context-type="sourcefile">example.c</context> + <context context-type="linenumber">123</context> + </context-group> + <context-group name="po-reference" purpose="location"> + <context context-type="sourcefile">place.py</context> + </context-group> +</trans-unit>''' + pofile = self.xliff2po(minixlf) + assert pofile.translate("nonsense") == "matlhapolosa" + assert pofile.translate("bla") is None + unit = pofile.units[0] + locations = unit.getlocations() + assert len(locations) == 2 + assert "example.c:123" in locations + assert "place.py" in locations + + def test_fuzzy(self): + """Tests fuzzyness""" + minixlf = self.xliffskeleton % '''<trans-unit approved="no"> + <source>book</source> + </trans-unit> + <trans-unit id="2" approved="yes"> + <source>nonsense</source> + <target>matlhapolosa</target> + </trans-unit> + <trans-unit id="2" approved="no"> + <source>verb</source> + <target state="needs-review-translation">lediri</target> + </trans-unit>''' + pofile = self.xliff2po(minixlf) + assert pofile.translate("nonsense") == "matlhapolosa" + assert pofile.translate("verb") == "lediri" + assert pofile.translate("book") is None + assert pofile.translate("bla") is None + assert len(pofile.units) == 3 + #TODO: decide if this one should be fuzzy: + #assert pofile.units[0].isfuzzy() + assert not pofile.units[1].isfuzzy() + assert pofile.units[2].isfuzzy() + + def test_plurals(self): + """Tests fuzzyness""" + minixlf = self.xliffskeleton % '''<group id="1" restype="x-gettext-plurals"> + <trans-unit id="1[0]" xml:space="preserve"> + <source>cow</source> + <target>inkomo</target> + </trans-unit> + <trans-unit id="1[1]" xml:space="preserve"> + <source>cows</source> + <target>iinkomo</target> + </trans-unit> +</group>''' + pofile = self.xliff2po(minixlf) + print str(pofile) + potext = str(pofile) + assert len(pofile.units) == 1 + assert potext.index('msgid_plural "cows"') + assert potext.index('msgstr[0] "inkomo"') + assert potext.index('msgstr[1] "iinkomo"') + + +class TestBasicXLIFF2PO(TestXLIFF2PO): + """This tests a basic XLIFF file without xmlns attribute""" + + xliffskeleton = '''<?xml version="1.0" ?> +<xliff version="1.1"> + <file original="filename.po" source-language="en-US" datatype="po"> + <body> + %s + </body> + </file> +</xliff>''' diff --git a/translate-toolkit-1.5.1/translate/convert/tiki2po b/translate-toolkit-1.5.1/translate/convert/tiki2po new file mode 100644 index 0000000..56a5c44 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/tiki2po @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Copyright 2008 Mozilla Corporation, Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +"""simple script to convert a TikiWiki style language.php file to a gettext .po localization file""" + +from translate.convert import tiki2po + +if __name__ == '__main__': + tiki2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/tiki2po.py b/translate-toolkit-1.5.1/translate/convert/tiki2po.py new file mode 100644 index 0000000..c9f768f --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/tiki2po.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2008 Mozilla Corporation, Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" Convert TikiWiki's language.php files to GetText PO files. """ + +import re +import sys +from translate.storage import tiki +from translate.storage import po + +class tiki2po: + def __init__(self, includeunused=False): + """ + @param includeunused: On conversion, should the "unused" section be preserved? Default: False + """ + self.includeunused = includeunused + + def convertstore(self, thetikifile): + """Converts a given (parsed) tiki file to a po file. + + @param thetikifile: a tikifile pre-loaded with input data + """ + thetargetfile = po.pofile() + + # Set up the header + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit") + thetargetfile.addunit(targetheader) + + # For each lang unit, make the new po unit accordingly + for unit in thetikifile.units: + if not self.includeunused and "unused" in unit.getlocations(): + continue + newunit = po.pounit() + newunit.source = unit.source + newunit.settarget(unit.target) + locations = unit.getlocations() + if locations: + newunit.addlocations(locations) + thetargetfile.addunit(newunit) + return thetargetfile + +def converttiki(inputfile, outputfile, template=None, includeunused=False): + """Converts from tiki file format to po. + + @param inputfile: file handle of the source + @param outputfile: file handle to write to + @param template: unused + @param includeunused: Include the "usused" section of the tiki file? Default: False + """ + convertor = tiki2po(includeunused=includeunused) + inputstore = tiki.TikiStore(inputfile) + outputstore = convertor.convertstore(inputstore) + if outputstore.isempty(): + return False + outputfile.write(str(outputstore)) + return True + +def main(argv=None): + """Converts tiki .php files to .po.""" + from translate.convert import convert + from translate.misc import stdiotell + sys.stdout = stdiotell.StdIOWrapper(sys.stdout) + + formats = {"php":("po",converttiki)} + + parser = convert.ConvertOptionParser(formats, description=__doc__) + parser.add_option("", "--include-unused", dest="includeunused", action="store_true", default=False, help="Include strings in the unused section") + parser.passthrough.append("includeunused") + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/ts2po b/translate-toolkit-1.5.1/translate/convert/ts2po new file mode 100755 index 0000000..4837430 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/ts2po @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts Qt .ts localization files to Gettext .po files +You can convert back to .ts using po2ts""" + +from translate.convert import ts2po + +if __name__ == '__main__': + ts2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/ts2po.py b/translate-toolkit-1.5.1/translate/convert/ts2po.py new file mode 100644 index 0000000..372f219 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/ts2po.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert Qt Linguist (.ts) files to Gettext PO localization files + +See: http://translate.sourceforge.net/wiki/toolkit/ts2po for examples and +usage instructions +""" + + +from translate.storage import po +from translate.storage import ts + +class ts2po: + def __init__(self, duplicatestyle="msgctxt"): + self.duplicatestyle = duplicatestyle + + def convertmessage(self, contextname, messagenum, source, target, msgcomments, transtype): + """makes a pounit from the given message""" + thepo = po.pounit(encoding="UTF-8") + thepo.addlocation("%s#%d" % (contextname, messagenum)) + thepo.source = source + thepo.target = target + if len(msgcomments) > 0: + thepo.addnote(msgcomments) + if transtype == "unfinished" and thepo.istranslated(): + thepo.markfuzzy() + if transtype == "obsolete": + # This should use the Gettext obsolete method but it would require quite a bit of work + thepo.addnote("(obsolete)", origin="developer") + # using the fact that -- quote -- "(this is nonsense)" + return thepo + + def convertfile(self, inputfile): + """converts a .ts file to .po format""" + tsfile = ts.QtTsParser(inputfile) + thetargetfile = po.pofile() + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit") + thetargetfile.addunit(targetheader) + for contextname, messages in tsfile.iteritems(): + messagenum = 0 + for message in messages: + messagenum += 1 + source = tsfile.getmessagesource(message) + translation = tsfile.getmessagetranslation(message) + comment = tsfile.getmessagecomment(message) + transtype = tsfile.getmessagetype(message) + thepo = self.convertmessage(contextname, messagenum, source, translation, comment, transtype) + thetargetfile.addunit(thepo) + thetargetfile.removeduplicates(self.duplicatestyle) + return thetargetfile + +def convertts(inputfile, outputfile, templates, duplicatestyle="msgctxt"): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + convertor = ts2po(duplicatestyle=duplicatestyle) + outputstore = convertor.convertfile(inputfile) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"ts":("po", convertts)} + parser = convert.ConvertOptionParser(formats, usepots=True, description=__doc__) + parser.add_duplicates_option() + parser.run(argv) + diff --git a/translate-toolkit-1.5.1/translate/convert/txt2po b/translate-toolkit-1.5.1/translate/convert/txt2po new file mode 100755 index 0000000..6df4f44 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/txt2po @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts plain text files to Gettext .po files +You can convert back to .txt using po2txt""" + +from translate.convert import txt2po + +if __name__ == '__main__': + txt2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/txt2po.py b/translate-toolkit-1.5.1/translate/convert/txt2po.py new file mode 100644 index 0000000..d16ab2a --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/txt2po.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert plain text (.txt) files to Gettext PO localization files + +See: http://translate.sourceforge.net/wiki/toolkit/txt2po for examples and +usage instructions +""" + +from translate.storage import txt +from translate.storage import po + +class txt2po: + def __init__(self, duplicatestyle="msgctxt"): + self.duplicatestyle = duplicatestyle + + def convertstore(self, thetxtfile): + """converts a file to .po format""" + thetargetfile = po.pofile() + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit") + targetheader.addnote("extracted from %s" % thetxtfile.filename, "developer") + thetargetfile.addunit(targetheader) + for txtunit in thetxtfile.units: + newunit = thetargetfile.addsourceunit(txtunit.source) + newunit.addlocations(txtunit.getlocations()) + thetargetfile.removeduplicates(self.duplicatestyle) + return thetargetfile + +def converttxt(inputfile, outputfile, templates, duplicatestyle="msgctxt", encoding="utf-8", flavour=None): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + inputstore = txt.TxtFile(inputfile, encoding=encoding, flavour=flavour) + convertor = txt2po(duplicatestyle=duplicatestyle) + outputstore = convertor.convertstore(inputstore) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + from translate.misc import stdiotell + import sys + sys.stdout = stdiotell.StdIOWrapper(sys.stdout) + formats = {"txt":("po", converttxt), "*":("po", converttxt)} + parser = convert.ConvertOptionParser(formats, usepots=True, description=__doc__) + parser.add_option("", "--encoding", dest="encoding", default='utf-8', type="string", + help="The encoding of the input file (default: UTF-8)") + parser.passthrough.append("encoding") + parser.add_option("", "--flavour", dest="flavour", default="plain", + type="choice", choices=["plain", "dokuwiki", "mediawiki"], + help="The flavour of text file: plain (default), dokuwiki, mediawiki", + metavar="FLAVOUR") + parser.passthrough.append("flavour") + parser.add_duplicates_option() + parser.run(argv) + diff --git a/translate-toolkit-1.5.1/translate/convert/web2py2po b/translate-toolkit-1.5.1/translate/convert/web2py2po new file mode 100755 index 0000000..7eaa514 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/web2py2po @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2009 Zuza Software Foundation +# +# This file is part of translate. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""convert web2py translation dictionaries (.py) to GNU/gettext PO files""" + +from translate.convert import web2py2po + +if __name__ == '__main__': + web2py2po.main() diff --git a/translate-toolkit-1.5.1/translate/convert/web2py2po.py b/translate-toolkit-1.5.1/translate/convert/web2py2po.py new file mode 100644 index 0000000..91da183 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/web2py2po.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2009 Zuza Software Foundation +# +# This file is part of translate. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# (c) 2009 Dominic König (dominic@nursix.org) +# + +"""convert web2py translation dictionaries (.py) to GNU/gettext PO files""" + +import sys +from translate.storage import po + +class web2py2po: + def __init__(self, pofile=None): + self.mypofile = pofile + + def convertunit(self, source_str, target_str): + pounit = po.pounit(encoding="UTF-8") + pounit.setsource( source_str ) + if target_str: + pounit.settarget( target_str ) + return pounit + + def convertstore(self, mydict): + + targetheader = self.mypofile.makeheader(charset="UTF-8", encoding="8bit") + targetheader.addnote("extracted from web2py", "developer") + + self.mypofile.addunit(targetheader) + + for source_str in mydict.keys(): + target_str = mydict[source_str] + if target_str.startswith('*** '): + target_str = '' + pounit = self.convertunit(source_str, target_str) + self.mypofile.addunit(pounit) + + return self.mypofile + +def convertpy(inputfile, outputfile, encoding="UTF-8"): + + new_pofile = po.pofile() + convertor = web2py2po(new_pofile) + + mydict = eval(inputfile.read()) + if not isinstance(mydict, dict): + return 0 + + outputstore = convertor.convertstore(mydict) + + if outputstore.isempty(): + return 0 + + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {("py", "po"): ("po", convertpy), ("py", None): ("po", convertpy)} + parser = convert.ConvertOptionParser(formats, usetemplates=False, description=__doc__) + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/xliff2odf b/translate-toolkit-1.5.1/translate/convert/xliff2odf new file mode 100755 index 0000000..02c28ee --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/xliff2odf @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# +# Copyright 2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Takes an ODF template file and an XLIFF file containing translations of +strings in the ODF template. It creates a new ODF file using the translations +of the XLIFF file. + +Files supported include: +- .odt (Wordprocessor documents) +- .ods (Spreadsheets) +- .odp (Presentations) +- .sxw (old OpenOffice.org 1.0 Wordprocessor documents) +""" + +from translate.convert import xliff2odf + +if __name__ == '__main__': + xliff2odf.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/xliff2odf.py b/translate-toolkit-1.5.1/translate/convert/xliff2odf.py new file mode 100644 index 0000000..9f562d6 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/xliff2odf.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert OpenDocument (ODF) files to Gettext PO localization files""" + +import cStringIO +import zipfile +import re + +import lxml.etree as etree + +from translate.storage import factory +from translate.storage.xml_extract import unit_tree +from translate.storage.xml_extract import extract +from translate.storage.xml_extract import generate +from translate.storage import odf_shared, odf_io + +def first_child(unit_node): + return unit_node.children.values()[0] + +def translate_odf(template, input_file): + def load_dom_trees(template): + odf_data = odf_io.open_odf(template) + return dict((filename, etree.parse(cStringIO.StringIO(data))) for filename, data in odf_data.iteritems()) + + def load_unit_tree(input_file, dom_trees): + store = factory.getobject(input_file) + tree = unit_tree.build_unit_tree(store) + + def extract_unit_tree(filename, root_dom_element_name): + """Find the subtree in 'tree' which corresponds to the data in XML file 'filename'""" + def get_tree(): + try: + return tree.children['office:%s' % root_dom_element_name, 0] + except KeyError: + return unit_tree.XPathTree() + return (filename, get_tree()) + + return dict([extract_unit_tree('content.xml', 'document-content'), + extract_unit_tree('meta.xml', 'document-meta'), + extract_unit_tree('styles.xml', 'document-styles')]) + + def translate_dom_trees(unit_trees, dom_trees): + make_parse_state = lambda: extract.ParseState(odf_shared.no_translate_content_elements, odf_shared.inline_elements) + for filename, dom_tree in dom_trees.iteritems(): + file_unit_tree = unit_trees[filename] + generate.apply_translations(dom_tree.getroot(), file_unit_tree, generate.replace_dom_text(make_parse_state)) + return dom_trees + + # Since the convertoptionsparser will give us an open file, we risk that + # it could have been opened in non-binary mode on Windows, and then we'll + # have problems, so let's make sure we have what we want. + template.close() + template = file(template.name, mode='rb') + dom_trees = load_dom_trees(template) + unit_trees = load_unit_tree(input_file, dom_trees) + return translate_dom_trees(unit_trees, dom_trees) + +def write_odf(xlf_data, template, output_file, dom_trees): + def write_content_to_odf(output_zip, dom_trees): + for filename, dom_tree in dom_trees.iteritems(): + output_zip.writestr(filename, etree.tostring(dom_tree, encoding='UTF-8', xml_declaration=True)) + + # Since the convertoptionsparser will give us an open file, we risk that + # it could have been opened in non-binary mode on Windows, and then we'll + # have problems, so let's make sure we have what we want. + template.close() + template = file(template.name, mode='rb') + template_zip = zipfile.ZipFile(template, 'r') + output_file.close() + output_file = file(output_file.name, mode='wb') + output_zip = zipfile.ZipFile(output_file, 'w', compression=zipfile.ZIP_DEFLATED) + output_zip = odf_io.copy_odf(template_zip, output_zip, dom_trees.keys() + ['META-INF/manifest.xml']) + output_zip = odf_io.add_file(output_zip, template_zip.read('META-INF/manifest.xml'), 'translation.xlf', xlf_data) + write_content_to_odf(output_zip, dom_trees) + +def convertxliff(input_file, output_file, template): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + xlf_data = input_file.read() + dom_trees = translate_odf(template, cStringIO.StringIO(xlf_data)) + write_odf(xlf_data, template, output_file, dom_trees) + output_file.close() + return True + +def main(argv=None): + from translate.convert import convert + formats = {"xlf": ("odt", convertxliff), # Text + "xlf": ("ods", convertxliff), # Spreadsheet + "xlf": ("odp", convertxliff), # Presentation + "xlf": ("odg", convertxliff), # Drawing + "xlf": ("odc", convertxliff), # Chart + "xlf": ("odf", convertxliff), # Formula + "xlf": ("odi", convertxliff), # Image + "xlf": ("odm", convertxliff), # Master Document + "xlf": ("ott", convertxliff), # Text template + "xlf": ("ots", convertxliff), # Spreadsheet template + "xlf": ("otp", convertxliff), # Presentation template + "xlf": ("otg", convertxliff), # Drawing template + "xlf": ("otc", convertxliff), # Chart template + "xlf": ("otf", convertxliff), # Formula template + "xlf": ("oti", convertxliff), # Image template + "xlf": ("oth", convertxliff), # Web page template + } + + parser = convert.ConvertOptionParser(formats, usetemplates=True, description=__doc__) + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/xliff2oo b/translate-toolkit-1.5.1/translate/convert/xliff2oo new file mode 100755 index 0000000..344cedb --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/xliff2oo @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# +# Copyright 2002-2004 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""script that converts a .po file with translations based on a .pot file +generated from a OpenOffice localization .oo back to the .oo (but translated) +Uses the original .oo to do the conversion as this makes sure we don't +leave out any unincluded stuff...""" + +from translate.convert import xliff2oo + +if __name__ == '__main__': + xliff2oo.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/xliff2oo.py b/translate-toolkit-1.5.1/translate/convert/xliff2oo.py new file mode 100644 index 0000000..67a1d73 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/xliff2oo.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2004-2006 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""convert XLIFF localization files to an OpenOffice.org (SDF) localization file""" + +import sys +import os +from translate.storage import oo +from translate.storage import factory +from translate.filters import pofilter +from translate.filters import checks +from translate.filters import autocorrect +import time + +class reoo: + def __init__(self, templatefile, languages=None, timestamp=None, includefuzzy=False, long_keys=False, filteraction="exclude"): + """construct a reoo converter for the specified languages (timestamp=0 means leave unchanged)""" + # languages is a pair of language ids + self.long_keys = long_keys + self.readoo(templatefile) + self.languages = languages + self.filteraction = filteraction + if timestamp is None: + self.timestamp = time.strptime("2002-02-02 02:02:02", "%Y-%m-%d %H:%M:%S") + else: + self.timestamp = timestamp + if self.timestamp: + self.timestamp_str = time.strftime("%Y-%m-%d %H:%M:%S", self.timestamp) + else: + self.timestamp_str = None + self.includefuzzy = includefuzzy + + def makeindex(self): + """makes an index of the oo keys that are used in the source file""" + self.index = {} + for ookey, theoo in self.o.ookeys.iteritems(): + sourcekey = oo.makekey(ookey, self.long_keys) + self.index[sourcekey] = theoo + + def readoo(self, of): + """read in the oo from the file""" + oosrc = of.read() + self.o = oo.oofile() + self.o.parse(oosrc) + self.makeindex() + + def handleunit(self, unit): + # TODO: make this work for multiple columns in oo... + locations = unit.getlocations() + # technically our formats should just have one location for each entry... + # but we handle multiple ones just to be safe... + for location in locations: + subkeypos = location.rfind('.') + subkey = location[subkeypos+1:] + key = location[:subkeypos] + # this is just to handle our old system of using %s/%s:%s instead of %s/%s#%s + key = key.replace(':', '#') + # this is to handle using / instead of \ in the sourcefile... + key = key.replace('\\', '/') + key = oo.normalizefilename(key) + if self.index.has_key(key): + # now we need to replace the definition of entity with msgstr + theoo = self.index[key] # find the oo + self.applytranslation(key, subkey, theoo, unit) + else: + print >> sys.stderr, "couldn't find key %s from po in %d keys" % (key, len(self.index)) + try: + sourceunitlines = str(unit) + if isinstance(sourceunitlines, unicode): + sourceunitlines = sourceunitlines.encode("utf-8") + print >> sys.stderr, sourceunitlines + except: + print >> sys.stderr, "error outputting source unit %r" % (str(unit),) + + def applytranslation(self, key, subkey, theoo, unit): + """applies the translation from the source unit to the oo unit""" + if not self.includefuzzy and unit.isfuzzy(): + return + makecopy = False + if self.languages is None: + part1 = theoo.lines[0] + if len(theoo.lines) > 1: + part2 = theoo.lines[1] + else: + makecopy = True + else: + part1 = theoo.languages[self.languages[0]] + if self.languages[1] in theoo.languages: + part2 = theoo.languages[self.languages[1]] + else: + makecopy = True + if makecopy: + part2 = oo.ooline(part1.getparts()) + unquotedid = unit.source + unquotedstr = unit.target + # If there is no translation, we don't want to add a line + if len(unquotedstr.strip()) == 0: + return + if isinstance(unquotedstr, unicode): + unquotedstr = unquotedstr.encode("UTF-8") + # finally set the new definition in the oo, but not if its empty + if len(unquotedstr) > 0: + subkey = subkey.strip() + setattr(part2, subkey, unquotedstr) + # set the modified time + if self.timestamp_str: + part2.timestamp = self.timestamp_str + if self.languages: + part2.languageid = self.languages[1] + if makecopy: + theoo.addline(part2) + + def convertstore(self, sourcestore): + self.p = sourcestore + # translate the strings + for unit in self.p.units: + # there may be more than one element due to msguniq merge + if filter.validelement(unit, self.p.filename, self.filteraction): + self.handleunit(unit) + # return the modified oo file object + return self.o + +def getmtime(filename): + import stat + return time.localtime(os.stat(filename)[stat.ST_MTIME]) + +class oocheckfilter(pofilter.pocheckfilter): + def validelement(self, unit, filename, filteraction): + """Returns whether or not to use unit in conversion. (filename is just for error reporting)""" + if filteraction == "none": return True + filterresult = self.filterunit(unit) + if filterresult: + if filterresult != autocorrect: + for filtername, filtermessage in filterresult.iteritems(): + location = unit.getlocations()[0] + if filtername in self.options.error: + print >> sys.stderr, "Error at %s::%s: %s" % (filename, location, filtermessage) + return not filteraction in ["exclude-all", "exclude-serious"] + if filtername in self.options.warning or self.options.alwayswarn: + print >> sys.stderr, "Warning at %s::%s: %s" % (filename, location, filtermessage) + return not filteraction in ["exclude-all"] + return True + +class oofilteroptions: + error = ['variables', 'xmltags', 'escapes'] + warning = ['blank'] + #To only issue warnings for tests listed in warning, change the following to False: + alwayswarn = True + limitfilters = error + warning + #To use all available tests, uncomment the following: + #limitfilters = [] + #To exclude certain tests, list them in here: + excludefilters = {} + includefuzzy = False + includereview = False + includeheader = False + autocorrect = False + +options = oofilteroptions() +filter = oocheckfilter(options, [checks.OpenOfficeChecker, checks.StandardUnitChecker], checks.openofficeconfig) + +def convertoo(inputfile, outputfile, templatefile, sourcelanguage=None, targetlanguage=None, timestamp=None, includefuzzy=False, multifilestyle="single", filteraction=None): + inputstore = factory.getobject(inputfile) + inputstore.filename = getattr(inputfile, 'name', '') + if not targetlanguage: + raise ValueError("You must specify the target language") + if not sourcelanguage: + if targetlanguage.isdigit(): + sourcelanguage = "01" + else: + sourcelanguage = "en-US" + languages = (sourcelanguage, targetlanguage) + if templatefile is None: + raise ValueError("must have template file for oo files") + else: + convertor = reoo(templatefile, languages=languages, timestamp=timestamp, includefuzzy=includefuzzy, long_keys=multifilestyle != "single", filteraction=filteraction) + outputstore = convertor.convertstore(inputstore) + # TODO: check if we need to manually delete missing items + outputfile.write(str(outputstore)) + return True + +def main(argv=None): + from translate.convert import convert + formats = {("po", "oo"):("oo", convertoo), ("xlf", "oo"):("oo", convertoo), ("xlf", "sdf"):("sdf", convertoo)} + # always treat the input as an archive unless it is a directory + archiveformats = {(None, "output"): oo.oomultifile, (None, "template"): oo.oomultifile} + parser = convert.ArchiveConvertOptionParser(formats, usetemplates=True, description=__doc__, archiveformats=archiveformats) + parser.add_option("-l", "--language", dest="targetlanguage", default=None, + help="set target language code (e.g. af-ZA) [required]", metavar="LANG") + parser.add_option("", "--source-language", dest="sourcelanguage", default=None, + help="set source language code (default en-US)", metavar="LANG") + parser.add_option("-T", "--keeptimestamp", dest="timestamp", default=None, action="store_const", const=0, + help="don't change the timestamps of the strings") + parser.add_option("", "--nonrecursiveoutput", dest="allowrecursiveoutput", default=True, action="store_false", help="don't treat the output oo as a recursive store") + parser.add_option("", "--nonrecursivetemplate", dest="allowrecursivetemplate", default=True, action="store_false", help="don't treat the template oo as a recursive store") + parser.add_option("", "--filteraction", dest="filteraction", default="none", metavar="ACTION", + help="action on pofilter failure: none (default), warn, exclude-serious, exclude-all") + parser.add_fuzzy_option() + parser.add_multifile_option() + parser.passthrough.append("sourcelanguage") + parser.passthrough.append("targetlanguage") + parser.passthrough.append("timestamp") + parser.passthrough.append("filteraction") + parser.run(argv) + +if __name__ == '__main__': + main() diff --git a/translate-toolkit-1.5.1/translate/convert/xliff2po b/translate-toolkit-1.5.1/translate/convert/xliff2po new file mode 100755 index 0000000..2a89eee --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/xliff2po @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# +# Copyright 2005 Zuza Software Foundation +# +# This file is part of translate. +# +# translate is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# translate is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with translate; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Converts .xliff localization files to Gettext .po files +You can convert back to .xliff using po2xliff""" + +from translate.convert import xliff2po + +if __name__ == '__main__': + xliff2po.main() + diff --git a/translate-toolkit-1.5.1/translate/convert/xliff2po.py b/translate-toolkit-1.5.1/translate/convert/xliff2po.py new file mode 100644 index 0000000..7d38575 --- /dev/null +++ b/translate-toolkit-1.5.1/translate/convert/xliff2po.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2002-2009 Zuza Software Foundation +# +# This file is part of the Translate Toolkit. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +"""Convert XLIFF localization files to Gettext PO localization files. + +see: http://translate.sourceforge.net/wiki/toolkit/xliff2po for examples and +usage instructions. +""" + +from translate.storage import po +from translate.storage import xliff +from translate.misc import wStringIO + +class xliff2po: + def converttransunit(self, transunit): + """makes a pounit from the given transunit""" + thepo = po.pounit() + + #Header + if transunit.getrestype() == "x-gettext-domain-header": + thepo.source = "" + else: + thepo.source = transunit.source + thepo.target = transunit.target + + #Location comments + locations = transunit.getlocations() + if locations: + thepo.addlocations(locations) + + #NOTE: Supporting both <context> and <note> tags in xliff files for comments + #Translator comments + trancomments = transunit.getnotes("translator") + if trancomments: + thepo.addnote(trancomments, origin="translator") + + #Automatic and Developer comments + autocomments = transunit.getnotes("developer") + if autocomments: + thepo.addnote(autocomments, origin="developer") + + #See 5.6.1 of the spec. We should not check fuzzyness, but approved attribute + if transunit.isfuzzy(): + thepo.markfuzzy(True) + + return thepo + + def convertstore(self, inputfile): + """Converts a .xliff file to .po format""" + # XXX: The inputfile is converted to string because Pootle supplies + # XXX: a PootleFile object as input which cannot be sent to PoXliffFile. + # XXX: The better way would be to have a consistent conversion API. + if not isinstance(inputfile, (file, wStringIO.StringIO)): + inputfile = str(inputfile) + XliffFile = xliff.xlifffile.parsestring(inputfile) + thetargetfile = po.pofile() + targetheader = thetargetfile.makeheader(charset="UTF-8", encoding="8bit") + # TODO: support multiple files + for transunit in XliffFile.units: + thepo = self.converttransunit(transunit) + thetargetfile.addunit(thepo) + return thetargetfile + +def convertxliff(inputfile, outputfile, templates): + """reads in stdin using fromfileclass, converts using convertorclass, writes to stdout""" + convertor = xliff2po() + outputstore = convertor.convertstore(inputfile) + if outputstore.isempty(): + return 0 + outputfile.write(str(outputstore)) + return 1 + +def main(argv=None): + from translate.convert import convert + formats = {"xlf":("po", convertxliff)} + parser = convert.ConvertOptionParser(formats, usepots=True, description=__doc__) + parser.run(argv) |