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, po 
 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 try: 41 temp_store = factory.getobject(input_file, classes=classes) 42 except: 43 # StringIO and other file like objects will be closed after parsing 44 temp_store = None 45 46 template_store = None 47 if template_file is not None: 48 template_store = factory.getobject(template_file, classes=classes) 49 output_store = convert_stores(input_store, template_store, temp_store, tm, min_similarity, fuzzymatching, **kwargs) 50 output_file.write(str(output_store)) 51 return 1
52 53
54 -def convert_stores(input_store, template_store, temp_store=None, tm=None, min_similarity=75, fuzzymatching=True, **kwargs):
55 """Actual conversion function, works on stores not files, returns 56 a properly initialized pretranslated output store, with structure 57 based on input_store, metadata based on template_store, migrates 58 old translations from template_store and pretranslating from tm""" 59 60 if temp_store is None: 61 temp_store = input_store 62 63 #create fuzzy matchers to be used by pretranslate.pretranslate_unit 64 matchers = [] 65 _prepare_merge(input_store, temp_store, template_store) 66 if fuzzymatching: 67 if template_store: 68 matcher = match.matcher(template_store, max_candidates=1, min_similarity=min_similarity, max_length=3000, usefuzzy=True) 69 matcher.addpercentage = False 70 matchers.append(matcher) 71 if tm: 72 matcher = pretranslate.memory(tm, max_candidates=1, min_similarity=min_similarity, max_length=1000) 73 matcher.addpercentage = False 74 matchers.append(matcher) 75 76 #initialize store 77 _store_pre_merge(input_store, temp_store, template_store) 78 79 # Do matching 80 match_locations = isinstance(input_store, po.pofile) and input_store.parseheader().get('X-Accelerator-Marker') in ('&', '~') 81 for input_unit in temp_store.units: 82 if input_unit.istranslatable(): 83 input_unit = pretranslate.pretranslate_unit(input_unit, template_store, matchers, mark_reused=True, match_locations=match_locations) 84 _unit_post_merge(input_unit, input_store, temp_store, template_store) 85 86 #finalize store 87 _store_post_merge(input_store, temp_store, template_store) 88 89 return temp_store
90 91 92 ##dispatchers
93 -def _prepare_merge(input_store, output_store, template_store, **kwargs):
94 """Prepare stores & TM matchers before merging.""" 95 #dispatch to format specific functions 96 prepare_merge_hook = "_prepare_merge_%s" % input_store.__class__.__name__ 97 if prepare_merge_hook in globals(): 98 globals()[prepare_merge_hook](input_store, output_store, template_store, **kwargs) 99 100 #generate an index so we can search by source string and location later on 101 input_store.makeindex() 102 if template_store: 103 template_store.makeindex()
104 105
106 -def _store_pre_merge(input_store, output_store, template_store, **kwargs):
107 """Initialize the new file with things like headers and metadata.""" 108 #formats that implement poheader interface are a special case 109 if isinstance(input_store, poheader.poheader): 110 _do_poheaders(input_store, output_store, template_store) 111 elif isinstance(input_store, catkeys.CatkeysFile): 112 #FIXME: shouldn't we be merging template_store.header instead? 113 #FIXME: also this should be a format specific hook 114 output_store.header = input_store.header 115 116 #dispatch to format specific functions 117 store_pre_merge_hook = "_store_pre_merge_%s" % input_store.__class__.__name__ 118 if store_pre_merge_hook in globals(): 119 globals()[store_pre_merge_hook](input_store, output_store, template_store, **kwargs)
120
121 -def _store_post_merge(input_store, output_store, template_store, **kwargs):
122 """Close file after merging all translations, used for adding 123 statistics, obsolete messages and similar wrapup tasks.""" 124 #dispatch to format specific functions 125 store_post_merge_hook = "_store_post_merge_%s" % input_store.__class__.__name__ 126 if store_post_merge_hook in globals(): 127 globals()[store_post_merge_hook](input_store, output_store, template_store, **kwargs)
128 129
130 -def _unit_post_merge(input_unit, input_store, output_store, template_store, **kwargs):
131 """Handle any unit level cleanup and situations not handled by the merge() 132 function.""" 133 #dispatch to format specific functions 134 unit_post_merge_hook = "_unit_post_merge_%s" % input_unit.__class__.__name__ 135 if unit_post_merge_hook in globals(): 136 globals()[unit_post_merge_hook](input_unit, input_store, output_store, template_store, **kwargs)
137 138 139 ##format specific functions
140 -def _prepare_merge_pofile(input_store, output_store, template_store):
141 """PO format specific template preparation logic.""" 142 #we need to revive obsolete units to be able to consider 143 #their translation when matching 144 if template_store: 145 for unit in template_store.units: 146 if unit.isobsolete(): 147 unit.resurrect()
148 149
150 -def _unit_post_merge_pounit(input_unit, input_store, output_store, template_store):
151 """PO format specific plural string initializtion logic.""" 152 #FIXME: do we want to do that for poxliff also? 153 if input_unit.hasplural() and len(input_unit.target) == 0: 154 # untranslated plural unit; Let's ensure that we have the correct number of plural forms: 155 nplurals, plural = output_store.getheaderplural() 156 if nplurals and nplurals.isdigit() and nplurals != '2': 157 input_unit.target = multistring([""] * int(nplurals))
158 159
160 -def _store_post_merge_pofile(input_store, output_store, template_store):
161 """PO format specific: adds newly obsoleted messages to end of store.""" 162 #Let's take care of obsoleted messages 163 if template_store: 164 newlyobsoleted = [] 165 for unit in template_store.units: 166 if not unit.istranslatable() or not unit.istranslated(): 167 continue 168 if unit.target and not (input_store.findid(unit.getid()) or hasattr(unit, "reused")): 169 #not in .pot, make it obsolete 170 unit.makeobsolete() 171 newlyobsoleted.append(unit) 172 elif unit.isobsolete(): 173 output_store.addunit(unit) 174 for unit in newlyobsoleted: 175 output_store.addunit(unit)
176 177
178 -def _do_poheaders(input_store, output_store, template_store):
179 """Adds initialized PO headers to output store.""" 180 # header values 181 charset = "UTF-8" 182 encoding = "8bit" 183 project_id_version = None 184 pot_creation_date = None 185 po_revision_date = None 186 last_translator = None 187 language_team = None 188 mime_version = None 189 plural_forms = None 190 kwargs = {} 191 192 if template_store is not None and isinstance(template_store, poheader.poheader): 193 templateheadervalues = template_store.parseheader() 194 for key, value in templateheadervalues.iteritems(): 195 if key == "Project-Id-Version": 196 project_id_version = value 197 elif key == "Last-Translator": 198 last_translator = value 199 elif key == "Language-Team": 200 language_team = value 201 elif key == "PO-Revision-Date": 202 po_revision_date = value 203 elif key in ("POT-Creation-Date", "MIME-Version"): 204 # don't know how to handle these keys, or ignoring them 205 pass 206 elif key == "Content-Type": 207 kwargs[key] = value 208 elif key == "Content-Transfer-Encoding": 209 encoding = value 210 elif key == "Plural-Forms": 211 plural_forms = value 212 else: 213 kwargs[key] = value 214 215 inputheadervalues = input_store.parseheader() 216 for key, value in inputheadervalues.iteritems(): 217 if key in ("Project-Id-Version", "Last-Translator", "Language-Team", "PO-Revision-Date", "Content-Type", "Content-Transfer-Encoding", "Plural-Forms"): 218 # want to carry these from the template so we ignore them 219 pass 220 elif key == "POT-Creation-Date": 221 pot_creation_date = value 222 elif key == "MIME-Version": 223 mime_version = value 224 else: 225 kwargs[key] = value 226 227 output_header = output_store.init_headers(charset=charset, encoding=encoding, project_id_version=project_id_version, 228 pot_creation_date=pot_creation_date, po_revision_date=po_revision_date, last_translator=last_translator, 229 language_team=language_team, mime_version=mime_version, plural_forms=plural_forms, **kwargs) 230 231 # Get the header comments and fuzziness state 232 # override some values from input file 233 if template_store is not None: 234 template_header = template_store.header() 235 if template_header is not None: 236 if template_header.getnotes("translator"): 237 output_header.addnote(template_header.getnotes("translator"), "translator", position="replace") 238 output_header.markfuzzy(template_header.isfuzzy())
239 240
241 -def main(argv=None):
242 from translate.convert import convert 243 formats = {"pot": ("po", convertpot), ("pot", "po"): ("po", convertpot), 244 "xlf": ("xlf", convertpot), ("xlf", "xlf"): ("xlf", convertpot), 245 "ts": ("ts", convertpot), ("ts", "ts"): ("ts", convertpot), 246 "catkeys": ("catkeys", convertpot), ("catkeys", "catkeys"): ("catkeys", convertpot), 247 } 248 parser = convert.ConvertOptionParser(formats, usepots=True, usetemplates=True, 249 allowmissingtemplate=True, description=__doc__) 250 parser.add_option("", "--tm", dest="tm", default=None, 251 help="The file to use as translation memory when fuzzy matching") 252 parser.passthrough.append("tm") 253 defaultsimilarity = 75 254 parser.add_option("-s", "--similarity", dest="min_similarity", default=defaultsimilarity, 255 type="float", help="The minimum similarity for inclusion (default: %d%%)" % defaultsimilarity) 256 parser.passthrough.append("min_similarity") 257 parser.add_option("--nofuzzymatching", dest="fuzzymatching", action="store_false", 258 default=True, help="Disable fuzzy matching") 259 parser.passthrough.append("fuzzymatching") 260 parser.run(argv)
261 262 263 if __name__ == '__main__': 264 main() 265