[Zope3-checkins] SVN: Zope3/trunk/src/zope/ Changed the strategy for handling fallback domains. We want fallback

Jim Fulton jim at zope.com
Wed Nov 9 16:30:39 EST 2005


Log message for revision 40011:
  Changed the strategy for handling fallback domains.  We want fallback
  domains to know what domain they fell back from to generate useful
  "test" translations.  Now, when we can't find a domain, we try to get
  a fallback-domain factory which is used to compute a fallback domain
  on the fly.
  
  Also, the test language now shows default values in translations.
  This is helpful for recognizing message ids and, in tests, for
  verifying that the correct default was used.
  

Changed:
  U   Zope3/trunk/src/zope/app/i18n/tests/configure.zcml
  D   Zope3/trunk/src/zope/app/i18n/tests/locales/
  U   Zope3/trunk/src/zope/i18n/__init__.py
  U   Zope3/trunk/src/zope/i18n/interfaces/__init__.py
  U   Zope3/trunk/src/zope/i18n/testmessagecatalog.py
  U   Zope3/trunk/src/zope/i18n/testmessagecatalog.txt
  U   Zope3/trunk/src/zope/i18n/tests/test.py
  U   Zope3/trunk/src/zope/i18n/tests/test_translationdomain.py
  U   Zope3/trunk/src/zope/i18n/translationdomain.py

-=-
Modified: Zope3/trunk/src/zope/app/i18n/tests/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/i18n/tests/configure.zcml	2005-11-09 20:50:12 UTC (rev 40010)
+++ Zope3/trunk/src/zope/app/i18n/tests/configure.zcml	2005-11-09 21:30:39 UTC (rev 40011)
@@ -1,9 +1,10 @@
 <configure
     xmlns="http://namespaces.zope.org/zope"
-    xmlns:i18n="http://namespaces.zope.org/i18n"
     >
 
   <!-- Default (empty) Translations -->
-  <i18n:registerTranslations directory="locales" />
+  <utility
+      component="zope.i18n.testmessagecatalog.TestMessageFallbackDomain"
+      />
 
 </configure>

Modified: Zope3/trunk/src/zope/i18n/__init__.py
===================================================================
--- Zope3/trunk/src/zope/i18n/__init__.py	2005-11-09 20:50:12 UTC (rev 40010)
+++ Zope3/trunk/src/zope/i18n/__init__.py	2005-11-09 21:30:39 UTC (rev 40011)
@@ -26,6 +26,7 @@
 
 from zope.i18nmessageid import MessageFactory, Message
 from zope.i18n.interfaces import ITranslationDomain
+from zope.i18n.interfaces import IFallbackTranslationDomainFactory
 from zope.component import queryUtility
 
 # Set up regular expressions for finding interpolation variables in text.
@@ -50,9 +51,13 @@
     if domain:
         util = queryUtility(ITranslationDomain, domain)
         if util is None:
-            util = queryUtility(ITranslationDomain)
+            util = queryUtility(IFallbackTranslationDomainFactory)
+            if util is not None:
+                util = util(domain)
     else:
-        util = queryUtility(ITranslationDomain)
+        util = queryUtility(IFallbackTranslationDomainFactory)
+        if util is not None:
+            util = util()
 
     if util is None:
         return interpolate(default, mapping)

Modified: Zope3/trunk/src/zope/i18n/interfaces/__init__.py
===================================================================
--- Zope3/trunk/src/zope/i18n/interfaces/__init__.py	2005-11-09 20:50:12 UTC (rev 40010)
+++ Zope3/trunk/src/zope/i18n/interfaces/__init__.py	2005-11-09 21:30:39 UTC (rev 40011)
@@ -151,7 +151,17 @@
         the simplifications.
         """
 
+class IFallbackTranslationDomainFactory(Interface):
+    """Factory for creating fallback translation domains
 
+    Fallback translation domains are primarily used for testing or
+    debugging i18n.
+    """
+
+    def __call__(domain_id=u''):
+        """Return a fallback translation domain for the given domain id. 
+        """
+
 class ITranslator(Interface):
     """A collaborative object which contains the domain, context, and locale.
 

