Package translate :: Package convert :: Module pot2po
[hide private]
[frames] | no frames]

Source Code for Module translate.convert.pot2po

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright 2004-2009 Zuza Software Foundation 
  5  # 
  6  # This file is part of translate. 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, see <http://www.gnu.org/licenses/>. 
 20   
 21  """Convert template files (like .pot or template .xlf files) translation files, 
 22  preserving existing translations. 
 23   
 24  See: http://translate.sourceforge.net/wiki/toolkit/pot2po for examples and 
 25  usage instructions. 
 26  """ 
 27   
 28  from translate.storage import factory 
 29  from translate.search import match 
 30  from translate.misc.multistring import multistring 
 31  from translate.tools import pretranslate 
 32  from translate.storage import poheader 
 33  from translate.storage import catkeys 
 34   
 35   
36 -def convertpot(input_file, output_file, template_file, tm=None, min_similarity=75, fuzzymatching=True, classes=factory.classes, **kwargs):
37 """Main conversion function""" 38 39 input_store = factory.getobject(input_file, classes=classes) 40 template_store = None 41 if template_file is not None: 42 template_store = factory.getobject(template_file, classes=classes) 43 output_store = convert_stores(input_store, template_store, tm, min_similarity, fuzzymatching, **kwargs) 44 output_file.write(str(output_store)) 45 return 1
46
47 -def convert_stores(input_store, template_store, tm=None, min_similarity=75, fuzzymatching=True, **kwargs):
48 """Actual conversion function, works on stores not files, returns 49 a properly initialized pretranslated output store, with structure 50 based on input_store, metadata based on template_store, migrates 51 old translations from template_store and pretranslating from tm""" 52 53 #prepare for merging 54 output_store = type(input_store)() 55 #create fuzzy matchers to be used by pretranslate.pretranslate_unit 56 matchers = [] 57 _prepare_merge(input_store, output_store, template_store) 58 if fuzzymatching: 59 if template_store: 60 matcher = match.matcher(template_store, max_candidates=1, min_similarity=min_similarity, max_length=3000, usefuzzy=True) 61 matcher.addpercentage = False 62 matchers.append(matcher) 63 if tm: 64 matcher = pretranslate.memory(tm, max_candidates=1, min_similarity=min_similarity, max_length=1000) 65 matcher.addpercentage = False 66 matchers.append(matcher) 67 68 #initialize store 69 _store_pre_merge(input_store, output_store, template_store) 70 71 # Do matching 72 for input_unit in input_store.units: 73 if input_unit.isheader(): 74 continue 75 if input_unit.istranslatable(): 76 input_unit = pretranslate.pretranslate_unit(input_unit, template_store, matchers, mark_reused=True) 77 _unit_post_merge(input_unit, input_store, output_store, template_store) 78 output_store.addunit(input_unit) 79 80 #finalize store 81 _store_post_merge(input_store, output_store, template_store) 82 83 return output_store
84 85 86 ##dispatchers
87 -def _prepare_merge(input_store, output_store, template_store, **kwargs):
88 """Prepare stores & TM matchers before merging.""" 89 #dispatch to format specific functions 90 prepare_merge_hook = "_prepare_merge_%s" % input_store.__class__.__name__ 91 if globals().has_key(prepare_merge_hook): 92 globals()[prepare_merge_hook](input_store, output_store, template_store, **kwargs) 93 94 #generate an index so we can search by source string and location later on 95 input_store.makeindex() 96 if template_store: 97 template_store.makeindex()
98 99
100 -def _store_pre_merge(input_store, output_store, template_store, **kwargs) :
101 """Initialize the new file with things like headers and metadata.""" 102 #formats that implement poheader interface are a special case 103 if isinstance(input_store, poheader.poheader): 104 _do_poheaders(input_store, output_store, template_store) 105 elif isinstance(input_store, catkeys.CatkeysFile): 106 output_store.header = input_store.header 107 108 #dispatch to format specific functions 109 store_pre_merge_hook = "_store_pre_merge_%s" % input_store.__class__.__name__ 110 if globals().has_key(store_pre_merge_hook): 111 globals()[store_pre_merge_hook](input_store, output_store, template_store, **kwargs)
112 113
114 -def _store_post_merge(input_store, output_store, template_store, **kwargs) :
115 """Close file after merging all translations, used for adding 116 statistics, obsolete messages and similar wrapup tasks.""" 117 #dispatch to format specific functions 118 store_post_merge_hook = "_store_post_merge_%s" % input_store.__class__.__name__ 119 if globals().has_key(store_post_merge_hook): 120 globals()[store_post_merge_hook](input_store, output_store, template_store, **kwargs)
121
122 -def _unit_post_merge(input_unit, input_store, output_store, template_store, **kwargs):
123 """Handle any unit level cleanup and situations not handled by the merge() 124 function.""" 125 #dispatch to format specific functions 126 unit_post_merge_hook = "_unit_post_merge_%s" % input_unit.__class__.__name__ 127 if globals().has_key(unit_post_merge_hook): 128 globals()[unit_post_merge_hook](input_unit, input_store, output_store, template_store, **kwargs)
129 130 131 ##format specific functions
132 -def _prepare_merge_pofile(input_store, output_store, template_store):
133 """PO format specific template preparation logic.""" 134 #we need to revive obsolete units to be able to consider 135 #their translation when matching 136 if template_store: 137 for unit in template_store.units: 138 if unit.isobsolete(): 139 unit.resurrect()
140 141
142 -def _unit_post_merge_pounit(input_unit, input_store, output_store, template_store):
143 """PO format specific plural string initializtion logic.""" 144 #FIXME: do we want to do that for poxliff also? 145 if input_unit.hasplural() and len(input_unit.target) == 0: 146 # untranslated plural unit; Let's ensure that we have the correct number of plural forms: 147 nplurals, plural = output_store.getheaderplural() 148 if nplurals and nplurals.isdigit() and nplurals != '2': 149 input_unit.target = multistring([""]*int(nplurals))
150 151
152 -def _store_post_merge_pofile(input_store, output_store, template_store):
153 """PO format specific: adds newly obsoleted messages to end of store.""" 154 #Let's take care of obsoleted messages 155 if template_store: 156 newlyobsoleted = [] 157 for unit in template_store.units: 158 if not unit.istranslatable() or not unit.istranslated(): 159 continue 160 if unit.target and not (input_store.findid(unit.getid()) or hasattr(unit, "reused")): 161 #not in .pot, make it obsolete 162 unit.makeobsolete() 163 newlyobsoleted.append(unit) 164 elif unit.isobsolete(): 165 output_store.addunit(unit) 166 for unit in newlyobsoleted: 167 output_store.addunit(unit)
168 169
170 -def _do_poheaders(input_store, output_store, template_store):
171 """Adds initialized PO headers to output store.""" 172 # header values 173 charset = "UTF-8" 174 encoding = "8bit" 175 project_id_version = None 176 pot_creation_date = None 177 po_revision_date = None 178 last_translator = None 179 language_team = None 180 mime_version = None 181 plural_forms = None 182 kwargs = {} 183 184 if template_store is not None and isinstance(template_store, poheader.poheader): 185 templateheadervalues = template_store.parseheader() 186 for key, value in templateheadervalues.iteritems(): 187 if key == "Project-Id-Version": 188 project_id_version = value 189 elif key == "Last-Translator": 190 last_translator = value 191 elif key == "Language-Team": 192 language_team = value 193 elif key == "PO-Revision-Date": 194 po_revision_date = value 195 elif key in ("POT-Creation-Date", "MIME-Version"): 196 # don't know how to handle these keys, or ignoring them 197 pass 198 elif key == "Content-Type": 199 kwargs[key] = value 200 elif key == "Content-Transfer-Encoding": 201 encoding = value 202 elif key == "Plural-Forms": 203 plural_forms = value 204 else: 205 kwargs[key] = value 206 207 inputheadervalues = input_store.parseheader() 208 for key, value in inputheadervalues.iteritems(): 209 if key in ("Project-Id-Version", "Last-Translator", "Language-Team", "PO-Revision-Date", "Content-Type", "Content-Transfer-Encoding", "Plural-Forms"): 210 # want to carry these from the template so we ignore them 211 pass 212 elif key == "POT-Creation-Date": 213 pot_creation_date = value 214 elif key == "MIME-Version": 215 mime_version = value 216 else: 217 kwargs[key] = value 218 219 output_header = output_store.init_headers(charset=charset, encoding=encoding, project_id_version=project_id_version, 220 pot_creation_date=pot_creation_date, po_revision_date=po_revision_date, last_translator=last_translator, 221 language_team=language_team, mime_version=mime_version, plural_forms=plural_forms, **kwargs) 222 223 # Get the header comments and fuzziness state 224 225 # initial values from pot file 226 input_header = input_store.header() 227 if input_header is not None: 228 if input_header.getnotes("developer"): 229 output_header.addnote(input_header.getnotes("developer"), origin="developer", position="replace") 230 if input_header.getnotes("translator"): 231 output_header.addnote(input_header.getnotes("translator"), origin="translator", position="replace") 232 output_header.markfuzzy(input_header.isfuzzy()) 233 234 # override some values from input file 235 if template_store is not None: 236 template_header = template_store.header() 237 if template_header is not None: 238 if template_header.getnotes("translator"): 239 output_header.addnote(template_header.getnotes("translator"), "translator", position="replace") 240 output_header.markfuzzy(template_header.isfuzzy())
241 242
243 -def main(argv=None):
244 from translate.convert import convert 245 formats = {"pot": ("po", convertpot), ("pot", "po"): ("po", convertpot), 246 "xlf": ("xlf", convertpot), ("xlf", "xlf"): ("xlf", convertpot), 247 "catkeys": ("catkeys", convertpot), ("catkeys", "catkeys"): ("catkeys", convertpot), 248 } 249 parser = convert.ConvertOptionParser(formats, usepots=True, usetemplates=True, 250 allowmissingtemplate=True, description=__doc__) 251 parser.add_option("", "--tm", dest="tm", default=None, 252 help="The file to use as translation memory when fuzzy matching") 253 parser.passthrough.append("tm") 254 defaultsimilarity = 75 255 parser.add_option("-s", "--similarity", dest="min_similarity", default=defaultsimilarity, 256 type="float", help="The minimum similarity for inclusion (default: %d%%)" % defaultsimilarity) 257 parser.passthrough.append("min_similarity") 258 parser.add_option("--nofuzzymatching", dest="fuzzymatching", action="store_false", 259 default=True, help="Disable fuzzy matching") 260 parser.passthrough.append("fuzzymatching") 261 parser.run(argv)
262 263 264 if __name__ == '__main__': 265 main() 266