If you want to make your Delphi application general enough to address multiple locales, then you need to internationalize it. There are three common aspects that I want to emphasize (not necessarily in that order):
- Resourcing
- Unit conversions
- Dynamic messages
We’ll cover the three of them with a very simple example. Consider the following code snippet intended for the en-US locale (English-United States of America):
procedure TForm1.DefineFever;
begin
ShowMessage('If the body temperature rises above 99°F the person is considered to have a fever.');
end;
Resourcing is the process of removing hard-coded strings from the code by making them resourcestrings
instead.
The code above is not localizable because the ShowMessage
procedure is taking a hard-coded string. What do you do? Take a look:
procedure TForm1.DefineFever;
resourcestring
strFeverDefinition = 'If the body temperature rises above 99°F
the person is considered to have a fever.';
begin
ShowMessage(strFeverDefinition);
end;
We defined strFeverDefinition
as a resourcestring
and used it as a parameter for the ShowMessage
procedure. The functionality remains the same, but the function is now localizable. Unit conversions: In some countries (like the United States and Belize) the temperature is given in the Fahrenheit scale, but in the rest is given in the Celsius scale. In order to internationalize this, we can do the following:
function GetFeverTemperature: string; var
LangID: LangID;
begin
Result:= '37.2°C';
LangID := GetSystemDefaultLangID;
if (LangID = 1033) or
(LangID = 10249) then Result:= '99°F';
end;
procedure TForm1.DefineFever;
begin
ShowMessage('If the body temperature rises above ' + GetFeverTemperature + ' the person is considered to have a fever');
end;
Wait a minute, we managed the unit conversion by introducing a dynamic message, but we reintroduced the hard-coded strings. That’s not good!
Dynamic messages: We consider the ShowMessage
above to be a dynamic message, because the parameter depends on the GetFeverTemperature
function, which of course can vary.
To solve the pitfall above, we can refactor the DefineFever
function as follows:
procedure TForm1.DefineFever;
resourcestring
strFeverDefinition = 'If the body temperature rises above %0:s
the person is considered to have a fever.';
begin
ShowMessage(Format(strFeverDefinition, [GetFeverTemperature]));
end;
We are just using a format string (resourcestring)
that we can format
by using the Format
routine. This allows resourcing and handling the dynamic message all at once.
The thing about dynamic messages goes beyond. In Spanish, for instance, the dynamic message would have been coded as follows: ShowMessage('Se considera que la persona tiene fiebre si la temperatura corporal es superior a ' + GetFeverTemperature);
Note that the GetFeverTemperature
is at the end of the ShowMesssage
parameter, as opposed to the English implementation that has it in the middle. There’s no way you can localize something like this if you don’t internationalize it first.
So the ABC for Delphi localization is Resourcing, Unit Conversions and Dynamic Messages.