Recipes about Internationalization

This section of the wxPython Cookbook contains recipes about Internationalization and Localization of wxPython apps.

If you can't find what you're looking for then please consider figuring it out and then writing a recipe for the Cookbook so others won't have to go through the same pain you did.

Overview of Internationalization

See the overview of source code internationalization (inside Internationalization) using the gettext system supported by Python and wxPython.

You can use the mki18n.py script (attached to the Internationalization page) to create the Portable Object (.po) and Machine Object (.mo) files easily for all Python source code (or C, C++,....) files inside a directory. The mki18n Python script uses the GNU gettext tools.

Recipes

A Simple walk-through

Once I read the Internationalization overview and got everything installed as instructed, I felt the need to play with it. Here's what I did and what I wrote.

1. The first thing I did was create a folder called "i18ntest". It seems easiest if the folder name and the project name match. I copied the mki18n.py file into that folder. (Note: I had to edit the file slightly to get it to run, removing a "unixpath" call that didn't work on my machine.)

2. I then wrote the following code, in a file called "i18ntest.py".

   1 # This example is provided with no promises or warranties whatsoever
   2 # import wxPython
   3 from wxPython import wx
   4 # import Python gettext
   5 import gettext
   6 # Declare Menu Constants
   7 MENU_FILE_EXIT        = 101
   8 MENU_LANGUAGE_ENGLISH = 201
   9 MENU_LANGUAGE_SPANISH = 202
  10 MENU_LANGUAGE_FRENCH  = 203
  11 class I18ntest(wx.wxFrame):
  12    """ This class demonstrates Internationalization. """
  13    def __init__(self):
  14       """ Create I18ntest form. """
  15       # Install gettext.  Once this is done, all strings enclosed in "_()" will automatically be translated.
  16       gettext.install('i18ntest', './locale', unicode=False)
  17       # Define supported languages
  18       self.presLan_en = gettext.translation('i18ntest', './locale', languages=['en']) # English
  19       self.presLan_es = gettext.translation('i18ntest', './locale', languages=['es']) # Spanish
  20       self.presLan_fr = gettext.translation('i18ntest', './locale', languages=['fr']) # French
  21       # Install English as the initial language
  22       self.presLan_en.install()
  23       # Define the main Frame for the Application.  Note that the Application Title in enclosed in _()
  24       wx.wxFrame.__init__(self, None, -4, _('MiniApp'), size = (500,250), style=wx.wxDEFAULT_FRAME_STYLE|wx.wxNO_FULL_REPAINT_ON_RESIZE)
  25       # Set the background color to White
  26       self.SetBackgroundColour(wx.wxWHITE)
  27       # Menu Bar
  28       # File Menu
  29       # Create a MenuBar
  30       self.menuBar = wx.wxMenuBar()
  31       # Build a Menu Object to go into the Menu Bar
  32       self.menu1 = wx.wxMenu()
  33       # Define the Exit Menu.  Note that the menu text and hint are enclosed in _()
  34       self.menu1.Append(MENU_FILE_EXIT, _("E&xit"), _("Quit Application"))
  35       #Place the Menu Item in the Menu Bar.  Note that the menu text is enclosed in _()
  36       self.menuBar.Append(self.menu1, _("&File"))
  37       # Language Menu
  38       # Build a Menu Object to go into the Menu Bar
  39       self.menu2 = wx.wxMenu()
  40       # Define the Menu Options.  Note that menu names are enclosed in _().  I chose to leave the Help Text untranslated, as I don't want it to change.
  41       self.menu2.Append(MENU_LANGUAGE_ENGLISH, _("&English"), "Do You Speak English? (Spoken slowly and loudly)")
  42       self.menu2.Append(MENU_LANGUAGE_SPANISH, _("&Spanish"), "Se habla Espanol?")
  43       self.menu2.Append(MENU_LANGUAGE_FRENCH,  _("&French"),  "Parlez vous Francais?")
  44       #Place the Menu Item in the Menu Bar.  Note the menu text is enclosed in _()
  45       self.menuBar.Append(self.menu2, _("&Language"))
  46       # Place the Menu on the Application Frame.
  47       self.SetMenuBar(self.menuBar)
  48       #Define Events for the Menu Items
  49       wx.EVT_MENU(self, MENU_FILE_EXIT, self.CloseWindow)
  50       wx.EVT_MENU_RANGE(self, MENU_LANGUAGE_ENGLISH, MENU_LANGUAGE_FRENCH, self.Language)
  51       # Create two labels on the Frame.  (I won't mess with Sizers or Layout Constraint here.)
  52       # The first label will display the selected Language.  It does NOT need translatable text.
  53       self.txt = wx.wxStaticText(self, -1, 'English', pos=(10, 20))
  54       # The second label will contain translatable text, but I don't need to put any text in it yet.
  55       self.txt2 = wx.wxStaticText(self, -1, '', pos=(10, 40))
  56       # Create a Status Bar
  57       self.CreateStatusBar()
  58       # Show the Frame
  59       self.Show(True)
  60       # Populate all text in the installed language
  61       self.UpdateText()
  62    # In order to be able to update all of the text on the Application Frame, I have written a method that
  63    # updates all of the text.
  64    def UpdateText(self):
  65       """ Update all Text on the Application Frame. """
  66       # Set the Frame Title
  67       self.SetTitle(_('MiniApp'))
  68       # Define the String for the second wxStaticText label and the Status Bar
  69       str = _("Hello.  This is translatable.")
  70       # Set the wxStaticText label
  71       self.txt2.SetLabel(str)
  72       # Update the text for the Main Menubar Items
  73       self.menuBar.SetLabelTop(0, _("&File"))
  74       self.menuBar.SetLabelTop(1, _("&Language"))
  75       # Update the text and Help Strings for the File Menu Items
  76       self.menu1.SetLabel(MENU_FILE_EXIT, _('E&xit'))
  77       self.menu1.SetHelpString(MENU_FILE_EXIT, _("Quit Application"))
  78       # Update the text for the Language Menu Items.  (I do not want to update the Help Strings.)
  79       self.menu2.SetLabel(MENU_LANGUAGE_ENGLISH, _('&English'))
  80       self.menu2.SetLabel(MENU_LANGUAGE_SPANISH, _('&Spanish'))
  81       self.menu2.SetLabel(MENU_LANGUAGE_FRENCH,  _('&French'))
  82       # Update the Status Bar Text
  83       self.SetStatusText(str)
  84    def CloseWindow(self, event):
  85       """ Method that Exits the program. """
  86       self.Close()
  87    def Language(self, event):
  88       """ Method to change application Language based on a Menu Selection """
  89       if event.GetId() == MENU_LANGUAGE_ENGLISH:
  90          # Set Language to English
  91          self.presLan_en.install()
  92          # Set wxStaticText Label to indicate that English has been selected
  93          self.txt.SetLabel("English")
  94       elif event.GetId() == MENU_LANGUAGE_SPANISH:
  95          # Set Language to Spanish
  96          self.presLan_es.install()
  97          # Set wxStaticText Label to indicate that Spanish has been selected
  98          self.txt.SetLabel("Espanol")
  99       elif event.GetId() == MENU_LANGUAGE_FRENCH:
 100          # Set Language to French
 101          self.presLan_fr.install()
 102          # Set wxStaticText Label to indicate that French has been selected
 103          self.txt.SetLabel("Francais")
 104       # Update all Application text to reflect Language change
 105       self.UpdateText()
 106 class MyApp(wx.wxApp):
 107    """ Define Application """
 108    def OnInit(self):
 109       """ Initialize the Application """
 110       # Define the Frame using the I18ntest class
 111       frame = I18ntest()
 112       # Set the Frame as the Top Window
 113       self.SetTopWindow(frame)
 114       return True
 115 # Define the Application and run it
 116 app = MyApp(0)
 117 app.MainLoop()

3. Next, I created a file called "app.fil" which contained only one line.

i18ntest.py

4. I then issued the following command at the DOS prompt:

python mki18n.py –-p

5. This created a file called "messages.pot", which is the template for the ".po" (portable object) files used for translation.

<header information removed for wiki presentation>
#: i18ntest.py:49 i18ntest.py:91
msgid "&English"
msgstr ""
#: i18ntest.py:43 i18ntest.py:85
msgid "&File"
msgstr ""
#: i18ntest.py:51 i18ntest.py:93
msgid "&French"
msgstr ""
#: i18ntest.py:53 i18ntest.py:86
msgid "&Language"
msgstr ""
#: i18ntest.py:50 i18ntest.py:92
msgid "&Spanish"
msgstr ""
#: i18ntest.py:41 i18ntest.py:88
msgid "E&xit"
msgstr ""
#: i18ntest.py:81
msgid "Hello.  This is translatable."
msgstr ""
#: i18ntest.py:30 i18ntest.py:79
msgid "MiniApp"
msgstr ""
#: i18ntest.py:41 i18ntest.py:89
msgid "Quit Application"
msgstr ""

6. I copied messages.pot to create files called "i18ntest_en_US.po", "i18ntest_es_ES.po", and "i18ntest_fr_FR.po". I then edited these files as best I could to provide the necessary translations. (I don't know either Spanish or French, so can't vouch for the translations presented here. If something translates to "My hovercraft is filled with eels," don't blame me. If you know Spanish or French, feel free to correct the translations!) (Check this list for ValidI18nCodes.)

i18ntest_es_ES.po:

<header information removed for wiki presentation>
#: i18ntest.py:49 i18ntest.py:91
msgid "&English"
msgstr "&Inglés"
#: i18ntest.py:43 i18ntest.py:85
msgid "&File"
msgstr "&Archivo"
#: i18ntest.py:51 i18ntest.py:93
msgid "&French"
msgstr "&Francés"
#: i18ntest.py:53 i18ntest.py:86
msgid "&Language"
msgstr "&Lenguage"
#: i18ntest.py:50 i18ntest.py:92
msgid "&Spanish"
msgstr "&Español"
#: i18ntest.py:41 i18ntest.py:88
msgid "E&xit"
msgstr "&Salir"
#: i18ntest.py:81
msgid "Hello.  This is translatable."
msgstr "Hola.  Esto es traducible."
#: i18ntest.py:30 i18ntest.py:79
msgid "MiniApp"
msgstr "Applicación Pequeña"
#: i18ntest.py:41 i18ntest.py:89
msgid "Quit Application"
msgstr "Salir de la Applicación"

i18ntest_fr_FR.po

<header information removed for wiki presentation>
#: i18ntest.py:49 i18ntest.py:91
msgid "&English"
msgstr "&Anglais"
#: i18ntest.py:43 i18ntest.py:85
msgid "&File"
msgstr "&Fichier"
#: i18ntest.py:51 i18ntest.py:93
msgid "&French"
msgstr "&Français"
#: i18ntest.py:53 i18ntest.py:86
msgid "&Language"
msgstr "&Langue"
#: i18ntest.py:50 i18ntest.py:92
msgid "&Spanish"
msgstr "&Espagnol"
#: i18ntest.py:41 i18ntest.py:88
msgid "E&xit"
msgstr "&Quitter"
#: i18ntest.py:81
msgid "Hello.  This is translatable."
msgstr "Bonjour. Ceci est traduisible."
#: i18ntest.py:30 i18ntest.py:79
msgid "MiniApp"
msgstr "Petite application"
#: i18ntest.py:41 i18ntest.py:89
msgid "Quit Application"
msgstr "Quitter l'application"

7. I then typed the following at the DOS prompt:

python mki18n.py –-m –-e

This created a "locale" folder with folders for the English, Spanish, and French translation ".mo" files properly distributed. (The "-e" parameter ensures that an English "translation" is created.)

8. Finally, I typed:

python i18ntest.py

to run my sample program. It actually worked!

I hope you find this helpful.

- David Woods

Comments

Pierre Rouleau, Nov 11,2003:

- I updated some of the French text. Really only minor touches to the French text inside the example written by David. Note that French and Spanish and other languages use accentuated letters. Inside real applications, you would place the accentated characters inside the .po files. However, I don't currently know how to put accents inside a wiki text page. If someone knows how to do this, feel free to update the translations.

- The mki18n.py file I posted originally was missing the definition of the function unixpath().

Dave Cridland, 05/03/2004 (That's March. l11n is down the corridor...)

- I ran through the Spanish and French to get some translations (which were probably correct anyway), and figure out where the accents go.

PPW, 26-Jul-2006:

- Apparently the mki18n.py file was updated by E. A. Tacao 2006-04-21 since this tutorial was written:

So I initially got stuck at Step 7., where "mki18n.py -m -e" would not output anything at all. Files called "i18ntest_en.po", "i18ntest_es.po", and "i18ntest_fr.po" will not work any more as country code settings are expected. Use files called "i18ntest_en_US.po", "i18ntest_es_ES.po", and "i18ntest_fr_FR.po" instead. A whole list of current locale settings supported by wxPython 2.6.3-unicode can be found at ValidI18nCodes. (I amended the tutorial in step 6. accordingly.)

Accents in .po

perico, 15-09-06

Let's go to see an example with spanish accents. If you want to write accents in your .po files, you have to do two things:

1.- Spanish codification is ISO-8859-1. So when the .po file is generated, you only have to change one of the lines that are at the begining of the file. Have a look at this line: "Content-Type: text/plain; charset=iso-8859-1\n".

2.- From this moment you can write spanish accents in the file, just as you would do in your favorite text processor, and they will appear after translation.

If you are not spanish, let's say you are norwegian, so you have to look for you ISO language. I think it is really easy, the problem has been to find it out. Don't you think?

Accents in .po mk2

Much better to use Unicode (UTF-8) for all po files. No need to hunt for specific ISO code, ability to mix different codes together at will, better compability.

"Content-Type: text/plain; charset=utf-8\n"

- ianaré 2007-08-12

RecipesI18n (last edited 2009-10-04 15:05:12 by lns-bzn-51f-81-56-130-3)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.