Skip to main content

Designing a World-Ready Program

 

Setting Up a World-Ready Development Environment

Along with assembling your product team, you will need to provide a development environment that is conducive to world-readiness.



On This Page

Eliminating Compile DependenciesEliminating Compile Dependencies

A key prerequisite to creating a world-ready core code base for your program is that all language editions share the same core source files. Maintaining separate source files for different language editions of the same product is error-prone, a waste of time and disk space, and unnecessary for code that has been made world-ready.

When you have shipped or are close to shipping your domestic product, you might decide to make a copy of your current source files and set up a separate development project for the next product revision. In such a case, you will need to pass bug fixes to the new project and incorporate updated code or features into the old project for international editions that have not yet shipped. When the schedules for your international releases overlap with the schedule for the next product revision, you might be torn between your desire to take advantage of improvements and your desire to ship to international markets on schedule. Obviously, you have to use your best judgment in these situations.

Just as all language editions should share a single set of source files, all language editions should share a single bug database or bug-tracking system. Many defects will be common to all language editions, but bugs that first appear only in non-native editions can reveal design flaws with regard to world-readiness. Those responsible for testing certain features in native editions should also be responsible for testing the same features in the international editions, or with non-native settings and parameters. It's a good idea to have dedicated test scripts for localized products and help from native speakers to test certain features, but the most successful philosophy is that of one product, one team.

As you start a new project, be sure to select tools-such as editors and compilers-that will help rather than hinder world-readiness. If you plan to write customized build tools or design your own resource-handling mechanisms (instead of using Microsoft Win32 resource files), first consider what effect they will have on your international editions. If you must write your own build tools, make them world-ready the way you would your main executable.

Top of pageTop of page

Eliminating Compile Dependencies

A major incentive for creating world-ready core code is not having to recompile source files to create localized editions of your program. If you eliminate compile dependencies by avoiding hard-coded strings or constants and by not putting localizable resources in header files, your localization process will go much faster. (See "Designing an International User Interface That's Localizable" earlier in this article.)

Very large programs take hours to compile, even with powerful processors. Building debug and nondebug executables from scratch for each language takes significantly longer (unless you buy a number of expensive machines) and requires large amounts of disk space. If you share common code, you have to compile your main executable only once. To create localized editions, you first separate items like strings and graphics from your source code, and then you compile only these localized resource files and link them to the executable or to a separate DLL. (See Localizability Overview, Isolate Localizable Resources, String Handling, Mirroring, and UI Considerations for details) It's even possible to eliminate compilation entirely by using localization tools that allow translators to edit executables directly.

To handle those parts of the code that will change from one language edition to another, you can create a directory structure that separates your core code from each language version of your localized resources. Each set of resources can be placed in a single directory. Translators need access only to the directory containing the native-language files and the localized files for which they are responsible. A multilingual build process can incorporate resource files from the appropriate language directory and place resulting files in that particular language's Build directory. (See Figure 2-4.)

Figure 2.4 - Sample directory structure.

Figure 2.4 - Sample directory structure.

All files needing language customization are located in the Resources directory. Developers update files in the Native directory and use batch files to propagate the changes to other language directories. (The Neutral directory separates all your resources that do not need translating from those that do.)

This "no-compile" strategy has a number of obvious benefits. Separating code from localizable resources helps you write world-ready code. Also, significantly reduced compile times allow for faster turnaround in the development, testing, and translation cycle. Testers, for example, have to test only one executable. Translators, especially third-party consultants, don't need to have access to source code-with only an executable, resource files, and a build script, they can compile a localized program to check their work.

Without this "no-compile" strategy, you might need more than one core executable-one for all single-byte, left-to-right languages; one for multibyte languages; one for single-byte, bidirectional languages and several for languages based on complex scripts. Multibyte, right-to-left, and complex-script languages require special input and layout handling. A single code base for Windows XP was used to produce more than 24 different language editions (35 in Windows Vista), all of which use a no-compile approach.

Even though source files for Windows contain #ifdef statements to distinguish code bases, #ifdefs are never used to handle cases specific to individual languages. The best place to put #ifdef statements is in header files. Don't use code with #ifdef statements to fix bugs that occur only in the nondomestic language editions, since this practice makes international concerns too easy to ignore. With the increasing importance of multilingual documents, it's harder to label bugs as being "international only." Often the code surrounding #ifdefs is poorly designed and the #ifdef is simply a hack. You can be sure that developers who use #ifdefs and hacks to fix international-related problems will forget to update the code inside the #ifdefs when they update the surrounding code.

You can easily replace language-specific #ifdef statements with run-time if and switch statements. The following fragment from a hypothetical program's startup code uses #ifdefs to omit features for certain language editions:


#ifdef USA
#define DEFAULT_PAPER_SIZE 1 // USLetter
#endif

#ifdef JAPAN
#define NO_SPELL_CHECKER
#define EASTASIA
#define DEFAULT_PAPER_SIZE 2 // A4
#endif

#ifdef KOREA
#define NO_SPELL_CHECKER
#define EASTASIA
#define DEFAULT_PAPER_SIZE 2 // A4
#endif

...

#ifndef NO_SPELL_CHECKER
InitSpellChecker(); // no spell-checker for Japan or Korea


#endif

...

#ifdef JAPAN
InitImperialCalendar(); // This is for Japan only.
#endif
...

#ifdef EASTASIA
InitIME(); // needed for all East Asian builds
#endif

This code is rewritten as follows to use a global variable that is initialized at startup time. Before the program calls any code related to spelling checkers, IMEs, or paper sizes, it checks this variable. (See topics under Locale/Culture Awareness on Globalization Step-by-Step page for a more detailed explanation).

typedef struct _LOCINFO
{
int fSpellChecker;
int fUsesIME;
int DefaultPaperSize;
LCID lcidDefaultLocale;
} LOCINFO;

...

LOCINFO locinfo;

GetLocInfo( // function implemented in language DLL

if (locinfo.fSpellChecker)
InitSpellChecker();

if (locinfo.fUsesIME)
InitIME();

if (PRIMARYLANGID(LANGIDFROMLOCALEID(locinfo.lcidDefaultLocale)) ==
?? LANG_JAPAN)
{
InitImperialCalendar();
} ...