[Checkins] SVN: zope.i18n/branches/lazy-tds/ The ZCML directive handler for a translation domain registration now
Malthe Borch
cvs-admin at zope.org
Wed Oct 10 09:25:21 UTC 2012
Log message for revision 127957:
The ZCML directive handler for a translation domain registration now
loads message catalogs lazily.
If the `zope_i18n_allowed_languages` environment variable is not
provided, the allowed languages are determined at runtime. To
participate in this, a translation domain implementation must now
expose a set of languages in a ``languages`` attribute.
The difficulty in this patch is that the ``INegotiator`` interface
relies on an "allowed languages" set, but this set is not readily
available. In the current definition of a translation domain, we can't
know until we attempt a translation whether a language is supported.
The solution in this patch is to require that a translation domain
exposes the defined languages and then query all translation domains
to determine the allowed languages. It would be nice if we could just
provide a value of ``None`` (meaning any language), but there's
already existing implementations that expect a set.
Changed:
U zope.i18n/branches/lazy-tds/CHANGES.txt
U zope.i18n/branches/lazy-tds/src/zope/i18n/__init__.py
U zope.i18n/branches/lazy-tds/src/zope/i18n/compile.py
U zope.i18n/branches/lazy-tds/src/zope/i18n/interfaces/__init__.py
U zope.i18n/branches/lazy-tds/src/zope/i18n/simpletranslationdomain.py
U zope.i18n/branches/lazy-tds/src/zope/i18n/tests/test_zcml.py
U zope.i18n/branches/lazy-tds/src/zope/i18n/translationdomain.py
U zope.i18n/branches/lazy-tds/src/zope/i18n/zcml.py
-=-
Modified: zope.i18n/branches/lazy-tds/CHANGES.txt
===================================================================
--- zope.i18n/branches/lazy-tds/CHANGES.txt 2012-10-10 08:02:21 UTC (rev 127956)
+++ zope.i18n/branches/lazy-tds/CHANGES.txt 2012-10-10 09:25:17 UTC (rev 127957)
@@ -5,6 +5,16 @@
4.0.0 (unreleased)
------------------
+- The ZCML directive handler for a translation domain registration now
+ loads message catalogs lazily.
+
+- If the `zope_i18n_allowed_languages` environment variable is not
+ provided, the allowed languages are determined at runtime. To
+ participate in this, a translation domain implementation must now
+ expose a set of languages in a ``languages`` attribute.
+
+ This has been implemented for the default implementations.
+
- log DEBUG when loading translations from directories.
- Replaced deprecated ``zope.interface.implements`` usage with equivalent
Modified: zope.i18n/branches/lazy-tds/src/zope/i18n/__init__.py
===================================================================
--- zope.i18n/branches/lazy-tds/src/zope/i18n/__init__.py 2012-10-10 08:02:21 UTC (rev 127956)
+++ zope.i18n/branches/lazy-tds/src/zope/i18n/__init__.py 2012-10-10 09:25:17 UTC (rev 127957)
@@ -15,10 +15,10 @@
"""
import re
-from zope.component import queryUtility
+from zope.component import queryUtility, getAllUtilitiesRegisteredFor
from zope.i18nmessageid import MessageFactory, Message
-from zope.i18n.config import ALLOWED_LANGUAGES
+from zope.i18n import config
from zope.i18n.interfaces import INegotiator
from zope.i18n.interfaces import ITranslationDomain
from zope.i18n.interfaces import IFallbackTranslationDomainFactory
@@ -33,17 +33,23 @@
def negotiate(context):
- """Negotiate language.
+ """Negotiate language."""
- This only works if the languages are set globally, otherwise each message
- catalog needs to do the language negotiation.
- """
- if ALLOWED_LANGUAGES is not None:
- negotiator = queryUtility(INegotiator)
- if negotiator is not None:
- return negotiator.getLanguage(ALLOWED_LANGUAGES, context)
- return None
+ negotiator = queryUtility(INegotiator)
+ if negotiator is not None:
+ languages = config.ALLOWED_LANGUAGES
+ if languages is None:
+ languages = set()
+ for domain in getAllUtilitiesRegisteredFor(ITranslationDomain):
+ try:
+ languages |= domain.languages
+ except AttributeError:
+ continue
+ if languages:
+ return negotiator.getLanguage(languages, context)
+
+
def translate(msgid, domain=None, mapping=None, context=None,
target_language=None, default=None):
"""Translate text.
Modified: zope.i18n/branches/lazy-tds/src/zope/i18n/compile.py
===================================================================
--- zope.i18n/branches/lazy-tds/src/zope/i18n/compile.py 2012-10-10 08:02:21 UTC (rev 127956)
+++ zope.i18n/branches/lazy-tds/src/zope/i18n/compile.py 2012-10-10 09:25:17 UTC (rev 127957)
@@ -45,3 +45,5 @@
fd.close()
except (IOError, OSError, PoSyntaxError):
logger.warn('Error while compiling %s' % pofile)
+
+ return mofile
Modified: zope.i18n/branches/lazy-tds/src/zope/i18n/interfaces/__init__.py
===================================================================
--- zope.i18n/branches/lazy-tds/src/zope/i18n/interfaces/__init__.py 2012-10-10 08:02:21 UTC (rev 127956)
+++ zope.i18n/branches/lazy-tds/src/zope/i18n/interfaces/__init__.py 2012-10-10 09:25:17 UTC (rev 127957)
@@ -132,6 +132,10 @@
description=u"The name of the domain this object represents.",
required=True)
+ languages = Attribute(
+ """Return the set of languages available in this domain."""
+ )
+
def translate(msgid, mapping=None, context=None, target_language=None,
default=None):
"""Return the translation for the message referred to by msgid.
Modified: zope.i18n/branches/lazy-tds/src/zope/i18n/simpletranslationdomain.py
===================================================================
--- zope.i18n/branches/lazy-tds/src/zope/i18n/simpletranslationdomain.py 2012-10-10 08:02:21 UTC (rev 127956)
+++ zope.i18n/branches/lazy-tds/src/zope/i18n/simpletranslationdomain.py 2012-10-10 09:25:17 UTC (rev 127957)
@@ -45,7 +45,14 @@
assert isinstance(messages, dict)
self.messages = messages
+ @property
+ def languages(self):
+ langs = set()
+ for (lang, msgid) in self.messages:
+ langs.add(lang)
+ return langs
+
def translate(self, msgid, mapping=None, context=None,
target_language=None, default=None):
'''See interface ITranslationDomain'''
Modified: zope.i18n/branches/lazy-tds/src/zope/i18n/tests/test_zcml.py
===================================================================
--- zope.i18n/branches/lazy-tds/src/zope/i18n/tests/test_zcml.py 2012-10-10 08:02:21 UTC (rev 127956)
+++ zope.i18n/branches/lazy-tds/src/zope/i18n/tests/test_zcml.py 2012-10-10 09:25:17 UTC (rev 127957)
@@ -43,13 +43,7 @@
from zope.configuration import xmlconfig
super(DirectivesTest, self).setUp()
self.context = xmlconfig.file('meta.zcml', zope.i18n)
- self.allowed = config.ALLOWED_LANGUAGES
- config.ALLOWED_LANGUAGES = None
- def tearDown(self):
- super(DirectivesTest, self).tearDown()
- config.ALLOWED_LANGUAGES = self.allowed
-
def testRegisterTranslations(self):
from zope.configuration import xmlconfig
self.assert_(queryUtility(ITranslationDomain) is None)
@@ -59,27 +53,13 @@
<i18n:registerTranslations directory="locale" />
</configure>
''', self.context)
- path = os.path.join(os.path.dirname(zope.i18n.tests.__file__),
- 'locale', 'en', 'LC_MESSAGES', 'zope-i18n.mo')
util = getUtility(ITranslationDomain, 'zope-i18n')
- self.assertEquals(util._catalogs.get('test'), ['test'])
- self.assertEquals(util._catalogs.get('en'), [unicode(path)])
- def testAllowedTranslations(self):
- from zope.configuration import xmlconfig
- self.assert_(queryUtility(ITranslationDomain) is None)
- config.ALLOWED_LANGUAGES = ('de', 'fr')
- xmlconfig.string(
- template % '''
- <configure package="zope.i18n.tests">
- <i18n:registerTranslations directory="locale" />
- </configure>
- ''', self.context)
- path = os.path.join(os.path.dirname(zope.i18n.tests.__file__),
- 'locale', 'de', 'LC_MESSAGES', 'zope-i18n.mo')
- util = getUtility(ITranslationDomain, 'zope-i18n')
- self.assertEquals(util._catalogs,
- {'test': ['test'], 'de': [unicode(path)]})
+ self.assertEquals(util.translate('test', target_language='test'),
+ u'[[zope-i18n][test]]')
+ self.assertEquals(util.translate('test', target_language='en'), u'test')
+ self.assertEquals(util.translate('New Domain', target_language='en'),
+ u'New Domain translated')
def testRegisterDistributedTranslations(self):
from zope.configuration import xmlconfig
@@ -96,14 +76,7 @@
<i18n:registerTranslations directory="locale2" />
</configure>
''', self.context)
- path1 = os.path.join(os.path.dirname(zope.i18n.tests.__file__),
- 'locale', 'en', 'LC_MESSAGES', 'zope-i18n.mo')
- path2 = os.path.join(os.path.dirname(zope.i18n.tests.__file__),
- 'locale2', 'en', 'LC_MESSAGES', 'zope-i18n.mo')
util = getUtility(ITranslationDomain, 'zope-i18n')
- self.assertEquals(util._catalogs.get('test'), ['test', 'test'])
- self.assertEquals(util._catalogs.get('en'),
- [unicode(path1), unicode(path2)])
msg = util.translate(u'Additional message', target_language='en')
self.assertEquals(msg, u'Additional message translated')
@@ -140,8 +113,6 @@
</configure>
''', self.context)
util = getUtility(ITranslationDomain, 'zope-i18n')
- self.assertEquals(util._catalogs,
- {'test': ['test'], 'en': [unicode(path)]})
msg = util.translate(u"I'm a newer file", target_language='en')
self.assertEquals(msg, u"I'm a newer file translated")
@@ -163,12 +134,9 @@
<i18n:registerTranslations directory="locale3" domain="zope-i18n" />
</configure>
''', self.context)
- path = os.path.join(os.path.dirname(zope.i18n.tests.__file__),
- 'locale3', 'en', 'LC_MESSAGES', 'zope-i18n.mo')
util = getUtility(ITranslationDomain, 'zope-i18n')
- self.assertEquals(util._catalogs,
- {'test': ['test'], 'en': [unicode(path)]})
-
+ self.assertEquals(util.translate('test', target_language='test'),
+ u'[[zope-i18n][test]]')
self.assert_(queryUtility(ITranslationDomain, 'zope-i18n2') is None)
Modified: zope.i18n/branches/lazy-tds/src/zope/i18n/translationdomain.py
===================================================================
--- zope.i18n/branches/lazy-tds/src/zope/i18n/translationdomain.py 2012-10-10 08:02:21 UTC (rev 127956)
+++ zope.i18n/branches/lazy-tds/src/zope/i18n/translationdomain.py 2012-10-10 09:25:17 UTC (rev 127957)
@@ -17,7 +17,9 @@
from zope.i18nmessageid import Message
from zope.i18n import translate, interpolate
from zope.i18n.simpletranslationdomain import SimpleTranslationDomain
-from zope.i18n.interfaces import ITranslationDomain, INegotiator
+from zope.i18n.interfaces import INegotiator
+from zope.i18n.compile import compile_mo_file
+from zope.i18n.gettextmessagecatalog import GettextMessageCatalog
# The configuration should specify a list of fallback languages for the
# site. If a particular catalog for a negotiated language is not available,
@@ -31,6 +33,7 @@
class TranslationDomain(SimpleTranslationDomain):
+ languages = ()
def __init__(self, domain, fallbacks=None):
self.domain = domain
@@ -43,22 +46,34 @@
if fallbacks is None:
fallbacks = LANGUAGE_FALLBACKS
self._fallbacks = fallbacks
+ self._pending = {}
+ self.languages = set()
def _registerMessageCatalog(self, language, catalog_name):
key = language
mc = self._catalogs.setdefault(key, [])
mc.append(catalog_name)
+ self.languages.add(language)
def addCatalog(self, catalog):
self._data[catalog.getIdentifier()] = catalog
self._registerMessageCatalog(catalog.language,
catalog.getIdentifier())
+ def addLanguage(self, lang, path):
+ self._pending.setdefault(lang, []).append(path)
+ self.languages.add(lang)
+
def setLanguageFallbacks(self, fallbacks=None):
if fallbacks is None:
fallbacks = LANGUAGE_FALLBACKS
self._fallbacks = fallbacks
+ def importCatalog(self, lang, lc_messages_path):
+ path = compile_mo_file(self.domain, lc_messages_path)
+ catalog = GettextMessageCatalog(lang, self.domain, path)
+ self.addCatalog(catalog)
+
def translate(self, msgid, mapping=None, context=None,
target_language=None, default=None):
"""See zope.i18n.interfaces.ITranslationDomain"""
@@ -74,6 +89,10 @@
# try to determine target language from negotiator utility
target_language = negotiator.getLanguage(langs, context)
+ paths = self._pending.pop(target_language, ())
+ for path in paths:
+ self.importCatalog(target_language, path)
+
return self._recursive_translate(
msgid, mapping, target_language, default, context)
@@ -124,7 +143,7 @@
# 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.
+ # allows the test language to get the default.
text = self._data[catalog_names[0]].queryMessage(
msgid, default)
else:
Modified: zope.i18n/branches/lazy-tds/src/zope/i18n/zcml.py
===================================================================
--- zope.i18n/branches/lazy-tds/src/zope/i18n/zcml.py 2012-10-10 08:02:21 UTC (rev 127956)
+++ zope.i18n/branches/lazy-tds/src/zope/i18n/zcml.py 2012-10-10 09:25:17 UTC (rev 127957)
@@ -28,8 +28,6 @@
from zope.schema import TextLine
from zope.i18n import config
-from zope.i18n.compile import compile_mo_file
-from zope.i18n.gettextmessagecatalog import GettextMessageCatalog
from zope.i18n.testmessagecatalog import TestMessageCatalog
from zope.i18n.translationdomain import TranslationDomain
from zope.i18n.interfaces import ITranslationDomain
@@ -61,7 +59,7 @@
return lang in config.ALLOWED_LANGUAGES
-def handler(catalogs, name):
+def handler(name, langs):
""" special handler handling the merging of two message catalogs """
gsm = getSiteManager()
# Try to get an existing domain and add the given catalogs to it
@@ -69,8 +67,8 @@
if domain is None:
domain = TranslationDomain(name)
gsm.registerUtility(domain, ITranslationDomain, name=name)
- for catalog in catalogs:
- domain.addCatalog(catalog)
+ for lang, path in langs.items():
+ domain.addLanguage(lang, path)
# make sure we have a TEST catalog for each domain:
domain.addCatalog(TestMessageCatalog(name))
@@ -86,38 +84,29 @@
for language in os.listdir(path):
if not allow_language(language):
continue
+
lc_messages_path = os.path.join(path, language, 'LC_MESSAGES')
if os.path.isdir(lc_messages_path):
- # Preprocess files and update or compile the mo files
- if config.COMPILE_MO_FILES:
- for domain_path in glob(os.path.join(lc_messages_path,
- '%s.po' % domain)):
- domain_file = os.path.basename(domain_path)
- name = domain_file[:-3]
- compile_mo_file(name, lc_messages_path)
- for domain_path in glob(os.path.join(lc_messages_path,
- '%s.mo' % domain)):
+ query = os.path.join(lc_messages_path, '%s.[pm]o' % domain)
+ for domain_path in glob(query):
loaded = True
- domain_file = os.path.basename(domain_path)
- name = domain_file[:-3]
+ base, ext = os.path.splitext(domain_path)
+ name = os.path.basename(base)
if not name in domains:
domains[name] = {}
- domains[name][language] = domain_path
+ domains[name][language] = lc_messages_path
if loaded:
logger.debug('register directory %s' % directory)
# Now create TranslationDomain objects and add them as utilities
for name, langs in domains.items():
- catalogs = []
- for lang, file in langs.items():
- catalogs.append(GettextMessageCatalog(lang, name, file))
# register the necessary actions directly (as opposed to using
# `zope.component.zcml.utility`) since we need the actual utilities
# in place before the merging can be done...
_context.action(
discriminator = None,
callable = handler,
- args = (catalogs, name))
+ args = (name, langs))
# also register the interface for the translation utilities
provides = ITranslationDomain
More information about the checkins
mailing list