Modified: Zope3/trunk/src/zope/i18n/testmessagecatalog.py
===================================================================
--- Zope3/trunk/src/zope/i18n/testmessagecatalog.py	2005-11-09 20:50:12 UTC (rev 40010)
+++ Zope3/trunk/src/zope/i18n/testmessagecatalog.py	2005-11-09 21:30:39 UTC (rev 40011)
@@ -16,11 +16,12 @@
 $Id$
 """
 
-from zope import interface
-from zope.i18n.interfaces import IGlobalMessageCatalog
+from zope import component, interface
+import zope.i18n.interfaces
+from zope.i18n.translationdomain import TranslationDomain
 
 class TestMessageCatalog:
-    interface.implements(IGlobalMessageCatalog)
+    interface.implements(zope.i18n.interfaces.IGlobalMessageCatalog)
 
     language = 'test'
 
@@ -28,8 +29,13 @@
         self.domain = domain
 
     def queryMessage(self, msgid, default=None):
-        return u'[[%s][%s]]' % (self.domain or getattr(msgid, 'domain', ''),
-                                msgid)
+        default = getattr(msgid, 'default', default)
+        if default != None and default != msgid:
+            msg = u"%s (%s)" % (msgid, default)
+        else:
+            msg = msgid
+            
+        return u'[[%s][%s]]' % (self.domain, msg)
 
     getMessage = queryMessage
 
@@ -39,4 +45,13 @@
     def reload(self):
         pass
 
-                     
+ at interface.implementer(zope.i18n.interfaces.ITranslationDomain)
+def TestMessageFallbackDomain(domain_id=u''):
+    domain = TranslationDomain(domain_id)
+    domain.addCatalog(TestMessageCatalog(domain_id))
+    return domain
+
+interface.directlyProvides(
+    TestMessageFallbackDomain,
+    zope.i18n.interfaces.IFallbackTranslationDomainFactory,
+    )

Modified: Zope3/trunk/src/zope/i18n/testmessagecatalog.txt
===================================================================
--- Zope3/trunk/src/zope/i18n/testmessagecatalog.txt	2005-11-09 20:50:12 UTC (rev 40010)
+++ Zope3/trunk/src/zope/i18n/testmessagecatalog.txt	2005-11-09 21:30:39 UTC (rev 40011)
@@ -21,25 +21,46 @@
 
     >>> cat.reload()
 
-Normally, the catalog's domain overrides the message id's domain:
+If a message id has a default, it will be included in the output:
 
-    >>> import zope.i18nmessageid
-    >>> id = zope.i18nmessageid.MessageFactory('baz.splat')('eek')
+    >>> id = zope.i18nmessageid.MessageFactory('foo.bar')('eek', default='Eek')
 
     >>> cat.queryMessage(id)
-    u'[[foo.bar][eek]]'
+    u'[[foo.bar][eek (Eek)]]'
 
     >>> cat.getMessage(id)
-    u'[[foo.bar][eek]]'
+    u'[[foo.bar][eek (Eek)]]'
 
-However, if the catalog's domain is '', indicating a fallback domain,
-then the id's domain is used:
+If a message doesn't have a default, but a default is passed in to
+queryMessage, the default will be used used:
 
-    >>> cat = zope.i18n.testmessagecatalog.TestMessageCatalog('')
+    >>> cat.queryMessage('eek', default='Eek')
+    u'[[foo.bar][eek (Eek)]]'
 
-    >>> cat.queryMessage(id)
-    u'[[baz.splat][eek]]'
+    >>> cat.getMessage(id, default='Waaa')
+    u'[[foo.bar][eek (Eek)]]'
 
-    >>> cat.getMessage(id)
-    u'[[baz.splat][eek]]'
+Fallback domains
+----------------
+
+The testmessagecatalog module also provide a fallback domain factory
+that has the test catalog as it's only catalog:
+
+    >>> factory = zope.i18n.testmessagecatalog.TestMessageFallbackDomain
+    >>> import zope.i18n.interfaces
+    >>> zope.i18n.interfaces.IFallbackTranslationDomainFactory.providedBy(
+    ...     factory)
+    True
     
+    >>> domain = factory('foo.bar')
+    >>> domain.translate(u'eek')
+    u'eek'
+
+    >>> domain.translate(u'eek', target_language='test')
+    u'[[foo.bar][eek]]'
+
+Note that if a default is padded in, it will be included in test
+output:
+
+    >>> domain.translate(u'eek', target_language='test', default=u'Eek')
+    u'[[foo.bar][eek (Eek)]]'

Modified: Zope3/trunk/src/zope/i18n/tests/test.py
===================================================================
--- Zope3/trunk/src/zope/i18n/tests/test.py	2005-11-09 20:50:12 UTC (rev 40010)
+++ Zope3/trunk/src/zope/i18n/tests/test.py	2005-11-09 21:30:39 UTC (rev 40011)
@@ -49,15 +49,22 @@
     >>> _translate(u'eek', 'your.domain')
     u'eek'
 
-A fallback domain can be provided. This is normally used for testing:
+A fallback domain factory can be provided. This is normally used for testing:
 
-    >>> component.provideUtility(TestDomain(eek=u'test'))
+    >>> def fallback(domain=u''):
+    ...     return TestDomain(eek=u'test-from-' + domain)
+    >>> interface.directlyProvides(
+    ...     fallback,
+    ...     zope.i18n.interfaces.IFallbackTranslationDomainFactory,
+    ...     )
 
+    >>> component.provideUtility(fallback)
+
     >>> _translate(u'eek')
-    u'test'
+    u'test-from-'
 
     >>> _translate(u'eek', 'your.domain')
-    u'test'
+    u'test-from-your.domain'
     
     """
 

