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
34
35 -def convertpot(input_file, output_file, template_file, tm=None, min_similarity=75, fuzzymatching=True, **kwargs):
36 """Main conversion function"""
37
38 input_store = factory.getobject(input_file)
39 template_store = None
40 if template_file is not None:
41 template_store = factory.getobject(template_file)
42 output_store = convert_stores(input_store, template_store, tm, min_similarity, fuzzymatching, **kwargs)
43 output_file.write(str(output_store))
44 return 1
45
46 -def convert_stores(input_store, template_store, tm=None, min_similarity=75, fuzzymatching=True, **kwargs):
47 """Actual conversion function, works on stores not files, returns
48 a properly initialized pretranslated output store, with structure
49 based on input_store, metadata based on template_store, migrates
50 old translations from template_store and pretranslating from tm"""
51
52
53 output_store = type(input_store)()
54
55 matchers = []
56 _prepare_merge(input_store, output_store, template_store)
57 if fuzzymatching:
58 if template_store:
59 matcher = match.matcher(template_store, max_candidates=1, min_similarity=min_similarity, max_length=3000, usefuzzy=True)
60 matcher.addpercentage = False
61 matchers.append(matcher)
62 if tm:
63 matcher = pretranslate.memory(tm, max_candidates=1, min_similarity=min_similarity, max_length=1000)
64 matcher.addpercentage = False
65 matchers.append(matcher)
66
67
68 _store_pre_merge(input_store, output_store, template_store)
69
70
71 for input_unit in input_store.units:
72 if input_unit.istranslatable():
73 input_unit = pretranslate.pretranslate_unit(input_unit, template_store, matchers, mark_reused=True)
74 _unit_post_merge(input_unit, input_store, output_store, template_store)
75 output_store.addunit(input_unit)
76
77
78 _store_post_merge(input_store, output_store, template_store)
79
80 return output_store
81
82
83
84 -def _prepare_merge(input_store, output_store, template_store, **kwargs):
85 """Prepare stores & TM matchers before merging."""
86
87 prepare_merge_hook = "_prepare_merge_%s" % input_store.__class__.__name__
88 if globals().has_key(prepare_merge_hook):
89 globals()[prepare_merge_hook](input_store, output_store, template_store, **kwargs)
90
91
92 input_store.makeindex()
93 if template_store:
94 template_store.makeindex()
95
96
98 """Initialize the new file with things like headers and metadata."""
99
100 if isinstance(input_store, poheader.poheader):
101 _do_poheaders(input_store, output_store, template_store)
102
103
104 store_pre_merge_hook = "_store_pre_merge_%s" % input_store.__class__.__name__
105 if globals().has_key(store_pre_merge_hook):
106 globals()[store_pre_merge_hook](input_store, output_store, template_store, **kwargs)
107
108
109 -def _store_post_merge(input_store, output_store, template_store, **kwargs) :
110 """Close file after merging all translations, used for adding
111 statistics, obselete messages and similar wrapup tasks."""
112
113 store_post_merge_hook = "_store_post_merge_%s" % input_store.__class__.__name__
114 if globals().has_key(store_post_merge_hook):
115 globals()[store_post_merge_hook](input_store, output_store, template_store, **kwargs)
116
117 -def _unit_post_merge(input_unit, input_store, output_store, template_store, **kwargs):
118 """Handle any unit level cleanup and situations not handled by the merge()
119 function."""
120
121 unit_post_merge_hook = "_unit_post_merge_%s" % input_unit.__class__.__name__
122 if globals().has_key(unit_post_merge_hook):
123 globals()[unit_post_merge_hook](input_unit, input_store, output_store, template_store, **kwargs)
124
125
126
128 """PO format specific template preparation logic."""
129
130
131 if template_store:
132 for unit in template_store.units:
133 if unit.isobsolete():
134 unit.resurrect()
135
136
137 -def _unit_post_merge_pounit(input_unit, input_store, output_store, template_store):
138 """PO format specific plural string initializtion logic."""
139
140 if input_unit.hasplural() and len(input_unit.target) == 0:
141
142 nplurals, plural = output_store.getheaderplural()
143 if nplurals and nplurals.isdigit() and nplurals != '2':
144 input_unit.target = multistring([""]*int(nplurals))
145
146
147 -def _store_post_merge_pofile(input_store, output_store, template_store):
148 """PO format specific: adds newly obseleted messages to end of store."""
149
150 if template_store:
151 newlyobsoleted = []
152 for unit in template_store.units:
153 if unit.isheader():
154 continue
155 if unit.target and not (input_store.findunit(unit.source) or hasattr(unit, "reused")):
156
157 unit.makeobsolete()
158 newlyobsoleted.append(unit)
159 elif unit.isobsolete():
160 output_store.addunit(unit)
161 for unit in newlyobsoleted:
162 output_store.addunit(unit)
163
164
166 """Adds initialized PO headers to output store."""
167
168 charset = "UTF-8"
169 encoding = "8bit"
170 project_id_version = None
171 pot_creation_date = None
172 po_revision_date = None
173 last_translator = None
174 language_team = None
175 mime_version = None
176 plural_forms = None
177 kwargs = {}
178
179 if template_store is not None and isinstance(template_store, poheader.poheader):
180 templateheadervalues = template_store.parseheader()
181 for key, value in templateheadervalues.iteritems():
182 if key == "Project-Id-Version":
183 project_id_version = value
184 elif key == "Last-Translator":
185 last_translator = value
186 elif key == "Language-Team":
187 language_team = value
188 elif key == "PO-Revision-Date":
189 po_revision_date = value
190 elif key in ("POT-Creation-Date", "MIME-Version"):
191
192 pass
193 elif key == "Content-Type":
194 kwargs[key] = value
195 elif key == "Content-Transfer-Encoding":
196 encoding = value
197 elif key == "Plural-Forms":
198 plural_forms = value
199 else:
200 kwargs[key] = value
201
202 inputheadervalues = input_store.parseheader()
203 for key, value in inputheadervalues.iteritems():
204 if key in ("Project-Id-Version", "Last-Translator", "Language-Team", "PO-Revision-Date", "Content-Type", "Content-Transfer-Encoding", "Plural-Forms"):
205
206 pass
207 elif key == "POT-Creation-Date":
208 pot_creation_date = value
209 elif key == "MIME-Version":
210 mime_version = value
211 else:
212 kwargs[key] = value
213
214 output_header = output_store.makeheader(charset=charset, encoding=encoding, project_id_version=project_id_version,
215 pot_creation_date=pot_creation_date, po_revision_date=po_revision_date, last_translator=last_translator,
216 language_team=language_team, mime_version=mime_version, plural_forms=plural_forms, **kwargs)
217
218
219 if template_store is not None and len(template_store.units) > 0:
220 if template_store.units[0].isheader():
221 if template_store.units[0].getnotes("translator"):
222 output_header.addnote(template_store.units[0].getnotes("translator"), "translator")
223 if input_store.units[0].getnotes("developer"):
224 output_header.addnote(input_store.units[0].getnotes("developer"), "developer")
225 output_header.markfuzzy(template_store.units[0].isfuzzy())
226 elif len(input_store.units) > 0 and input_store.units[0].isheader():
227 output_header.addnote(input_store.units[0].getnotes())
228
229 output_store.addunit(output_header)
230
231
232 -def main(argv=None):
233 from translate.convert import convert
234 formats = {"pot": ("po", convertpot), ("pot", "po"): ("po", convertpot),
235 "xlf": ("xlf", convertpot), ("xlf", "xlf"): ("xlf", convertpot),
236 }
237 parser = convert.ConvertOptionParser(formats, usepots=True, usetemplates=True,
238 allowmissingtemplate=True, description=__doc__)
239 parser.add_option("", "--tm", dest="tm", default=None,
240 help="The file to use as translation memory when fuzzy matching")
241 parser.passthrough.append("tm")
242 defaultsimilarity = 75
243 parser.add_option("-s", "--similarity", dest="min_similarity", default=defaultsimilarity,
244 type="float", help="The minimum similarity for inclusion (default: %d%%)" % defaultsimilarity)
245 parser.passthrough.append("min_similarity")
246 parser.add_option("--nofuzzymatching", dest="fuzzymatching", action="store_false",
247 default=True, help="Disable fuzzy matching")
248 parser.passthrough.append("fuzzymatching")
249 parser.run(argv)
250
251
252 if __name__ == '__main__':
253 main()
254