[Checkins] SVN: zope.i18n/trunk/ Feature: Recursive translations of
message strings with mappings
Christian Zagrodnick
cz at gocept.com
Sun Apr 20 13:22:50 EDT 2008
Log message for revision 85508:
Feature: Recursive translations of message strings with mappings
(https://bugs.launchpad.net/zope3/+bug/210177), thanks to Hermann Himmelbauer
for the inital patch.
Changed:
U zope.i18n/trunk/CHANGES.txt
U zope.i18n/trunk/src/zope/i18n/interfaces/__init__.py
U zope.i18n/trunk/src/zope/i18n/tests/test_translationdomain.py
U zope.i18n/trunk/src/zope/i18n/translationdomain.py
-=-
Modified: zope.i18n/trunk/CHANGES.txt
===================================================================
--- zope.i18n/trunk/CHANGES.txt 2008-04-20 17:15:56 UTC (rev 85507)
+++ zope.i18n/trunk/CHANGES.txt 2008-04-20 17:22:49 UTC (rev 85508)
@@ -2,6 +2,13 @@
CHANGES
=======
+3.4.1 (unreleased)
+------------------
+
+- Feature: Recursive translations of message strings with mappings
+ (https://bugs.launchpad.net/zope3/+bug/210177), thanks to Hermann
+ Himmelbauer for the inital patch.
+
3.4.0 (2007-10-02)
------------------
Modified: zope.i18n/trunk/src/zope/i18n/interfaces/__init__.py
===================================================================
--- zope.i18n/trunk/src/zope/i18n/interfaces/__init__.py 2008-04-20 17:15:56 UTC (rev 85507)
+++ zope.i18n/trunk/src/zope/i18n/interfaces/__init__.py 2008-04-20 17:22:49 UTC (rev 85508)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# Copyright (c) 2001-2008 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -143,7 +143,10 @@
However, the method does a little more than a vanilla translation.
The method also looks for a possible language to translate to.
After a translation it also replaces any $name variable variables
- inside the post-translation string.
+ inside the post-translation string with data from `mapping`. If a
+ value of `mapping` is a Message it is also translated before
+ interpolation.
+
"""
class IFallbackTranslationDomainFactory(Interface):
Modified: zope.i18n/trunk/src/zope/i18n/tests/test_translationdomain.py
===================================================================
--- zope.i18n/trunk/src/zope/i18n/tests/test_translationdomain.py 2008-04-20 17:15:56 UTC (rev 85507)
+++ zope.i18n/trunk/src/zope/i18n/tests/test_translationdomain.py 2008-04-20 17:22:49 UTC (rev 85508)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# Copyright (c) 2001-2008 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -92,8 +92,42 @@
mapping={'that': 'THAT'})
self.assertEqual(
translate(msgid, target_language='en', default="default",
- mapping={"that": "that"}), "this THAT the other")
+ mapping={"that": "that"}), "this THAT the other")
+ def testMessageIDRecursiveTranslate(self):
+ factory = MessageFactory('default')
+ translate = self._domain.translate
+ msgid_sub1 = factory(u'44-not-there', '${blue}',
+ mapping = {'blue': 'BLUE'})
+ msgid_sub2 = factory(u'45-not-there', '${yellow}',
+ mapping = {'yellow': 'YELLOW'})
+ mapping = {'color1': msgid_sub1,
+ 'color2': msgid_sub2}
+ msgid = factory(u'46-not-there', 'Color: ${color1}/${color2}',
+ mapping=mapping)
+ self.assertEqual(
+ translate(msgid, target_language='en', default="default"),
+ "Color: BLUE/YELLOW")
+ # The recursive translation must not change the mappings
+ self.assertEqual(msgid.mapping, {'color1': msgid_sub1,
+ 'color2': msgid_sub2})
+ # A circular reference should not lead to crashes
+ msgid1 = factory(u'47-not-there', 'Message 1 and $msg2',
+ mapping = {})
+ msgid2 = factory(u'48-not-there', 'Message 2 and $msg1',
+ mapping = {})
+ msgid1.mapping['msg2'] = msgid2
+ msgid2.mapping['msg1'] = msgid1
+ self.assertRaises(RuntimeError,
+ translate, msgid1, None, None, 'en',"default")
+ # Recusrive translations also work if the original message id wasn't a
+ # message id but a unicode with a directly passed mapping
+ self.assertEqual("Color: BLUE/YELLOW",
+ translate(u'Color: ${color1}/${color2}', mapping=mapping,
+ target_language='en'))
+
+
+
def testMessageIDTranslateForDifferentDomain(self):
domain = TranslationDomain('other')
path = testdir()
Modified: zope.i18n/trunk/src/zope/i18n/translationdomain.py
===================================================================
--- zope.i18n/trunk/src/zope/i18n/translationdomain.py 2008-04-20 17:15:56 UTC (rev 85507)
+++ zope.i18n/trunk/src/zope/i18n/translationdomain.py 2008-04-20 17:22:49 UTC (rev 85508)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# Copyright (c) 2001-2008 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -15,7 +15,7 @@
$Id$
"""
-from zope.component import getUtility
+import zope.component
from zope.i18nmessageid import Message
from zope.i18n import interpolate
from zope.i18n.simpletranslationdomain import SimpleTranslationDomain
@@ -64,7 +64,6 @@
def translate(self, msgid, mapping=None, context=None,
target_language=None, default=None):
"""See zope.i18n.interfaces.ITranslationDomain"""
-
# if the msgid is empty, let's save a lot of calculations and return
# an empty string.
if msgid == u'':
@@ -73,17 +72,41 @@
if target_language is None and context is not None:
langs = self._catalogs.keys()
# invoke local or global unnamed 'INegotiator' utilities
- negotiator = getUtility(INegotiator)
+ negotiator = zope.component.getUtility(INegotiator)
# try to determine target language from negotiator utility
target_language = negotiator.getLanguage(langs, context)
+ return self._recursive_translate(
+ msgid, mapping, target_language, default)
+
+ def _recursive_translate(self, msgid, mapping, target_language, default,
+ seen=None):
+ """Recurivly translate msg."""
# MessageID attributes override arguments
if isinstance(msgid, Message):
if msgid.domain != self.domain:
- util = getUtility(ITranslationDomain, msgid.domain)
+ util = zope.component.getUtility(
+ ITranslationDomain, msgid.domain)
+ default = msgid.default
mapping = msgid.mapping
- default = msgid.default
+ # Recursively translate mappings, if they are translatable
+ if (mapping is not None
+ and Message in (type(m) for m in mapping.values())):
+ if seen is None:
+ seen = set()
+ seen.add(msgid)
+ mapping = mapping.copy()
+ for key, value in mapping.items():
+ if isinstance(value, Message):
+ # XXX why isn't there an IMessage interface?
+ if value in seen:
+ raise RuntimeError( # XXX ValueError
+ "Circular Reference in Mappings detected")
+ mapping[key]=self._recursive_translate(
+ value, mapping, target_language,
+ default, seen)
+
if default is None:
default = unicode(msgid)
More information about the Checkins
mailing list