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>
-
== How to add translations ==
+
== Generate template for the translators ==
-
Now you want to get a translation in an specific language. You need to generate a <code>.po</code> for it.
+
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.
-
There are several ways to do it. I suggest to do it the easy way with poEdit:
+
xgettext --language=Python --keyword=_ --output=po/PROJECTNAME.pot `find . -name "*.py"`
-
=== With poEdit ===
+
-
Poedit is cross-platform gettext catalogs (.po files) editor. For more information, see its website http://www.poedit.net/.
+
-
Just start it and generate a new catalog from the generated .pot file.
+
-
In the option window you can select the translated language, your name, email address and so on. This is useful, so others know who created this translation.  
+
-
Now you can start to translate all the text. If you modify your application and generate a new .pot file, you can easily import it. It will use all the existing translation and add the new/modified strings. If you changed the charset before like me, you will have to select it again.
+
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.
-
Every time you save the file, poEdit will generate a .mo file. You can use it for testing the new translation.
+
 
 +
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.
-
=== Manual way ===
 
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 www.roseindia.net/tutorials/I18N/locales-list.shtml] or [http://people.w3.org/rishida/utils/subtags/ people.w3.org/rishida/utils/subtags].
+
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.


Please note that all contributions to maemo.org wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see maemo.org wiki:Copyrights for details). Do not submit copyrighted work without permission!


Cancel | Editing help (opens in new window)