1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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):
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
54 output_store = type(input_store)()
55
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
69 _store_pre_merge(input_store, output_store, template_store)
70
71
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
81 _store_post_merge(input_store, output_store, template_store)
82
83 return output_store
84
85
86
87 -def _prepare_merge(input_store, output_store, template_store, **kwargs):
88 """Prepare stores & TM matchers before merging."""
89
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
95 input_store.makeindex()
96 if template_store:
97 template_store.makeindex()
98
99
101 """Initialize the new file with things like headers and metadata."""
102
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
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
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
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
133 """PO format specific template preparation logic."""
134
135
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
145 if input_unit.hasplural() and len(input_unit.target) == 0:
146
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
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
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
171 """Adds initialized PO headers to output store."""
172
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
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
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
224
225
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
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