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-2010 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=None, classes_str=factory.classes_str, **kwargs):
37 """Main conversion function""" 38 39 input_store = factory.getobject(input_file, classes=classes, classes_str=classes_str) 40 try: 41 temp_store = factory.getobject(input_file, classes_str=classes_str) 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_str=classes_str) 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 _unit_post_merge_pounit(input_unit, input_store, output_store, template_store):
141 """PO format specific plural string initializtion logic.""" 142 #FIXME: do we want to do that for poxliff also? 143 if input_unit.hasplural() and len(input_unit.target) == 0: 144 # untranslated plural unit; Let's ensure that we have the correct number of plural forms: 145 nplurals, plural = output_store.getheaderplural() 146 if nplurals and nplurals.isdigit() and nplurals != '2': 147 input_unit.target = multistring([""] * int(nplurals))
148 149
150 -def _store_post_merge_pofile(input_store, output_store, template_store):
151 """PO format specific: adds newly obsoleted messages to end of store.""" 152 #Let's take care of obsoleted messages 153 if template_store: 154 newlyobsoleted = [] 155 for unit in template_store.units: 156 if unit.isheader() or unit.isblank(): 157 continue 158 if unit.target and not (input_store.findid(unit.getid()) or hasattr(unit, "reused")): 159 #not in .pot, make it obsolete 160 unit.makeobsolete() 161 newlyobsoleted.append(unit) 162 for unit in newlyobsoleted: 163 output_store.addunit(unit)
164 165
166 -def _do_poheaders(input_store, output_store, template_store):
167 """Adds initialized PO headers to output store.""" 168 # header values 169 charset = "UTF-8" 170 encoding = "8bit" 171 project_id_version = None 172 pot_creation_date = None 173 po_revision_date = None 174 last_translator = None 175 language_team = None 176 mime_version = None 177 plural_forms = None 178 kwargs = {} 179 180 if template_store is not None and isinstance(template_store, poheader.poheader): 181 templateheadervalues = template_store.parseheader() 182 for key, value in templateheadervalues.iteritems(): 183 if key == "Project-Id-Version": 184 project_id_version = value 185 elif key == "Last-Translator": 186 last_translator = value 187 elif key == "Language-Team": 188 language_team = value 189 elif key == "PO-Revision-Date": 190 po_revision_date = value 191 elif key in ("POT-Creation-Date", "MIME-Version"): 192 # don't know how to handle these keys, or ignoring them 193 pass 194 elif key == "Content-Type": 195 kwargs[key] = value 196 elif key == "Content-Transfer-Encoding": 197 encoding = value 198 elif key == "Plural-Forms": 199 plural_forms = value 200 else: 201 kwargs[key] = value 202 203 inputheadervalues = input_store.parseheader() 204 for key, value in inputheadervalues.iteritems(): 205 if key in ("Project-Id-Version", "Last-Translator", "Language-Team", "PO-Revision-Date", "Content-Type", "Content-Transfer-Encoding", "Plural-Forms"): 206 # want to carry these from the template so we ignore them 207 pass 208 elif key == "POT-Creation-Date": 209 pot_creation_date = value 210 elif key == "MIME-Version": 211 mime_version = value 212 else: 213 kwargs[key] = value 214 215 output_header = output_store.init_headers(charset=charset, encoding=encoding, project_id_version=project_id_version, 216 pot_creation_date=pot_creation_date, po_revision_date=po_revision_date, last_translator=last_translator, 217 language_team=language_team, mime_version=mime_version, plural_forms=plural_forms, **kwargs) 218 219 # Get the header comments and fuzziness state 220 # override some values from input file 221 if template_store is not None: 222 template_header = template_store.header() 223 if template_header is not None: 224 if template_header.getnotes("translator"): 225 output_header.addnote(template_header.getnotes("translator"), "translator", position="replace") 226 output_header.markfuzzy(template_header.isfuzzy())
227 228
229 -def main(argv=None):
230 from translate.convert import convert 231 formats = {"pot": ("po", convertpot), ("pot", "po"): ("po", convertpot), 232 "xlf": ("xlf", convertpot), ("xlf", "xlf"): ("xlf", convertpot), 233 "ts": ("ts", convertpot), ("ts", "ts"): ("ts", convertpot), 234 "catkeys": ("catkeys", convertpot), ("catkeys", "catkeys"): ("catkeys", convertpot), 235 } 236 parser = convert.ConvertOptionParser(formats, usepots=True, usetemplates=True, 237 allowmissingtemplate=True, description=__doc__) 238 parser.add_option("", "--tm", dest="tm", default=None, 239 help="The file to use as translation memory when fuzzy matching") 240 parser.passthrough.append("tm") 241 defaultsimilarity = 75 242 parser.add_option("-s", "--similarity", dest="min_similarity", default=defaultsimilarity, 243 type="float", help="The minimum similarity for inclusion (default: %d%%)" % defaultsimilarity) 244 parser.passthrough.append("min_similarity") 245 parser.add_option("--nofuzzymatching", dest="fuzzymatching", action="store_false", 246 default=True, help="Disable fuzzy matching") 247 parser.passthrough.append("fuzzymatching") 248 parser.run(argv)
249 250 251 if __name__ == '__main__': 252 main() 253