| 3 | | ''Noone has written this yet. If you'd like to give it a shot, this page is all yours.'' |
| | 3 | To internationalize our application we'll use [http://babel.edgewall.org Babel]. |
| | 4 | {{{ |
| | 5 | easy_install Babel |
| | 6 | }}} |
| | 7 | |
| | 8 | 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. |
| | 9 | |
| | 10 | 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: |
| | 11 | {{{ |
| | 12 | # Extraction from Python source files |
| | 13 | |
| | 14 | [python: **.py] |
| | 15 | |
| | 16 | # Extraction from Genshi HTML and text templates |
| | 17 | |
| | 18 | [genshi: **/templates/**.html |
| | 19 | }}} |
| | 20 | 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. |
| | 21 | |
| | 22 | Now let's extract some messages to be translated: |
| | 23 | {{{ |
| | 24 | pybabel extract -o geddit/locale/geddit.pot -F ./mapping.cfg geddit |
| | 25 | }}} |
| | 26 | |
| | 27 | The output should be similar to: |
| | 28 | {{{ |
| | 29 | extracting messages from geddit/__init__.py |
| | 30 | extracting messages from geddit/controller.py |
| | 31 | extracting messages from geddit/form.py |
| | 32 | extracting messages from geddit/model.py |
| | 33 | extracting messages from geddit/translator.py |
| | 34 | extracting messages from geddit/lib/__init__.py |
| | 35 | extracting messages from geddit/lib/ajax.py |
| | 36 | extracting messages from geddit/lib/template.py |
| | 37 | extracting messages from geddit/templates/_comment.html |
| | 38 | extracting messages from geddit/templates/_form.html |
| | 39 | extracting messages from geddit/templates/comment.html |
| | 40 | extracting messages from geddit/templates/index.html |
| | 41 | extracting messages from geddit/templates/info.html |
| | 42 | extracting messages from geddit/templates/layout.html |
| | 43 | extracting messages from geddit/templates/submit.html |
| | 44 | writing PO template file to geddit/locale/geddit.pot |
| | 45 | }}} |
| | 46 | |
| | 47 | Now let's create the English catalog which normally isn't translated: |
| | 48 | {{{ |
| | 49 | pybabel init -D geddit -i geddit/locale/geddit.pot -d geddit/locale/ -l en |
| | 50 | }}} |
| | 51 | |
| | 52 | And, since I'm Portuguese, we'll create a Portuguese catalog to serve as an example: |
| | 53 | {{{ |
| | 54 | pybabel init -D geddit -i geddit/locale/geddit.pot -d geddit/locale/ -l pt_PT |
| | 55 | }}} |
| | 56 | On this step you can create a catalog in your mother language if not english so that you can see Geddit translated. |
| | 57 | |
| | 58 | Edit the contents of the resulting `geddit.po` for the locale you created, translate it, save and exit. |
| | 59 | |
| | 60 | Next step will involve compiling these catalogs so that they can be used by python's gettext module: |
| | 61 | {{{ |
| | 62 | pybabel compile -D geddit -d geddit/locale/ -f --statistics |
| | 63 | }}} |
| | 64 | |
| | 65 | Now, our Geddit application needs to use and know how to use these translated catalogs. |
| | 66 | |
| | 67 | Let's modify `geddit/controller.py` with(this is a diff): |
| | 68 | {{{ |
| | 69 | #!diff |
| | 70 | Index: geddit/controller.py |
| | 71 | =================================================================== |
| | 72 | --- geddit/controller.py (revision 766) |
| | 73 | +++ geddit/controller.py (working copy) |
| | 74 | @@ -92,8 +92,40 @@ |
| | 75 | links = sorted(self.data.values(), key=operator.attrgetter('time')) |
| | 76 | return template.render(links=links) |
| | 77 | |
| | 78 | + @cherrypy.expose |
| | 79 | + def set_lang(self, language): |
| | 80 | + import gettext |
| | 81 | + import formencode |
| | 82 | + import __builtin__ |
| | 83 | + locale_dir = os.path.join(os.path.dirname(__file__), 'locale') |
| | 84 | + domain = 'geddit' |
| | 85 | + codeset= 'utf-8' |
| | 86 | + |
| | 87 | + gettext.bindtextdomain(domain, locale_dir) |
| | 88 | + gettext.textdomain(domain) |
| | 89 | + |
| | 90 | + try: |
| | 91 | + translator = gettext.translation(domain, |
| | 92 | + locale_dir, |
| | 93 | + languages=[language], |
| | 94 | + codeset=codeset) |
| | 95 | + except IOError, error: |
| | 96 | + language=['en'] |
| | 97 | + translator = gettext.translation(domain, |
| | 98 | + locale_dir, |
| | 99 | + languages=language, |
| | 100 | + codeset=codeset) |
| | 101 | + formencode.api.set_stdtranslation(languages=[language]) |
| | 102 | + __builtin__._ = translator.ugettext |
| | 103 | + raise cherrypy.HTTPRedirect('/') |
| | 104 | + |
| | 105 | + |
| | 106 | |
| | 107 | def main(filename): |
| | 108 | + import __builtin__ |
| | 109 | + from gettext import NullTranslations |
| | 110 | + |
| | 111 | + __builtin__._ = NullTranslations().ugettext |
| | 112 | # load data from the pickle file, or initialize it to an empty list |
| | 113 | if os.path.exists(filename): |
| | 114 | fileobj = open(filename, 'rb') |
| | 115 | }}} |
| | 116 | |
| | 117 | Our `geddit/lib/template.py` with(also diff): |
| | 118 | {{{ |
| | 119 | #!diff |
| | 120 | Index: geddit/lib/template.py |
| | 121 | =================================================================== |
| | 122 | --- geddit/lib/template.py (revision 766) |
| | 123 | +++ geddit/lib/template.py (working copy) |
| | 124 | @@ -4,6 +4,7 @@ |
| | 125 | from genshi.core import Stream |
| | 126 | from genshi.output import encode, get_serializer |
| | 127 | from genshi.template import Context, TemplateLoader |
| | 128 | +from genshi.filters import Translator |
| | 129 | |
| | 130 | from geddit.lib import ajax |
| | 131 | |
| | 132 | @@ -42,6 +43,7 @@ |
| | 133 | template = loader.load(args[0]) |
| | 134 | else: |
| | 135 | template = cherrypy.thread_data.template |
| | 136 | + template.filters.insert(0, Translator(_)) |
| | 137 | ctxt = Context(url=cherrypy.url) |
| | 138 | ctxt.push(kwargs) |
| | 139 | return template.generate(ctxt) |
| | 140 | }}} |
| | 141 | |
| | 142 | And finaly our `geddit/templates/layout.html` with(also diff): |
| | 143 | {{{ |
| | 144 | #!diff |
| | 145 | Index: geddit/templates/layout.html |
| | 146 | =================================================================== |
| | 147 | --- geddit/templates/layout.html (revision 766) |
| | 148 | +++ geddit/templates/layout.html (working copy) |
| | 149 | @@ -19,6 +19,16 @@ |
| | 150 | <a href="/"><img src="${url('/media/logo.gif')}" width="201" height="79" alt="geddit?" /></a> |
| | 151 | </div> |
| | 152 | <div id="content"> |
| | 153 | + <form method="post" id="lang_choose" name="lang_choose" action="${url('/set_lang')}"> |
| | 154 | + <label for="language">Language:</label> |
| | 155 | + <select id="language" name="language" onChange="$('#lang_choose').submit()"> |
| | 156 | + <option value="en">English</option> |
| | 157 | + <option value="pt_PT">Portuguese</option> |
| | 158 | + </select> |
| | 159 | + </form> |
| | 160 | + <noscript> |
| | 161 | + <input type="submit" name="submit" value="Update"/> |
| | 162 | + </noscript> |
| | 163 | ${select('*|text()')} |
| | 164 | </div> |
| | 165 | <div id="footer"> |
| | 166 | }}} |
| | 167 | |
| | 168 | This will allow the user to select the language. |
| | 169 | [[Image(tutorial07.png)]] |
| | 170 | |
| | 171 | And that wraps it up! |