diff --git a/openerp/addons/base/tests/test_translate.py b/openerp/addons/base/tests/test_translate.py index 35518bbfb11..40cf960873c 100644 --- a/openerp/addons/base/tests/test_translate.py +++ b/openerp/addons/base/tests/test_translate.py @@ -2,7 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import unittest -from openerp.tools.translate import quote, unquote, xml_translate +from openerp.tools.translate import quote, unquote, xml_translate, html_translate class TranslationToolsTestCase(unittest.TestCase): @@ -142,3 +142,11 @@ class TranslationToolsTestCase(unittest.TestCase): self.assertEquals(result, source) self.assertItemsEqual(terms, ['Blah', 'More ']) + + def test_translate_html(self): + """ Test xml_translate() and html_translate() with elements. """ + source = """""" + result = xml_translate(lambda term: term, source) + self.assertEquals(result, """""") + result = html_translate(lambda term: term, source) + self.assertEquals(result, source) diff --git a/openerp/fields.py b/openerp/fields.py index 5f815de6909..ec3a1b8d3c3 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -15,7 +15,7 @@ import xmlrpclib from openerp.tools import float_round, frozendict, html_sanitize, ustr, OrderedSet from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT -from openerp.tools.translate import xml_translate +from openerp.tools.translate import html_translate DATE_LENGTH = len(date.today().strftime(DATE_FORMAT)) DATETIME_LENGTH = len(datetime.now().strftime(DATETIME_FORMAT)) @@ -1245,9 +1245,9 @@ class Html(_String): def _setup_attrs(self, model, name): super(Html, self)._setup_attrs(model, name) - # Translated sanitized html fields must use xml_translate or a callable. + # Translated sanitized html fields must use html_translate or a callable. if self.translate and not callable(self.translate) and self.sanitize: - self.translate = xml_translate + self.translate = html_translate _column_sanitize = property(attrgetter('sanitize')) _related_sanitize = property(attrgetter('sanitize')) diff --git a/openerp/tools/translate.py b/openerp/tools/translate.py index 713603b5904..3287547c790 100644 --- a/openerp/tools/translate.py +++ b/openerp/tools/translate.py @@ -151,16 +151,8 @@ TRANSLATED_ATTRS = { 'string', 'help', 'sum', 'avg', 'confirm', 'placeholder', 'alt', 'title', } -def serialize(tag, attrib, content): - """ Return a serialized element with the given `tag`, attributes - `attrib`, and already-serialized `content`. - """ - elem = etree.tostring(etree.Element(tag, attrib)) - assert elem.endswith("/>") - return "%s>%s" % (elem[:-2], content, tag) if content else elem - class XMLTranslator(object): - """ A sequence of serialized xml items, with some of them to translate + """ A sequence of serialized XML/HTML items, with some of them to translate (todo) and others already translated (done). The purpose of this object is to simplify the handling of phrasing elements (like ) that must be translated together with their surrounding text. @@ -177,8 +169,9 @@ class XMLTranslator(object): """ - def __init__(self, callback): + def __init__(self, callback, method): self.callback = callback # callback function to translate terms + self.method = method # serialization method ('xml' or 'html') self._done = [] # translated strings self._todo = [] # todo strings that come after _done self.needs_trans = False # whether todo needs translation @@ -227,12 +220,12 @@ class XMLTranslator(object): ): # do not translate the contents of the node tail, node.tail = node.tail, None - self.done(etree.tostring(node)) + self.done(etree.tostring(node, method=self.method)) self.todo(escape(tail or "")) return # process children nodes locally in child_trans - child_trans = XMLTranslator(self.callback) + child_trans = XMLTranslator(self.callback, self.method) child_trans.todo(escape(node.text or "")) for child in node: child_trans.process(child) @@ -241,38 +234,61 @@ class XMLTranslator(object): node.tag in TRANSLATED_ELEMENTS and not any(attr.startswith("t-") for attr in node.attrib)): # serialize the node element as todo - self.todo(serialize(node.tag, node.attrib, child_trans.get_todo()), + self.todo(self.serialize(node.tag, node.attrib, child_trans.get_todo()), child_trans.needs_trans) else: # complete translations and serialize result as done for attr in TRANSLATED_ATTRS: if node.get(attr): node.set(attr, self.process_text(node.get(attr))) - self.done(serialize(node.tag, node.attrib, child_trans.get_done())) + self.done(self.serialize(node.tag, node.attrib, child_trans.get_done())) # add node tail as todo self.todo(escape(node.tail or "")) + def serialize(self, tag, attrib, content): + """ Return a serialized element with the given `tag`, attributes + `attrib`, and already-serialized `content`. + """ + if content: + elem = etree.tostring(etree.Element(tag, attrib), method='xml') + assert elem.endswith("/>") + return "%s>%s" % (elem[:-2], content, tag) + else: + return etree.tostring(etree.Element(tag, attrib), method=self.method) + def xml_translate(callback, value): """ Translate an XML value (string), using `callback` for translating text - appearing in `value`. If `value` is not XML valid, it is parsed with an HTML - parser instead, and some element wrapping is done to make the parsing work. + appearing in `value`. """ if not value: return value - trans = XMLTranslator(callback) + trans = XMLTranslator(callback, 'xml') try: root = etree.fromstring(encode(value)) trans.process(root) return trans.get_done() except etree.ParseError: - wrapped = "
%s
" % encode(value) - root = etree.fromstring(wrapped, etree.HTMLParser(encoding='utf-8')) - # html > body > div - trans.process(root[0][0]) - return trans.get_done()[5:-6] + # fallback in case it is a translated term... + root = etree.fromstring("
%s
" % encode(value)) + trans.process(root) + return trans.get_done()[5:-6] # remove tags
and
+ +def html_translate(callback, value): + """ Translate an HTML value (string), using `callback` for translating text + appearing in `value`. + """ + if not value: + return value + + trans = XMLTranslator(callback, 'html') + wrapped = "
%s
" % encode(value) + root = etree.fromstring(wrapped, etree.HTMLParser(encoding='utf-8')) + # html > body > div + trans.process(root[0][0]) + return trans.get_done()[5:-6] # remove tags
and
#