1475 | | |
1476 | | === Internationalize The Application (I18N) === |
1477 | | To internationalize our application we'll use [http://babel.edgewall.org Babel]. |
1478 | | {{{ |
1479 | | easy_install Babel |
1480 | | }}} |
1481 | | |
1482 | | To get the basic info on how to work with message catalog using Babel [http://babel.edgewall.org/wiki/Documentation/0.9/messages.html read this], I'll just follow you through the basic procedures. |
1483 | | |
1484 | | 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: |
1485 | | {{{ |
1486 | | # Extraction from Python source files |
1487 | | |
1488 | | [python: **.py] |
1489 | | |
1490 | | # Extraction from Genshi HTML and text templates |
1491 | | |
1492 | | [genshi: **/templates/**.html |
1493 | | }}} |
1494 | | 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. |
1495 | | |
1496 | | Now let's extract some messages to be translated: |
1497 | | {{{ |
1498 | | pybabel extract -o geddit/locale/geddit.pot -F ./mapping.cfg geddit |
1499 | | }}} |
1500 | | |
1501 | | The output should be similar to: |
1502 | | {{{ |
1503 | | extracting messages from geddit/__init__.py |
1504 | | extracting messages from geddit/controller.py |
1505 | | extracting messages from geddit/form.py |
1506 | | extracting messages from geddit/model.py |
1507 | | extracting messages from geddit/translator.py |
1508 | | extracting messages from geddit/lib/__init__.py |
1509 | | extracting messages from geddit/lib/ajax.py |
1510 | | extracting messages from geddit/lib/template.py |
1511 | | extracting messages from geddit/templates/_comment.html |
1512 | | extracting messages from geddit/templates/_form.html |
1513 | | extracting messages from geddit/templates/comment.html |
1514 | | extracting messages from geddit/templates/index.html |
1515 | | extracting messages from geddit/templates/info.html |
1516 | | extracting messages from geddit/templates/layout.html |
1517 | | extracting messages from geddit/templates/submit.html |
1518 | | writing PO template file to geddit/locale/geddit.pot |
1519 | | }}} |
1520 | | |
1521 | | Now let's create the English catalog which normally isn't translated: |
1522 | | {{{ |
1523 | | pybabel init -D geddit -i geddit/locale/geddit.pot -d geddit/locale/ -l en |
1524 | | }}} |
1525 | | |
1526 | | And, since I'm Portuguese, we'll create a Portuguese catalog to serve as an example: |
1527 | | {{{ |
1528 | | pybabel init -D geddit -i geddit/locale/geddit.pot -d geddit/locale/ -l pt_PT |
1529 | | }}} |
1530 | | On this step you can create a catalog in your mother language if not english so that you can see Geddit translated. |
1531 | | |
1532 | | Edit the contents of the resulting `geddit.po` for the locale you created, translate it, save and exit. |
1533 | | |
1534 | | Next step will involve compiling these catalogs so that they can be used by python's gettext module: |
1535 | | {{{ |
1536 | | pybabel compile -D geddit -d geddit/locale/ -f --statistics |
1537 | | }}} |
1538 | | |
1539 | | Now, our Geddit application needs to use and know how to use these translated catalogs. |
1540 | | |
1541 | | Let's modify `geddit/controller.py` with(this is a diff): |
1542 | | {{{ |
1543 | | #!diff |
1544 | | Index: geddit/controller.py |
1545 | | =================================================================== |
1546 | | --- geddit/controller.py (revision 766) |
1547 | | +++ geddit/controller.py (working copy) |
1548 | | @@ -92,8 +92,40 @@ |
1549 | | links = sorted(self.data.values(), key=operator.attrgetter('time')) |
1550 | | return template.render(links=links) |
1551 | | |
1552 | | + @cherrypy.expose |
1553 | | + def set_lang(self, language): |
1554 | | + import gettext |
1555 | | + import formencode |
1556 | | + import __builtin__ |
1557 | | + locale_dir = os.path.join(os.path.dirname(__file__), 'locale') |
1558 | | + domain = 'geddit' |
1559 | | + codeset= 'utf-8' |
1560 | | + |
1561 | | + gettext.bindtextdomain(domain, locale_dir) |
1562 | | + gettext.textdomain(domain) |
1563 | | + |
1564 | | + try: |
1565 | | + translator = gettext.translation(domain, |
1566 | | + locale_dir, |
1567 | | + languages=[language], |
1568 | | + codeset=codeset) |
1569 | | + except IOError, error: |
1570 | | + language=['en'] |
1571 | | + translator = gettext.translation(domain, |
1572 | | + locale_dir, |
1573 | | + languages=language, |
1574 | | + codeset=codeset) |
1575 | | + formencode.api.set_stdtranslation(languages=[language]) |
1576 | | + __builtin__._ = translator.ugettext |
1577 | | + raise cherrypy.HTTPRedirect('/') |
1578 | | + |
1579 | | + |
1580 | | |
1581 | | def main(filename): |
1582 | | + import __builtin__ |
1583 | | + from gettext import NullTranslations |
1584 | | + |
1585 | | + __builtin__._ = NullTranslations().ugettext |
1586 | | # load data from the pickle file, or initialize it to an empty list |
1587 | | if os.path.exists(filename): |
1588 | | fileobj = open(filename, 'rb') |
1589 | | }}} |
1590 | | |
1591 | | Our `geddit/lib/template.py` with(also diff): |
1592 | | {{{ |
1593 | | #!diff |
1594 | | Index: geddit/lib/template.py |
1595 | | =================================================================== |
1596 | | --- geddit/lib/template.py (revision 766) |
1597 | | +++ geddit/lib/template.py (working copy) |
1598 | | @@ -4,6 +4,7 @@ |
1599 | | from genshi.core import Stream |
1600 | | from genshi.output import encode, get_serializer |
1601 | | from genshi.template import Context, TemplateLoader |
1602 | | +from genshi.filters import Translator |
1603 | | |
1604 | | from geddit.lib import ajax |
1605 | | |
1606 | | @@ -42,6 +43,7 @@ |
1607 | | template = loader.load(args[0]) |
1608 | | else: |
1609 | | template = cherrypy.thread_data.template |
1610 | | + template.filters.insert(0, Translator(_)) |
1611 | | ctxt = Context(url=cherrypy.url) |
1612 | | ctxt.push(kwargs) |
1613 | | return template.generate(ctxt) |
1614 | | }}} |
1615 | | |
1616 | | And finaly our `geddit/templates/layout.html` with(also diff): |
1617 | | {{{ |
1618 | | #!diff |
1619 | | Index: geddit/templates/layout.html |
1620 | | =================================================================== |
1621 | | --- geddit/templates/layout.html (revision 766) |
1622 | | +++ geddit/templates/layout.html (working copy) |
1623 | | @@ -19,6 +19,16 @@ |
1624 | | <a href="/"><img src="${url('/media/logo.gif')}" width="201" height="79" alt="geddit?" /></a> |
1625 | | </div> |
1626 | | <div id="content"> |
1627 | | + <form method="post" id="lang_choose" name="lang_choose" action="${url('/set_lang')}"> |
1628 | | + <label for="language">Language:</label> |
1629 | | + <select id="language" name="language" onChange="$('#lang_choose').submit()"> |
1630 | | + <option value="en">English</option> |
1631 | | + <option value="pt_PT">Portuguese</option> |
1632 | | + </select> |
1633 | | + </form> |
1634 | | + <noscript> |
1635 | | + <input type="submit" name="submit" value="Update"/> |
1636 | | + </noscript> |
1637 | | ${select('*|text()')} |
1638 | | </div> |
1639 | | <div id="footer"> |
1640 | | }}} |
1641 | | |
1642 | | This will allow the user to select the language. |
1643 | | [[Image(tutorial07.png)]] |
1644 | | |
1645 | | And that wraps it up! |
1646 | | |