1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 from lxml import etree
22
23 from translate.storage import base
24 from translate.misc.typecheck import accepts, Self, IsCallable, IsOneOf, Any, Class
25 from translate.misc.typecheck.typeclasses import Number
26 from translate.misc.contextlib import contextmanager, nested
27 from translate.misc.context import with_
28 from translate.storage.xml_extract import xpath_breadcrumb
29 from translate.storage.xml_extract import misc
30 from translate.storage.placeables import xliff, StringElem
33 return IsOneOf(t, type(None))
34
35 TranslatableClass = Class('Translatable')
38 """A node corresponds to a translatable element. A node may
39 have children, which correspond to placeables."""
40 @accepts(Self(), unicode, unicode, etree._Element, [IsOneOf(TranslatableClass, unicode)])
41 - def __init__(self, placeable_name, xpath, dom_node, source):
42 self.placeable_name = placeable_name
43 self.source = source
44 self.xpath = xpath
45 self.is_inline = False
46 self.dom_node = dom_node
47
50
51 placeables = property(_get_placeables)
52
56
58 """Maintain constants and variables used during the walking of a
59 DOM tree (via the function apply)."""
60 - def __init__(self, no_translate_content_elements, inline_elements = {}, nsmap = {}):
61 self.no_translate_content_elements = no_translate_content_elements
62 self.inline_elements = inline_elements
63 self.is_inline = False
64 self.xpath_breadcrumb = xpath_breadcrumb.XPathBreadcrumb()
65 self.placeable_name = u"<top-level>"
66 self.nsmap = nsmap
67
68 @accepts(etree._Element, ParseState)
70 """Run find_translatable_dom_nodes on the current dom_node"""
71 placeable = find_translatable_dom_nodes(dom_node, state)
72
73
74
75 if len(placeable) == 0:
76 return Translatable(u"placeable", state.xpath_breadcrumb.xpath, dom_node, [])
77
78
79 elif len(placeable) == 1:
80 return placeable[0]
81 else:
82 raise Exception("BUG: find_translatable_dom_nodes should never return more than a single translatable")
83
84 @accepts(etree._Element, ParseState)
86 """Return a list of placeables and list with
87 alternating string-placeable objects. The former is
88 useful for directly working with placeables and the latter
89 is what will be used to build the final translatable string."""
90
91 source = []
92 for child in dom_node:
93 source.extend([_process_placeable(child, state), unicode(child.tail or u"")])
94 return source
95
96 @accepts(etree._Element, ParseState)
102
103 @accepts(etree._Element, ParseState)
105 _namespace, tag = misc.parse_tag(dom_node.tag)
106 children = [find_translatable_dom_nodes(child, state) for child in dom_node]
107
108 children = [child for child_list in children for child in child_list]
109 if len(children) > 1:
110 intermediate_translatable = Translatable(tag, state.xpath_breadcrumb.xpath, dom_node, children)
111 return [intermediate_translatable]
112 else:
113 return children
114
120
121 @accepts(etree._Element, ParseState)
137
138 @contextmanager
139 def placeable_set():
140 old_placeable_name = state.placeable_name
141 state.placeable_name = tag
142 yield state.placeable_name
143 state.placeable_name = old_placeable_name
144
145 @contextmanager
146 def inline_set():
147 old_inline = state.is_inline
148 if (namespace, tag) in state.inline_elements:
149 state.is_inline = True
150 else:
151 state.is_inline = False
152 yield state.is_inline
153 state.is_inline = old_inline
154
155 def with_block(xpath_breadcrumb, placeable_name, is_inline):
156 if (namespace, tag) not in state.no_translate_content_elements:
157 return _process_translatable(dom_node, state)
158 else:
159 return _process_children(dom_node, state)
160 return with_(nested(xpath_set(), placeable_set(), inline_set()), with_block)
161
164 self._max_id = 0
165 self._obj_id_map = {}
166
168 if not self.has_id(obj):
169 self._obj_id_map[obj] = self._max_id
170 self._max_id += 1
171 return self._obj_id_map[obj]
172
174 return obj in self._obj_id_map
175
178 result = []
179 for chunk in translatable.source:
180 if isinstance(chunk, unicode):
181 result.append(chunk)
182 else:
183 id = unicode(id_maker.get_id(chunk))
184 if chunk.is_inline:
185 result.append(xliff.G(sub=_to_placeables(parent_translatable, chunk, id_maker), id=id))
186 else:
187 result.append(xliff.X(id=id, xid=chunk.xpath))
188 return result
189
190 @accepts(base.TranslationStore, Nullable(Translatable), Translatable, IdMaker)
192 """Construct a new translation unit, set its source and location
193 information and add it to 'store'.
194 """
195 unit = store.UnitClass(u'')
196 unit.rich_source = [StringElem(_to_placeables(parent_translatable, translatable, id_maker))]
197 unit.addlocation(translatable.xpath)
198 store.addunit(unit)
199
202 """Checks whether translatable contains any chunks of text which contain
203 more than whitespace.
204
205 If not, then there's nothing to translate."""
206 for chunk in translatable.source:
207 if isinstance(chunk, unicode):
208 if chunk.strip() != u"":
209 return True
210 return False
211
212 @accepts(base.TranslationStore)
214 """Return a function which, when called with a Translatable will add
215 a unit to 'store'. The placeables will represented as strings according
216 to 'placeable_quoter'."""
217 id_maker = IdMaker()
218
219 def add_to_store(parent_translatable, translatable, rid):
220 _add_translatable_to_store(store, parent_translatable, translatable, id_maker)
221
222 return add_to_store
223
226 for translatable in translatables:
227 if _contains_translatable_text(translatable) and not translatable.is_inline:
228 rid = rid + 1
229 new_parent_translatable = translatable
230 f(parent_translatable, translatable, rid)
231 else:
232 new_parent_translatable = parent_translatable
233
234 _walk_translatable_tree(translatable.placeables, f, new_parent_translatable, rid)
235
238
239 @accepts(lambda obj: hasattr(obj, "read"), base.TranslationStore, ParseState, Nullable(IsCallable()))
240 -def build_store(odf_file, store, parse_state, store_adder = None):
249