Zope Internationalization Support
zope.i18n implements several APIs related to internationalization and localization, offering features such as locale objects based on ICU, Gettext-based message catalogs, and locale discovery for web requests. Version 6.0, released recently, continues its active development with regular updates to support new Python versions and address architectural improvements.
Common errors
-
NameError: global name '_' is not defined
cause Attempting to use the `_` (MessageFactory) function in a Zope Restricted Python script without explicitly importing it or declaring it public.fixIn your `__init__.py` (or similar product setup), declare `YourDomainMessageFactory = MessageFactory('your.domain')` and make it public using `ModuleSecurityInfo('your.packagename').declarePublic('YourDomainMessageFactory')`. Then import it as `from your.packagename import YourDomainMessageFactory as _` in your Restricted Python scripts. -
Numbers or dates are formatted incorrectly (e.g., '2.021' instead of '2021' for a year in German).
cause Default locale formatters in `zope.i18n.locales` provide specific number/date patterns that might not align with desired display formats, especially for region-specific nuances.fixOverride the default locale definitions. This typically involves copying the relevant XML locale file (e.g., `de.xml`) from `zope.i18n.locales.data` into your project's locale directory and modifying the `<numbers>` or `<dates>` section as needed. -
Translated text overflows UI elements or breaks layout.
cause Different languages have varying text lengths (e.g., German words are often longer than English), leading to UI elements not accommodating translated strings.fixDesign UI elements with flexibility in mind (e.g., using dynamic sizing, Flexbox, CSS Grid) rather than fixed widths. Provide ample padding and margins. Thorough localization testing is essential to catch these issues early.
Warnings
- breaking Version 6.0 replaced `pkg_resources` namespace declarations with PEP 420 native namespaces. Projects relying on the old namespace package style might break.
- breaking Support for Python versions 3.8 and older has been dropped in recent `zope.i18n` versions (e.g., 5.3 dropped 3.8, 5.0 dropped 3.5/3.6).
- gotcha When using `zope.i18n.translate` with a `zope.i18nmessageid.Message` object, explicitly passed `mapping` arguments might be overridden by an empty default mapping from the `Message` object, preventing interpolation.
- gotcha Translation (`.mo`) files are often not included in releases or automatically compiled. Manual compilation or specific environment variable setup is required.
Install
-
pip install zope.i18n
Imports
- translate
from zope.i18n import translate
- MessageFactory
from zope.i18nmessageid import MessageFactory
- getRequest
from zope.globalrequest import getRequest
Quickstart
import os
from zope.i18n import translate
from zope.i18nmessageid import MessageFactory
# Simulate a request object for translation context
class MockRequest:
def __init__(self, lang):
self.LANGUAGE_NEGOTIATED = lang
self.locale = MockLocale(lang)
class MockLocale:
def __init__(self, lang):
self.getLocaleID = lambda: lang
# Define a message factory for your domain
_ = MessageFactory('my.application')
# A translatable message
msg = _('hello_world_id', default='Hello, World!', mapping={'name': 'User'})
# Example of a simple translation service (in a real Zope app, this would be set up)
def get_translation_service(request):
# In a real Zope setup, this would resolve the utility
# For this example, we just return the 'translate' function itself
return lambda message, target_language=None, default=None, mapping=None, context=None, domain=None:
if target_language == 'de':
return f"Hallo, {mapping.get('name', '')}!"
elif target_language == 'fr':
return f"Bonjour, {mapping.get('name', '')}!"
return default if default else str(message) # Fallback
# Translate the message
# Often, the request context implicitly provides the target_language
mock_request_en = MockRequest('en')
mock_request_de = MockRequest('de')
mock_request_fr = MockRequest('fr')
# Using translate with a mocked context and explicit target language
translated_en = translate(msg, target_language='en', context=mock_request_en, mapping={'name': 'Alice'})
translated_de = translate(msg, target_language='de', context=mock_request_de, mapping={'name': 'Bob'})
translated_fr = translate(msg, target_language='fr', context=mock_request_fr, mapping={'name': 'Charlie'})
print(f"English: {translated_en}")
print(f"German: {translated_de}")
print(f"French: {translated_fr}")
# Example for automatic MO file compilation (if 'compile' extra is installed)
os.environ['ZOPE_I18N_COMPILE_MO_FILES'] = 'true'
# In a real application, MO files would now be compiled on startup if .po files exist
print(f"\nZOPE_I18N_COMPILE_MO_FILES env var set: {os.environ.get('ZOPE_I18N_COMPILE_MO_FILES')}")