Modified: Zope3/trunk/src/zope/i18n/tests/test_translationdomain.py
===================================================================
--- Zope3/trunk/src/zope/i18n/tests/test_translationdomain.py	2005-11-09 20:50:12 UTC (rev 40010)
+++ Zope3/trunk/src/zope/i18n/tests/test_translationdomain.py	2005-11-09 21:30:39 UTC (rev 40011)
@@ -16,7 +16,6 @@
 $Id$
 """
 import unittest, os
-from zope.testing import doctest
 from zope.i18n.translationdomain import TranslationDomain
 from zope.i18n.gettextmessagecatalog import GettextMessageCatalog
 from zope.i18n.tests.test_itranslationdomain import \
@@ -127,41 +126,9 @@
             "this THAT the other")
 
 
-def test_message_domain_ignored_by_fallback_domain():
-    """\
-
-Normally, a translation domain will try to lookup an alternative
-translation domain if a message's domain is different than it's own:
-
-    >>> domain = TranslationDomain('mydomain')
-    >>> msgid = MessageIDFactory('huh')(u'short_greeting', 'default')
-    >>> domain.translate(msgid, target_language='en')
-    ... # doctest: +NORMALIZE_WHITESPACE
-    Traceback (most recent call last):
-    ...
-    ComponentLookupError:
-    (<InterfaceClass zope.i18n.interfaces.ITranslationDomain>, 'huh')
-
-
-However, fallback domains, which have an empty domain name, ignore
-message domains:
-
-    >>> domain = TranslationDomain('')
-    >>> path = testdir()
-    >>> en_catalog = GettextMessageCatalog('en', 'other',
-    ...                                    os.path.join(path, 'en-default.mo'))
-    >>> domain.addCatalog(en_catalog)
-    >>> domain.translate(msgid, target_language='en')
-    u'Hello!'
-
-Fallback domains are used mainly for testing.
-
-"""
-
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(TestGlobalTranslationDomain))
-    suite.addTest(doctest.DocTestSuite())
     return suite
 
 if __name__ == '__main__':

Modified: Zope3/trunk/src/zope/i18n/translationdomain.py
===================================================================
--- Zope3/trunk/src/zope/i18n/translationdomain.py	2005-11-09 20:50:12 UTC (rev 40010)
+++ Zope3/trunk/src/zope/i18n/translationdomain.py	2005-11-09 21:30:39 UTC (rev 40011)
@@ -85,7 +85,7 @@
 
         # MessageID attributes override arguments
         if isinstance(msgid, (Message, MessageID)):
-            if (msgid.domain != self.domain) and self.domain:
+            if msgid.domain != self.domain:
                 util = getUtility(ITranslationDomain, msgid.domain)
             mapping = msgid.mapping
             default = msgid.default
@@ -101,15 +101,22 @@
                 if catalog_names is not None:
                     break
 
-        # Did the fallback fail?  Sigh, return None
         text = default
         if catalog_names:
-            for name in catalog_names:
-                catalog = self._data[name]
-                s = catalog.queryMessage(msgid)
-                if s is not None:
-                    text = s
-                    break
+            if len(catalog_names) == 1:
+                # this is a slight optimization for the case when there is a
+                # single catalog. More importantly, it is extremely helpful
+                # when testing and the test language is used, because it
+                # allows the test language to get the default. 
+                text = self._data[catalog_names[0]].queryMessage(
+                    msgid, default)
+            else:
+                for name in catalog_names:
+                    catalog = self._data[name]
+                    s = catalog.queryMessage(msgid)
+                    if s is not None:
+                        text = s
+                        break
 
         # Now we need to do the interpolation
         if text is not None:



More information about the Zope3-Checkins mailing list