Developing a multilingual application is rather simple with Symbian OS and this tutorial will show you to achieve this. Of course, it is always tempting to hardcode your strings. But with only a little effort you can have your application working in English and your native language - sorry for the Brits which do not need this ;-). A couple of web friends will probably help you adding extra translations in a second step.
Introduction
In our example, let's consider we will write an application that supports three languages:
English
French
German
The application is amazingly called MyApp. It might be running on any GUI environment, a few figures here and there are specific to UIQ but are not directly related to our discussion.
This tutorial mostly introduce the changes you have to make to this application to turn it into a true multilingual one. There are 5 steps:
identify all your strings and put them into specific language file
update your resources
update your source code so that it handle variable strings instead of hardcoded ones
update your MMP file to tell the resource compiler which language are supported
localize the name of the application
package the whole.
Step 1: Put your strings in a localisation file
The first step is to create three localised files, named MyApp.l01, MyApp.l02 and MyApp.l03, which will respectively describe my english, french and german strings. The 01, 02 and 03 figures are Symbian OS convention to identify a language. All the possible values are defined by the TLanguage enum found in e32std.h:
| Language | TLanguage Enum | Code | | Language | TLanguage Enum | Code |
| UK English | ELangEnglish | 01 | | Catalan | ELangCatalan | 44 |
| French | ELangFrench | 02 | | Croation | ELangCroatian | 45 |
| German | ELangGerman | 03 | | Canadian English | ELangCanadianEnglish | 46 |
| Spanish | ELangSpanish | 04 | | International English | ELangInternationalEnglish | 47 |
| Italian | ELangItalian | 05 | | South African English | ELangSouthAfricanEnglish | 48 |
| Swedish | ELangSwedish | 06 | | Estonian | ELangEstonian | 49 |
| Danish | ELangDanish | 07 | | Farsi | ELangFarsi | 50 |
| Norwegian | ELangNorwegian | 08 | | Canadian French | ELangCanadianFrench | 51 |
| Finnish | ELangFinnish | 09 | | Gaelic | ELangScotsGaelic | 52 |
| American | ELangAmerican | 10 | | Georgian | ELangGeorgian | 53 |
| Swiss French | ELangSwissFrench | 11 | | Greek | ELangGreek | 54 |
| Swiss German | ELangSwissGerman | 12 | | Cyprus Greek | ELangCyprusGreek | 55 |
| Portuguese | ELangPortuguese | 13 | | Gujarati | ELangGujarati | 56 |
| Turkish | ELangTurkish | 14 | | Hebrew | ELangHebrew | 57 |
| Icelandic | ELangIcelandic | 15 | | Hindi | ELangHindi | 58 |
| Russian | ELangRussian | 16 | | Indonesian | ELangIndonesian | 59 |
| Hungarian | ELangHungarian | 17 | | Irish | ELangIrish | 60 |
| Dutch | ELangDutch | 18 | | Swiss Italian | ELangSwissItalian | 61 |
| Belgian Flemish | ELangBelgianFlemish | 19 | | Kannada | ELangKannada | 62 |
| Australian English | ELangAustralian | 20 | | Kazakh | ELangKazakh | 63 |
| Belgian French | ELangBelgianFrench | 21 | | Kmer | ELangKhmer | 64 |
| Austrian German | ELangAustrian | 22 | | Korean | ELangKorean | 65 |
| New Zealand English | ELangNewZealand | 23 | | Lao | ELangLao | 66 |
| International French | ELangInternationalFrench | 24 | | Latvian | ELangLatvian | 67 |
| Czech | ELangCzech | 25 | | Lithuanian | ELangLithuanian | 68 |
| Slovak | ELangSlovak | 26 | | Macedonian | ELangMacedonian | 69 |
| Polish | ELangPolish | 27 | | Malay | ELangMalay | 70 |
| Slovenian | ELangSlovenian | 28 | | Malayalam | ELangMalayalam | 71 |
| Taiwanese Chinese | ELangTaiwanChinese | 29 | | Marathi | ELangMarathi | 72 |
| Hong Kong Chinese | ELangHongKongChinese | 30 | | Moldovian | ELangMoldavian | 73 |
| PRC Chinese | ELangPrcChinese | 31 | | Mongolian | ELangMongolian | 74 |
| Japanese | ELangJapanese | 32 | | Norwegian Nynorsk | ELangNorwegianNynorsk | 75 |
| Thai | ELangThai | 33 | | Brazilian Portuguese | ELangBrazilianPortuguese | 76 |
| Afrikaans | ELangAfrikaans | 34 | | Punjabi | ELangPunjabi | 77 |
| Albanian | ELangAlbanian | 35 | | Romanian | ELangRomanian | 78 |
| Amharic | ELangAmharic | 36 | | Serbian | ELangSerbian | 79 |
| Arabic | ELangArabic | 37 | | Sinhalese | ELangSinhalese | 80 |
| Armenian | ELangArmenian | 38 | | Somali | ELangSomali | 81 |
| Tagalog | ELangTagalog | 39 | | International Spanish | ELangInternationalSpanish | 82 |
| Belarussian | ELangBelarussian | 40 | | American Spanish | ELangLatinAmericanSpanish | 83 |
| Bengali | ELangBengali | 41 | | Swahili | ELangSwahili | 84 |
| Bulgarian | ELangBulgarian | 42 | | Finland Swedish | ELangFinlandSwedish | 85 |
| Burmese | ELangBurmese | 43 | | Tamil | ELangTamil | 87 |
| Telugu | ELangTelugu | 88 | | | | |
| Tibetan | ELangTibetan | 89 | | | | |
| Tigrinya | ELangTigrinya | 90 | | | | |
| Cyprus Turkish | ELangCyprusTurkish | 91 | | | | |
| Turkmen | ELangTurkmen | 92 | | | | |
| Ukrainian | ELangUkrainian | 93 | | | | |
| Urdu | ELangUrdu | 94 | | | | |
| Vietnamese | ELangVietnamese | 96 | | | | |
| Welsh | ELangWelsh | 97 | | | | |
// MyApp.l01
// English localisation
#define ELanguage ELangEnglish
// Localised strings
#define STRING_HOWAREYOU "How are you ?"
#define STRING_APPLES "You have %d apples."
#define STRING_EXIT "Exit"
// MyApp.l02
// French localisation
#define ELanguage ELangFrench
// Localised strings
#define STRING_HOWAREYOU "Comment allez vous ?"
#define STRING_APPLES "Vous avez %d pommes."
#define STRING_EXIT "Sortir"
// MyApp.l3
// German localisation
#define ELanguage ELangGerman
// Localised strings
#define STRING_HOWAREYOU "Wie gehen Sie ?"
#define STRING_APPLES "Sie haben %d Apfels."
#define STRING_EXIT "Schliessen"
Then create a localisation file that will gather the three files above:
// MyApp.loc
// Localisation for MyApp
#ifdef LANGUAGE_01
#include "MyApp.l01"
#endif
#ifdef LANGUAGE_02
#include "MyApp.l02"
#endif
#ifdef LANGUAGE_03
#include "MyApp.l03"
#endif
The purpose of this .loc file is only to ease the inclusion of the proper language file in your .rss, so it is a pure convention to use and define it.
As far as I am concerned, I generally put all these files with my RSS file, in a dedicated directory called rsc. This is definitely not a mandatory requirement. Just think that these files are included so their path should be accessible from the MMP file. But this is another story we will discuss in step 4.
Step 2: Use localised strings in your menus and dialogs
Now that we have our localised strings, let's integrate them in our resource files so they can be taken into account. First, we have to include the .loc file at the beginning of our resource definitions:
// MyApp.rss
NAME HELL
#include <eikon.rh>
#include "MyApp.hrh"
#include "MyApp.loc" // <<<< Here it is
RESOURCE RSS_SIGNATURE { }
RESOURCE TBUF { buf=""; }
...
Once this is done, we change the hardcoded strings in our objects by indirections to the localised ones. Ex:
//***************************************************************************
RESOURCE MENU_PANE r_myapp_menu
//***************************************************************************
{
items=
{
MENU_ITEM { command=EMyAppHowAreYou; txt=STRING_HOWAREYOU; },
MENU_ITEM { command=EEikCmdExit; txt=STRING_EXIT; }
};
}
instead of:
//***************************************************************************
RESOURCE MENU_PANE r_myapp_menu
//***************************************************************************
{
items=
{
MENU_ITEM { command=EMyAppHowAreYou; txt="How are you?"; },
MENU_ITEM { command=EEikCmdExit; txt="Exit"; }
};
}
That wasn't a big deal! This is enough if you only need to localise your menu. But if you intend to use localised strings in your code as well, then you also have to define a resource buffer to hold them. Simply add a TBUF resource declaration for each string you need to access from your code at the end of your .rss file:
/***************************************************************************
// STRINGS
//***************************************************************************
RESOURCE TBUF r_string_apple { buf=STRING_APPLE; }
RESOURCE TBUF r_string_thanks { buf=STRING_THANKS; }
...
Step 3: Use localised strings in your code
You should now be able to delete most of the _LIT() definitions in your code. Ex, no more:
myDes.Format(_L("You have %d apples"),num);
Use instead a format string read from the resource file. This is slightly more complex but CCoeEnv::AllocReadResourceLC() will do most of the job for you:
HBufC* formatBuf=CCoeEnv::Static()->AllocReadResourceLC(R_STRING_THANKS);
myDes.Format(formatBuf,num);
CleanupStack::PopAndDestroy(formatBuf);
This code allocates a HBufC descriptor and copy the R_STRING_APPLE strings into it. The descriptor is then used as a format template to include a numerical value. Depending on the language settings of your phone (and the num value!), the myDes descriptor contain "You have 3 apples.", "Vous avez 3 pommes." or "Sie haben 3 Apfels" (Note: I must apologize for this real dummy text but I only speak and write very limited German!).
Step 4: Update your MMP file
That's all as far the code is concerned. However, a small update is necessary in the MMP file to tell the resource compiler which language shall be used. Add a LANG statement with the code corresponding to each of the language you currently support:
TARGET MyApp.app
TARGETTYPE app
UID 0x100039CE 0x0EB00551
TARGETPATH \system\apps\Myapp
// Language in which the application is translated
// (each supported language shall provide a MyApp.lxx file where
// xx is the code number of the language:
// 01 = english, 02 = french, 03 = german, etc... )
LANG 01 02 03
SOURCEPATH ..\src
SOURCE MyAppMain.cpp
SOURCE MyAppDocument.cpp
SOURCE MyAppUi.cpp
SOURCE MyAppView.cpp
SOURCE MyAppModel.cpp
SOURCEPATH ..\rsc
RESOURCE MyApp.rss
USERINCLUDE ..\inc
USERINCLUDE ..\rsc
...
Remember to include your .rss file in the compilation (with the RESOURCE statement) and to include the path to your localisation files as well (the second USERINCLUDE primitive).
Step 5: Localize the name of your application
The name of the application is by default the name of the .app file which cannot be localised. However,by creating an .aif file, you can specify localised name for your application. First, create or update the definition file as specified:
//MyAppAif.rss
#include <aiftool.rh>
RESOURCE AIF_DATA
{
app_uid=0x0EB00551;
num_icons=2;
embeddability=KAppNotEmbeddable;
newfile=KAppDoesNotSupportNewFile;
caption_list=
{
CAPTION { code=ELangEnglish; caption="MyApp"; },
CAPTION { code=ELangFrench; caption="MonApp"; },
CAPTION { code=ELangGerman; caption="MeinApp";}
};
}
The caption_list associates one different name to each language configuration. Now add the corresponding statement at the end of your .mmp if it is not already present:
AIF MyApp.aif . ..\aif\MyAppaif.rss
c12 ..\aif\newlc42x35.bmp ..\aif\newlc42x35m.bmp
..\aif\newlc42x29.bmp ..\aif\newlc42x29m.bmp
This also customizes the logo that appears on the Launcher Menu to represent your application.
Step 6: Update your PKG file
Still reading ? Good, and you are now close to the end. But now is the time to ask you a question reagarding the packaging and the installation of the languages files. Do you want to:
install all the languages silently on the phone (this may waste some space for complex/big applications but do not bother the user with a language popup)
ask the user which language he wants to install ? (this uses less space on the disk but adds a step in the installation process)
Let's take the first option first. As we don't want to bother the user with the language popup dialog, then we will install a resource file corresponding to each language we support and we rely on the operating system for the language selection. By default, these resource files have the extension .rXX where XX is the language code and they are located in the
\epoc32\data\z\system\apps\MyApp\
directory. We have to list all them in our .pkg and install them in our application directory:
;
; Installation file for MyApp example application
; "Silent" version: no language popup
;
; UID is the app's UID
;
#{"MyApp"},(0x0EB00551),1,0,0
; Target is UIQ 2.0 devices.
(0x101F617B), 2, 0, 0, {"UIQ20ProductID"}
;
; Main App
;
"\epoc32\release\armi\urel\MyApp.app" - "!:\system\apps\MyApp\MyApp.app"
"\epoc32\data\z\system\apps\MyApp\MyApp.r01"
- "!:\system\apps\MyApp\MyApp.r01"
"\epoc32\data\z\system\apps\MyApp\MyApp.r02"
- "!:\system\apps\MyApp\MyApp.r02"
"\epoc32\data\z\system\apps\MyApp\MyApp.r03"
- "!:\system\apps\MyApp\MyApp.r03"
"\epoc32\data\z\system\apps\MyApp\MyApp.aif" -"!:\system\apps\MyApp\MyApp.aif"
You may notice that, despite the fact we are writing a multilingual application, we do not specify any language in this .pkg. This is why the user won't be asked. And the magic there is that your application will appear in English for a English user, in French for a French user and in German for a German user. User of other countries will see what Symbian OS think is the most appropriate language according to their locale settings (which is generally English). And if you switch your phone language, the application will automatically switc has well! One of the issue with this method is that ou cannot completely localize the name of the application as it will be displayed by the installer.
The other alternative is to display a language popup at install time, and only install the requested language. No change is required in your code, just tweak a few lines in your .pkg:
;
; Installation file for MyApp example application
; Standard version: language popup
;
;Languages
&EN,FR,GE
; UID is the app's UID
;
#{"MyApp","MonApp","MeinApp"},(0x0EB00551),1,0,0
; Target is UIQ 2.0 devices.
(0x101F617B), 2, 0, 0, {"UIQ20ProductID", "UIQ20ProductID", "UIQ20ProductID"}
;
; Main App
;
"\epoc32\release\armi\urel\MyApp.app" - "!:\system\apps\MyApp\MyApp.app"
{
"\epoc32\data\z\system\apps\MyApp\MyApp.r01"
"\epoc32\data\z\system\apps\MyApp\MyApp.r02"
"\epoc32\data\z\system\apps\MyApp\MyApp.r03"
}
- "!:\system\apps\MyApp\MyApp.rsc"
"\epoc32\data\z\system\apps\MyApp\MyApp.aif" -"!:\system\apps\MyApp\MyApp.aif"
This .pkg generates a .sis file that will behave slightly differently at installation:
the application has the name that is localised even in the installer (here "MyApp", "MonApp" or "MeinApp".
a popup will be shown and the user will be asked to select a language
only one of the three .rXX file will be installed on the phone and it will be renamed as .rsc.
after installation, the application will always appear in this language, no matter of the phone language settings.
Related Links
If you are interested in localizong your own application, the following links may be of interest for you:
The Nokia language example in the directory Series60Ex\Language of your Series 60 SDK
Localization by Ariel of Giant Steps, Ltd
How to make a multilingual application installerby SavaaZ
The Localisation example on Sendo Developer site (requires a registration).
Hi,
I am trying to follow this article however, the resource file does not compile. I cannot even see any error, just that when I try to make the SIS package, it cannot find the .rsc file.
As soon as I remove localization code, the app works fine.
If anyone can help it would be great.
Thanks!
Hi Eric, Very good article. But All the articles related to this subjects, ignores, HLP file creation for multiple languages. Can you please throw some light on this issue as how we can localize HLP files and add to the installer.
thanks
regards
rapsage
Hi Guys,
There is a simpler way to localize your software. It is called "StringDB". It is a binary resource file for strings that is generated directly from string tables in MS Excel sheets. It is simple to use and highly flexible for different platforms.
By using StringDB concept you don't need any other resource files for strings. No need to manipulate strings manually. And no need to recompile to change language package of the software.
You can find the trial software and documentation on the web site www.swbox.com.
Mike
StringDB is for .NET devel. Not Symbian.
just for the sake of a correct example: in german "How are you?" is "Wie geht es ihnen?" and "apples" is not Apfels, but "Äpfel"....
but i like the rest of the tutorial :-)
hey this StringDB is a better approach i think,
i just used 3 functions, and multi-language system is done! using MS Excel is also a plus fo rme.
pew, now i see that those long struggles for multi-language applications was a waste of time.
Hello,
Can you tell me in detail what is PRC code. What does PRC stand for? and How to creat it? Has it related to Unicode for the font?
Thanks your help. Best regards,
Sochan.
HI,
PRC stands for People Republic of China
i m getting error while creating the sis file, Error is :
Processing Localization.pkg... Localization.pkg(23) : error: Expected quoted string read , Localization.pkg(23) : error: unexpected text
My pkg file is :
; ; Installation file for $$Root$$ application ; ;Languages &EN, ZH ; ; UID is the app's UID ; #"Localization", "Localization",(0x04F141C6),1,0,0 ; ;Supports Series 60 v 2.0 ;This line indicates that this installation is for the Series 60 platform v2.0 ;This line must appear _exactly_ as shown below in the sis file ;If this line is missing or incorrect, the sis file will not be able ;to be installed on Series 60 v2.0 platforms (0x101F7960), 0, 0, 0, "Series60ProductID", "Series60ProductID" ; ; Four files to install ; "C:\Symbian\7.0s\Series60_v21\Epoc32\release\armi\urel\Localization.app" -"!:\system\apps\Localization\Localization.app" "C:\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\Localization\Localization.rsc" , //THIS IS CAUSING ERROR "C:\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\Localization\Localization.r31" -"!:\system\apps\Localization\Localization.rsc" "C:\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\Localization\Localization_caption.rsc" -"!:\system\apps\Localization\Localization_caption.rsc" "C:\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\Localization\Localization.aif" -"!:\system\apps\Localization\Localization.aif"
plz let me know wht i m doing wrong.
I think it is worth mentioning here that for languages like Chinese and others with non-ASCII characters, the localisation files should be saved in UTF-8.
Windows adds a few characters at beginning of file to identify it as UTF-8 file and those characters should be removed as rcomp doesn't like it. It is better to edit the file in carbide/codewarrior and not using notepad/wordpad of windows. Moreover, the .loc file should have this statement for rcomp to treat the file as UTF-8.
CHARACTER_SET UTF8I hope it saves time for others looking to localize applications.