Developing International Software When a localized software fails
The basic application WORDPAD The basic application WORDPAD New features for WORDPAD New features for WORDPAD Implementing the new features Implementing the new features Testing WORDPAD Testing WORDPAD Localizing WORDPAD Localizing WORDPAD Reviewing the i18n programming errors Reviewing the i18n programming errors Localizing fixed WORDPAD Localizing fixed WORDPAD Testing fixed WORDPAD Testing fixed WORDPAD More i18n issues More i18n issues
The Basic Application WORDPAD A sample application for developers provided by Microsoft – including source code A sample application for developers provided by Microsoft – including source code WORDPAD is a small text-editor WORDPAD is a small text-editor Developed using C++ Developed using C++ Uses Windows Resource Scripts to store localizable data Uses Windows Resource Scripts to store localizable data Start WORDPAD
New Features for WORDPAD Map selected text to upper-case Map selected text to upper-case Map selected text to lower-case Map selected text to lower-case Reverse selected text (our job!) Reverse selected text (our job!) Display statistics about selected text, like alphabetical characters, numerical characters, white spaces,… Display statistics about selected text, like alphabetical characters, numerical characters, white spaces,… Display a dialog with a preview of to-upper, to- lower and reverse Display a dialog with a preview of to-upper, to- lower and reverse
Implementing the New Features Start the Developer Studio Start the Developer Studio Open the WORDPAD project in directory "Wordpad Start" Open the WORDPAD project in directory "Wordpad Start" Compile and run the application Compile and run the application Implement "OnLocalizationtestReverse" in module "mainfrm.cpp" Implement "OnLocalizationtestReverse" in module "mainfrm.cpp" Start DevStudio
Testing WORDPAD (The Programmers View) Testing WORDPAD with "test.rtf" Testing WORDPAD with "test.rtf" in directory "Wordpad Start" or "Wordpad implemented" in directory "Wordpad Start" or "Wordpad implemented" Your test results and my test results Your test results and my test results Fixing the programming error Fixing the programming error Start DevStudio Start WORDPAD
Localizing WORDPAD Testing WORDPAD (Localizers View) Testing WORDPAD (Localizers View) Open PASSOLO with project "Wordpad" in directory "Wordpad Start" or "Wordpad implemented" Open PASSOLO with project "Wordpad" in directory "Wordpad Start" or "Wordpad implemented" Use Pseudo-Translation Use Pseudo-Translation Your test results and my test results ) Your test results and my test results ) Localizing WORDPAD Localizing WORDPAD Open PASSOLO with project "Wordpad" in directory "Wordpad Start" or "Wordpad implemented" Open PASSOLO with project "Wordpad" in directory "Wordpad Start" or "Wordpad implemented" Dialog "Preview of Text Modification Functions" Dialog "Preview of Text Modification Functions" StringTable ID's 193 to 199 StringTable ID's 193 to 199 Start PASSOLO
Reviewing the i18n programming errors Open the WORDPAD project in directory "Wordpad Start" or "Wordpad implemented" Open the WORDPAD project in directory "Wordpad Start" or "Wordpad implemented" Fix error in "OnLocalizationtestReverse“ Fix error in "OnLocalizationtestReverse“ Move error message to StringTable Move error message to StringTable Load error message from StringTable Load error message from StringTable Reviewing other code in module "mainfrm.cpp" Reviewing other code in module "mainfrm.cpp" Reviewing module "TextPreview.cpp" Reviewing module "TextPreview.cpp" Start DevStudio
void CMainFrame::OnLocalizationtestReverse() { CRichEditView *pView = (CRichEditView *) GetActiveView(); if (pView->GetRichEditCtrl().GetSelectionType() & (SEL_OBJECT | SEL_MULTIOBJECT )) { // PASSOLO: Hardcoded, cannot be localized! AfxMessageBox(L"Cannot reverse text because selection contains embedded objects!", MB_OK); return; } CString str = pView->GetRichEditCtrl().GetSelText(); str.MakeReverse(); pView->GetRichEditCtrl().ReplaceSel((LPCTSTR) str); } void CMainFrame::OnLocalizationtestReverse() { CRichEditView *pView = (CRichEditView *) GetActiveView(); if (pView->GetRichEditCtrl().GetSelectionType() & (SEL_OBJECT | SEL_MULTIOBJECT )) { // PASSOLO: Moved message to string table AfxMessageBox(IDS_NOTREVERSE, MB_OK); return; } CString str = pView->GetRichEditCtrl().GetSelText(); str.MakeReverse(); pView->GetRichEditCtrl().ReplaceSel((LPCTSTR) str); } Resources: STRINGTABLE DISCARDABLE BEGIN IDS_NOTREVERSE "Cannot reverse text because selection contains one or more embedded objects!" IDS_LESS50 "Selection contains less than 50 characters" IDS_MORE50 "Selection contains 50 or more characters" IDS_NOQUOTES "Cannot add quotes to text because selection contains one or more embedded objects!" END
void CMainFrame::OnLocalizationtestTolower() { CRichEditView *pView = (CRichEditView *) GetActiveView(); if (pView->GetRichEditCtrl().GetSelectionType() & (SEL_OBJECT | SEL_MULTIOBJECT )) { // A little artifical sequence but things like this happen! CString str; TCHAR buffer[100]; str.LoadString(IDS_NOTOLOWER); // If message is longer than 100 characters stack will be overwritten _tcscpy(buffer, (LPCTSTR) str); AfxMessageBox(buffer, MB_OK | MB_ICONINFORMATION); return; } CString str = pView->GetRichEditCtrl().GetSelText(); str.MakeLower(); pView->GetRichEditCtrl().ReplaceSel((LPCTSTR) str); } void CMainFrame::OnLocalizationtestTolower() { CRichEditView *pView = (CRichEditView *) GetActiveView(); if (pView->GetRichEditCtrl().GetSelectionType() & (SEL_OBJECT | SEL_MULTIOBJECT )) { // PASSOLO: More spaces for translated message! CString str; TCHAR buffer[200]; str.LoadString(IDS_NOTOLOWER); // PASSOLO: Limit the max number of chars copied _tcsncpy(buffer, (LPCTSTR) str, DIMOF(buffer)); buffer[DIMOF(buffer) - 1] = 0; AfxMessageBox(buffer, MB_OK | MB_ICONINFORMATION); return; } CString str = pView->GetRichEditCtrl().GetSelText(); str.MakeLower(); pView->GetRichEditCtrl().ReplaceSel((LPCTSTR) str); }
void CTextPreview::OnRadio3() { USES_CONVERSION; if (CurrentOption == IDC_RADIO3) return; this->CheckRadioButton(IDC_RADIO1, IDC_RADIO3, IDC_RADIO3); CurrentOption = IDC_RADIO3; // This is a Unicode application, so using ANSI API is artificial but this // is the kind of function being used in a lot of application and causing // a lot of troubles. // Also static buffer size can be problem! char buffer[100]; LoadStringA(theApp.m_hInstance, IDS_SAMLPETEXT, buffer, sizeof(buffer)); m_Current = A2CW(buffer); SetData(); UpdateData(FALSE); } void CTextPreview::OnRadio3() { USES_CONVERSION; if (CurrentOption == IDC_RADIO3) return; this->CheckRadioButton(IDC_RADIO1, IDC_RADIO3, IDC_RADIO3); CurrentOption = IDC_RADIO3; // PASSOLO: Luckily the application is Unicode, no need to loose character information CString str; str.LoadString(IDS_SAMLPETEXT); m_Current = (LPCTSTR) str; SetData(); UpdateData(FALSE); }
Localizing Fixed WORDPAD Copy old project from directory "Wordpad Start" or "Wordpad implemented" to "Wordpad fixed" Copy old project from directory "Wordpad Start" or "Wordpad implemented" to "Wordpad fixed" Update source string list and translation list Update source string list and translation list Translate new messages (Ids in StringTable ) Translate new messages (Ids in StringTable ) Start PASSOLOOpen "Wordpad Start"
Testing fixed WORDPAD Testing WORDPAD with "test.rtf" Testing WORDPAD with "test.rtf" In directory "Wordpad fixed" In directory "Wordpad fixed" Start WORDPAD
More i18n Issues
Internationalization (i18n) First Documentation can be created without having to know that it will be translated. Documentation can be created without having to know that it will be translated. But technologies like controlled language and good terminology management can improve the process! But technologies like controlled language and good terminology management can improve the process! Creating software without having localization in mind, means: Creating software without having localization in mind, means: Source code must be changed Source code must be changed High localization costs, delays High localization costs, delays Even project failure is possible Even project failure is possible
i18n – A Definition Internationalization covers all activities during the development process, which make it possible to adapt a product/program to different countries and cultures (also called locales).
i18n and the Human Factor Developers are often not aware of the problem Developers are often not aware of the problem Developers like to re-invent the wheel Developers like to re-invent the wheel int lower(int c) { if (c >= 'A' && c <= 'Z') return (c - 'A' + 'a'); return c; }
i18n and character sets 1 'Old' character sets (code page based) use the same code for different characters 'Old' character sets (code page based) use the same code for different characters
i18n and character set 2 'Old' character sets use complicated coding schemes for Asian scripts 'Old' character sets use complicated coding schemes for Asian scripts
I18n and character sets 3 Unicode is the new character set with about 1 Mio. possible characters Unicode is the new character set with about 1 Mio. possible characters But mapping to code page based character sets might still be necessary (if this fails you get '?') (Character does not exist in code page) But mapping to code page based character sets might still be necessary (if this fails you get '?') (Character does not exist in code page) Sometimes you see the wrong characters. This happens, when code page based characters are rendered with the wrong code page. (Character OK, code page wrong) Sometimes you see the wrong characters. This happens, when code page based characters are rendered with the wrong code page. (Character OK, code page wrong) Sometimes you see squares. This happens, when characters cannot be displayed with the selected font. (Character OK, Font wrong). Sometimes you see squares. This happens, when characters cannot be displayed with the selected font. (Character OK, Font wrong).
i18n and Programming 1 Typical... but wrong! Program code and GUI must be separated Program code and GUI must be separated Grammar is not the same for all languages Grammar is not the same for all languages Translating text fragments is more difficult, than translating in context Translating text fragments is more difficult, than translating in context C: AfxMessageBox("%d errors in file %s", cnt, fname); Basic: MsgBox(CStr(cnt) & " errors in file " & fname)
i18n and Programming 2 Translated strings are often longer Take care when designing dialogs/forms Take care when designing dialogs/forms Take care when using static buffers in software Take care when using static buffers in software
i18n and Graphics 1 Don‘t use/paint text in graphics
i18n and Graphics 2 Even graphics without text can be locale specific!
i18n - Summary Has to be done during document creation (Programming) Has to be done during document creation (Programming) Is important for efficient localization Is important for efficient localization Has many aspects and a lot of things can go wrong Has many aspects and a lot of things can go wrong If the localized product fails, it does not mean that the localizer failed If the localized product fails, it does not mean that the localizer failed
i18n – Tools, Books, Magazines SGIL von Everlasting Systems SGIL von Everlasting Systems I18N Expeditor von OneRealm I18N Expeditor von OneRealm Developing International Software Dr. International, Microsoft Press 2002, ISBN Developing International Software Dr. International, Microsoft Press 2002, ISBN Internationalization with Visual Basic Michael S. Kaplan, SAMS, ISBN Internationalization with Visual Basic Michael S. Kaplan, SAMS, ISBN Internationalization and Localization using Microsoft.NET Nick Symmonds, APRESS, ISBN Internationalization and Localization using Microsoft.NET Nick Symmonds, APRESS, ISBN Java Internationalization Andy Deitsch & David Czarnecki, O'Reilly, ISBN Java Internationalization Andy Deitsch & David Czarnecki, O'Reilly, ISBN MultiLingual Computing & Technology MultiLingual Computing & Technology