Editing Internationalize a Python application
Warning: You are not logged in.
Your IP address will be recorded in this page's edit history.
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision | Your text | ||
Line 21: | Line 21: | ||
Ok, this is what we need to do. Let's code. | Ok, this is what we need to do. Let's code. | ||
+ | |||
+ | == Configure gettext Define the '_()' function == | ||
+ | |||
+ | If you don't care too much about the details, just copy this [https://garage.maemo.org/plugins/ggit/browse.php/?p=mussorgsky;a=blob;f=src/i18n.py;hb=HEAD i18n.py] file in your src/ directory ('''change the APP_NAME''' variable there!), and in every .py file in your module that needs to translate any string, add these lines at the top: | ||
+ | <source lang="python"> | ||
+ | import i18n | ||
+ | _ = i18n.language.gettext | ||
+ | </source> | ||
+ | Done. If you are curious about what is going on there, the file has plenty of comments explaining every line. | ||
== Mark strings for i18n == | == Mark strings for i18n == | ||
Line 38: | Line 47: | ||
self.set_title ( _("All music") ) <------- ADD THIS! | self.set_title ( _("All music") ) <------- ADD THIS! | ||
</source> | </source> | ||
- | == | + | == Generate template for the translators == |
- | + | Create a po folder in your project (usually at the same level as src/) and run the following command from the top directory of your project. You can do this inside or outside Scratchbox, there is no difference. | |
- | + | xgettext --language=Python --keyword=_ --output=po/PROJECTNAME.pot `find . -name "*.py"` | |
- | === | + | |
- | + | ||
- | + | ||
- | + | ||
- | + | It will parse files, written in Python, looking for strings marked with the keyword (function name) '_' and saving the output in a file called '<code>po/PROJECTNAME.pot</code>'. The list of files is the last argument. I like to use "find", but you can manually put a list of files there. | |
- | + | ||
+ | That command will generate the <code>.pot</code> file. Easy, isn't it? | ||
+ | |||
+ | == How to add translations == | ||
+ | |||
+ | Now you want to get a translation in an specific language. You need to generate a <code>.po</code> for it. | ||
- | |||
Go to the <code>po/</code> folder and run this command (again, inside/outside scratchbox doesn't matter): | Go to the <code>po/</code> folder and run this command (again, inside/outside scratchbox doesn't matter): | ||
Line 68: | Line 77: | ||
"Content-Type: text/plain; charset=utf-8\n" <--- THIS MUST BE UTF-8 | "Content-Type: text/plain; charset=utf-8\n" <--- THIS MUST BE UTF-8 | ||
- | If you don't know the locale of a language, can check [http://www.roseindia.net/tutorials/I18N/locales-list.shtml | + | If you don't know the locale of a language, can check [http://www.roseindia.net/tutorials/I18N/locales-list.shtml this list]. |
+ | |||
+ | == Include translations in your installation == | ||
+ | |||
+ | This part is more tricky, because it depends on your build system. I'll explain how it goes with the common python 'distutils' (what makes the usual ''<code>python setup.py install</code>'' command work). In distutils all the magic happens in the ''<code>setup.py</code>'' file. | ||
+ | |||
+ | First of all, if in the previous steps you have added the <code>i18n.py</code> file to your source tree, don't forget to include it in your <code>data_files</code> list! | ||
+ | |||
+ | Then, copy to the root folder of your project the file [https://garage.maemo.org/plugins/ggit/browse.php/?p=mussorgsky;a=blob;f=msgfmt.py;hb=HEAD msgfmt.py]. It is a program that translates the .po files into the binary .mo files that the application needs. | ||
+ | |||
+ | In your <code>setup.py</code> file you need to add all this code. It "overloads" the default 'build' and 'install' instructions to include the translations in the process. Now in 'build' time it will generate the .mo files (using the <code>msgfmt.py</code> we added just now), and in 'install' time it will include the '<code>.mo</code>' files in the <code>data_files</code> list. The code is copied from this a [https://garage.maemo.org/plugins/ggit/browse.php/?p=mussorgsky;a=blob;f=setup.py;hb=HEAD setup.py] file. | ||
+ | |||
+ | <source lang="python"> | ||
+ | from distutils.core import setup | ||
+ | from distutils import cmd | ||
+ | from distutils.command.install_data import install_data as _install_data | ||
+ | from distutils.command.build import build as _build | ||
+ | |||
+ | import msgfmt | ||
+ | import os | ||
+ | </source> | ||
+ | This command compiles every <code>.po</code> file under the <code>po/</code> folder into a <code>.mo</code> (the <code>.mo</code> files will be under <code>build/locale/</code>) | ||
+ | <source lang="python"> | ||
+ | class build_trans(cmd.Command): | ||
+ | description = 'Compile .po files into .mo files' | ||
+ | def initialize_options(self): | ||
+ | pass | ||
+ | |||
+ | def finalize_options(self): | ||
+ | pass | ||
+ | |||
+ | def run(self): | ||
+ | po_dir = os.path.join(os.path.dirname(os.curdir), 'po') | ||
+ | for path, names, filenames in os.walk(po_dir): | ||
+ | for f in filenames: | ||
+ | if f.endswith('.po'): | ||
+ | lang = f[:len(f) - 3] | ||
+ | src = os.path.join(path, f) | ||
+ | dest_path = os.path.join('build', 'locale', lang, 'LC_MESSAGES') | ||
+ | dest = os.path.join(dest_path, 'mussorgsky.mo') | ||
+ | if not os.path.exists(dest_path): | ||
+ | os.makedirs(dest_path) | ||
+ | if not os.path.exists(dest): | ||
+ | print 'Compiling %s' % src | ||
+ | msgfmt.make(src, dest) | ||
+ | else: | ||
+ | src_mtime = os.stat(src)[8] | ||
+ | dest_mtime = os.stat(dest)[8] | ||
+ | if src_mtime > dest_mtime: | ||
+ | print 'Compiling %s' % src | ||
+ | msgfmt.make(src, dest) | ||
+ | </source> | ||
+ | Now we append the previous command to the 'build' command. | ||
+ | <source lang="python"> | ||
+ | class build(_build): | ||
+ | sub_commands = _build.sub_commands + [('build_trans', None)] | ||
+ | def run(self): | ||
+ | _build.run(self) | ||
+ | </source> | ||
+ | Installation time: put every <code>.mo</code> file under <code>build/locale</code> in the <code>data_files</code> list (the list of things to be installed) and call the default 'install' operation | ||
+ | <source lang="python"> | ||
+ | class install_data(_install_data): | ||
+ | |||
+ | def run(self): | ||
+ | for lang in os.listdir('build/locale/'): | ||
+ | lang_dir = os.path.join('share', 'locale', lang, 'LC_MESSAGES') | ||
+ | lang_file = os.path.join('build', 'locale', lang, 'LC_MESSAGES', 'mussorgsky.mo') | ||
+ | self.data_files.append( (lang_dir, [lang_file]) ) | ||
+ | _install_data.run(self) | ||
+ | </source> | ||
+ | Finally, add the new commands in distutils... | ||
+ | <source lang="python"> | ||
+ | cmdclass = { | ||
+ | 'build': build, | ||
+ | 'build_trans': build_trans, | ||
+ | 'install_data': install_data, | ||
+ | } | ||
+ | </source> | ||
+ | And don't forget to add this in your setup function, in the <code>setup.py</code> | ||
+ | <source lang="python"> | ||
+ | setup(name = 'your project', | ||
+ | ... | ||
+ | license = 'GPL v2 or later', | ||
+ | data_files = DATA, | ||
+ | scripts = SCRIPTS, | ||
+ | cmdclass = cmdclass <----- DON'T FORGET THIS | ||
+ | ) | ||
+ | </source> | ||
+ | Ok, now everything should be ready. If you run | ||
+ | |||
+ | python2.5 setup.py build | ||
+ | |||
+ | the terminal will print things like | ||
+ | |||
+ | compiling es.po | ||
+ | compiling de.po | ||
+ | ... | ||
+ | |||
+ | Usually <code>dpkg-buildpackage</code> uses <code>python2.5 setup.py build</code> and <code>install</code>, so everything should work out-of-the-box with your previous package configuration. | ||
+ | |||
+ | Feel free to complete/correct this wiki page with your own experience. | ||
+ | |||
+ | [[Category:Internationalization]] | ||
+ | [[Category:Development]] | ||
+ | [[Category:HowTo]] | ||
+ | [[Category:Python]] |
Learn more about Contributing to the wiki.