Hard coded strings, characters, constants, screen positions, filenames, and file paths are difficult to track down and localize. Isolate all localizable items into resource files and minimize compile dependencies. One special case is character constants, which predefined Windows resource types do not handle.
In the following code segment, szInputString is a string from a list box selection, and the programmer is assuming that the selection can be for only one of several English words: Open, Save, Find, Copy, and Paste.
switch (szInputString[0])
{
case 'O':
DoOpen();
break;
case 'S':
DoSave();
break;
case 'F':
DoSearch();
break;
case 'C':
DoCopy();
break;
case 'P':
DoPaste();
break;
}
This code is efficient, but it's difficult to translate. Translating the character constants in this switch statement requires editing code directly. It's possible to extract the characters and replace them with #defined constants, but then the code would need to be recompiled for all languages.
One solution is to concatenate the single characters into a string that can then be placed in a resource file. Instead of comparing szInputString[0] with character constants, the revised code searches for it in the concatenated string and returns an index to the string if it finds a match. The switch statement is based on the possible index values.
... in the .RC file ...
STRINGTABLE DISCARDABLE
BEGIN
// OSFCP are the first letters of Open, Save, Find, Copy, Paste.
IDS_ABBREVS_COMMANDS "OSFCP"
END
... in the .C file ...
#define CAOpen 0
#define CASave 1
#define CAFind 2
#define CACopy 3
#define CAPaste 4
char szCommands[cbMaxSz];
int iCommand;
LoadString(hinst, IDS_ABBREVS_COMMANDS, szCommands, cbMaxSz);
iCommand = GetIndex(szInputString[0], szCommands);
switch (iCommand)
{
case CAOpen:
DoOpen();
break;
case CASave:
DoSave();
break;
case CAFind:
DoSearch();
break;
case CACopy:
DoCopy();
break;
case CAPaste:
DoPaste();
break;
}
It's poor practice to use numeric constants to indicate a position in a string that might change when the string is translated. The next code segment redraws szString repeatedly, filling in different percentage values. It avoids the use of an intermediate string by forcing characters into a fixed position in the middle of szString.
char szString[] =
"Searching... 0% complete";
#define ichPercent 13
while (!Finished())
{
int nPercent = PercentComplete();
ConvertPercentToString(nPercent, &szString[ichPercent]);
/* always writes three characters, leading spaces if necessary */
DrawString(szString);
DoSomethingInLoop();
}
Although you save some memory (the rewritten code below uses two buffers), the code in the example above is particularly difficult to translate. Not only must the string be translated accurately (note the extra spaces before the "0%" that are necessary for the 100 percent case), but the constant ichPercent must also be adjusted.
One possible alternative, which eliminates the constant (and thus a compile dependency), is to use a printf-like function.
... in the .RC file ...
STRINGTABLE DISCARDABLE
BEGIN
IDS_MSG_PERCENT_COMPLETE "Searching... %u%% complete"
END
... in the .RC file ...
char szFormat[cbMaxSz];
LoadString(hinst, IDS_MSG_PERCENT_COMPLETE, szFormat, cbMaxSz);
while (!Finished())
{
char szString[cbMaxSz];
int nPercent = PercentComplete();
sprintf(szString, szFormat, nPercent);
DrawString(szString);
DoSomethingInLoop();
}
An even better alternative is to eliminate this string altogether and use a progress indicator instead. (See The Windows Interface: An Application Design Guide for more information.)