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
Internationalization Overview
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
- Wisconsin Center for Education Research, University of Wisconsin, Madison
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().
It has been updated. See the Internationalization page for the attachment.
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:
- I tried to use the Pierre Rouleau's mki18n.py script at
http://wiki.wxpython.org/index.cgi/Internationalization and I ran into a couple of issues: (1) it supports only the two letter ISO language code (for example, "pt"), and not a language code followed by the country code ("pt_BR") and (2) things become weird if the app's directory contains spaces. I fixed both issues here (Python 2.3.5, wxPython 2.6.3.0 ansi, msw XP (pt_BR)), and replaced the 'iso639_languageDict' by some code that finds out what are the currently wx supported canonical forms of current locale names. It seems to be working.
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