Edgewall Software

Genshi Tutorial: Adding Internationalization

To internationalize our application we'll use Babel.

easy_install Babel

To get the basic info on how to work with message catalog using Babel read this, I'll just follow you through the basic procedures.

First we'll create a locale directory under geddit which will hold the message catalogs. Next, we'll create a mapping file which will tell Babel how to handle the several type of files. Create a mappings.cfg on the directory above the geddit one and add inside:

# Extraction from Python source files

[python: **.py]

# Extraction from Genshi HTML and text templates

[genshi: **/templates/**.html]

The above will tell Babel that .py files should be handled by the python extractor and that the .html files should be handled by the Genshi extractor.

Now let's extract some messages to be translated:

pybabel extract -o geddit/locale/geddit.pot -F ./mappings.cfg geddit

The output should be similar to:

extracting messages from geddit/__init__.py
extracting messages from geddit/controller.py
extracting messages from geddit/form.py
extracting messages from geddit/model.py
extracting messages from geddit/translator.py
extracting messages from geddit/lib/__init__.py
extracting messages from geddit/lib/ajax.py
extracting messages from geddit/lib/template.py
extracting messages from geddit/templates/_comment.html
extracting messages from geddit/templates/_form.html
extracting messages from geddit/templates/comment.html
extracting messages from geddit/templates/index.html
extracting messages from geddit/templates/info.html
extracting messages from geddit/templates/layout.html
extracting messages from geddit/templates/submit.html
writing PO template file to geddit/locale/geddit.pot

Now let's create the English catalog which normally isn't translated:

pybabel init -D geddit -i geddit/locale/geddit.pot -d geddit/locale/ -l en

And, since I'm Portuguese, we'll create a Portuguese catalog to serve as an example:

pybabel init -D geddit -i geddit/locale/geddit.pot -d geddit/locale/ -l pt_PT

On this step you can create a catalog in your mother language if not english so that you can see Geddit translated.

Edit the contents of the resulting geddit.po for the locale you created, translate it, save and exit.

Next step will involve compiling these catalogs so that they can be used by python's gettext module:

pybabel compile -D geddit -d geddit/locale/ -f --statistics

Now, our Geddit application needs to use and know how to use these translated catalogs.

Let's modify geddit/controller.py with(this is a diff):

  • geddit/controller.py

    9292            links = sorted(self.data.values(), key=operator.attrgetter('time'))
    9393            return template.render(links=links)
     95    @cherrypy.expose
     96    def set_lang(self, language):
     97       import gettext
     98       import formencode
     99       import __builtin__
     100       locale_dir = os.path.join(os.path.dirname(__file__), 'locale')
     101       domain = 'geddit'
     102       codeset= 'utf-8'
     104       gettext.bindtextdomain(domain, locale_dir)
     105       gettext.textdomain(domain)
     107       try:
     108            translator = gettext.translation(domain,
     109                                         locale_dir,
     110                                         languages=[language],
     111                                         codeset=codeset)
     112       except IOError, error:
     113            language=['en']
     114            translator = gettext.translation(domain,
     115                                         locale_dir,
     116                                         languages=language,
     117                                         codeset=codeset)
     118       formencode.api.set_stdtranslation(languages=[language])
     119       __builtin__._ = translator.ugettext
     120       raise cherrypy.HTTPRedirect('/')
    96124def main(filename):
     125    import __builtin__
     126    from gettext import NullTranslations
     128    __builtin__._ = NullTranslations().ugettext
    97129    # load data from the pickle file, or initialize it to an empty list
    98130    if os.path.exists(filename):
    99131        fileobj = open(filename, 'rb')

Our geddit/lib/template.py with(also diff):

  • geddit/lib/template.py

    44from genshi.core import Stream
    55from genshi.output import encode, get_serializer
    66from genshi.template import Context, TemplateLoader
     7from genshi.filters import Translator
    89from geddit.lib import ajax
    4243        template = loader.load(args[0])
    4344    else:
    4445        template = cherrypy.thread_data.template
     46    template.filters.insert(0, Translator(_))
    4547    ctxt = Context(url=cherrypy.url)
    4648    ctxt.push(kwargs)
    4749    return template.generate(ctxt)

WARNING: there's an error in the above code, please see #238.

And finaly our geddit/templates/layout.html with(also diff):

  • geddit/templates/layout.html

    1919        <a href="/"><img src="${url('/media/logo.gif')}" width="201" height="79" alt="geddit?" /></a>
    2020      </div>
    2121      <div id="content">
     22        <form method="post" id="lang_choose" name="lang_choose" action="${url('/set_lang')}">
     23        <label for="language">Language:</label>
     24        <select id="language" name="language" onChange="$('#lang_choose').submit()">
     25          <option value="en">English</option>
     26          <option value="pt_PT">Portuguese</option>
     27        </select>
     28        </form>
     29        <noscript>
     30          <input type="submit" name="submit" value="Update"/>
     31        </noscript>
    2232        ${select('*|text()')}
    2333      </div>
    2434      <div id="footer">

This will allow the user to select the language. Move screenshot over, too

And that wraps it up!

Last modified 12 years ago Last modified on Jan 26, 2011, 6:18:33 PM

Attachments (1)

Download all attachments as: .zip