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, 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
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
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
77 _store_pre_merge(input_store, temp_store, template_store)
78
79
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
87 _store_post_merge(input_store, temp_store, template_store)
88
89 return temp_store
90
91
92
93 -def _prepare_merge(input_store, output_store, template_store, **kwargs):
94 """Prepare stores & TM matchers before merging."""
95
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
101 input_store.makeindex()
102 if template_store:
103 template_store.makeindex()
104
105
107 """Initialize the new file with things like headers and metadata."""
108
109 if isinstance(input_store, poheader.poheader):
110 _do_poheaders(input_store, output_store, template_store)
111 elif isinstance(input_store, catkeys.CatkeysFile):
112
113
114 output_store.header = input_store.header
115
116
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
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
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
140 -def _unit_post_merge_pounit(input_unit, input_store, output_store, template_store):
141 """PO format specific plural string initializtion logic."""
142
143 if input_unit.hasplural() and len(input_unit.target) == 0:
144
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
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
160 unit.makeobsolete()
161 newlyobsoleted.append(unit)
162 for unit in newlyobsoleted:
163 output_store.addunit(unit)
164
165
167 """Adds initialized PO headers to output store."""
168
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
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
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
220
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