Tool to integrate babel (localization and gettext translations) in CherryPy.
# -*- coding: utf-8 -*- """Internationalization and Localization for CherryPy **Tested with CherryPy 3.1.2** This tool provides locales and loads translations based on the HTTP-ACCEPT-LANGUAGE header. If no header is send or the given language is not supported by the application, it falls back to `tools.I18nTool.default`. Set `default` to the native language used in your code for strings, so you must not provide a .mo file for it. The tool uses `babel<http://babel.edgewall.org>`_ for localization and handling translations. Within your Python code you can use four functions defined in this module and the loaded locale provided as `cherrypy.response.i18n.locale`. Example:: from i18n_tool import ugettext as _, ungettext class MyController(object): @cherrypy.expose def index(self): loc = cherrypy.response.i18n.locale s1 = _(u'Translateable string') s2 = ungettext(u'There is one string.', u'There are more strings.', 2) return u'<br />'.join([s1, s2, loc.display_name]) If you have code (e.g. database models) that is executed before the response object is available, use the *_lazy functions to mark the strings translateable. They will be translated later on, when the text is used (and hopefully the response object is available then). Example:: from i18n_tool import ugettext_lazy class Model: def __init__(self): name = ugettext_lazy(u'Name of the model') For your templates read the documentation of your template engine how to integrate babel with it. I think `Genshi<http://genshi.edgewall.org>`_ and `Jinja 2<http://jinja.pocoo.org`_ support it out of the box. Settings for the CherryPy configuration:: [/] tools.I18nTool.on = True tools.I18nTool.default = Your language with territory (e.g. 'en_US') tools.I18nTool.mo_dir = Directory holding the locale directories tools.I18nTool.domain = Your gettext domain (e.g. application name) The mo_dir must contain subdirectories named with the language prefix for all translations, containing a LC_MESSAGES dir with the compiled catalog file in it. Example:: [/] tools.I18nTool.on = True tools.I18nTool.default = 'en_US' tools.I18nTool.mo_dir = '/home/user/web/myapp/i18n' tools.I18nTool.domain = 'myapp' Now the tool will look for a file called myapp.mo in /home/user/web/myapp/i18n/en/LC_MESSACES/ or generic: <mo_dir>/<language>/LC_MESSAGES/<domain>.mo That's it. :License: BSD :Author: Thorsten Weimann <thorsten.weimann (at) gmx (dot) net> :Date: 2010-02-08 """ import re import cherrypy from babel.core import Locale, UnknownLocaleError from babel.support import Translations, LazyProxy try: # Python 2.6 and above from collections import namedtuple Lang = namedtuple('Lang', 'locale trans') except ImportError: # Python 2.5 class Lang(object): def __init__(self, locale, trans): self.locale = locale self.trans = trans # Cache for Translations and Locale objects _languages = {} # Exception class ImproperlyConfigured(Exception): """Raised if no known locale were found.""" # Public translation functions def ugettext(message): """Standard translation function. You can use it in all your exposed methods and everywhere where the response object is available. :parameters: message : Unicode The message to translate. :returns: The translated message. :rtype: Unicode """ return cherrypy.response.i18n.trans.ugettext(message) def ugettext_lazy(message): """Like ugettext, but lazy. :returns: A proxy for the translation object. :rtype: LazyProxy """ def get_translation(): return cherrypy.response.i18n.trans.ugettext(message) return LazyProxy(get_translation) def ungettext(singular, plural, num): """Like ugettext, but considers plural forms. :parameters: singular : Unicode The message to translate in singular form. plural : Unicode The message to translate in plural form. num : Integer Number to apply the plural formula on. If num is 1 or no translation is found, singular is returned. :returns: The translated message as singular or plural. :rtype: Unicode """ return cherrypy.response.i18n.trans.ungettext(singular, plural, num) def ungettext_lazy(singular, plural, num): """Like ungettext, but lazy. :returns: A proxy for the translation object. :rtype: LazyProxy """ def get_translation(): return cherrypy.response.i18n.trans.ungettext(singular, plural, num) return LazyProxy(get_translation) def load_translation(langs, dirname, domain): """Loads the first existing translations for known locale and saves the `Lang` object in a global cache for faster lookup on the next request. :parameters: langs : List List of languages as returned by `parse_accept_language_header`. dirname : String Directory of the translations (`tools.I18nTool.mo_dir`). domain : String Gettext domain of the catalog (`tools.I18nTool.domain`). :returns: Lang object with two attributes (Lang.trans = the translations object, Lang.locale = the corresponding Locale object). :rtype: Lang :raises: ImproperlyConfigured if no locale where known. """ locale = None for lang in langs: short = lang[:2].lower() try: locale = Locale.parse(lang) if (domain, short) in _languages: return _languages[(domain, short)] trans = Translations.load(dirname, short, domain) except (ValueError, UnknownLocaleError): continue # If the translation was found, exit loop if isinstance(trans, Translations): break if locale is None: raise ImproperlyConfigured('Default locale not known.') _languages[(domain, short)] = res = Lang(locale, trans) return res def get_lang(mo_dir, default, domain): """Main function which will be invoked during the request by `I18nTool`. If the SessionTool is on and has a lang key, this language get the highest priority. Default language get the lowest priority. The `Lang` object will be saved as `cherrypy.response.i18n` and the language string will also saved as `cherrypy.session['_lang_']` (if SessionTool is on). :parameters: mo_dir : String `tools.I18nTool.mo_dir` default : String `tools.I18nTool.default` domain : String `tools.I18nTool.domain` """ langs = [x.value.replace('-', '_') for x in cherrypy.request.headers.elements('Accept-Language')] sessions_on = cherrypy.request.config.get('tools.sessions.on', False) if sessions_on and cherrypy.session.get('_lang_', ''): langs.insert(0, cherrypy.session.get('_lang_', '__')) langs.append(default) loc = load_translation(langs, mo_dir, domain) cherrypy.response.i18n = loc if sessions_on: cherrypy.session['_lang_'] = str(loc.locale) def set_lang(): """Sets the Content-Language response header (if not already set) to the language of `cherrypy.response.i18n.locale`. """ if 'Content-Language' not in cherrypy.response.headers: cherrypy.response.headers['Content-Language'] = str( cherrypy.response.i18n.locale) class I18nTool(cherrypy.Tool): """Tool to integrate babel translations in CherryPy.""" def __init__(self): self._name = 'I18nTool' self._point = 'before_handler' self.callable = get_lang # Make sure, session tool (priority 50) is loaded before self._priority = 100 def _setup(self): c = cherrypy.request.config if c.get('tools.staticdir.on', False) or \ c.get('tools.staticfile.on', False): return cherrypy.Tool._setup(self) cherrypy.request.hooks.attach('before_finalize', set_lang) cherrypy.tools.I18nTool = I18nTool()
Instead of parse_accept_language_header, try: [x.value for x in request.headers.elements['Accept-Language']]
fumanchu
Thank you for the advise, [x.value for x in request.headers.elements('Accept-Language')] worked for me (CherryPy 3.1.2). Code is updated.
Whitie
Attachments
- i18n_tool.py (8.0 kB) - added by thorsten.weimann@gmx.net on 02/08/10 11:03:15.

