Internationalizing your Delphi application: An ABC example

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 (no 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 snipped 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
  //By default
  Result:= '37.2°C';

  {read current system locale}
 
LangID := GetSystemDefaultLangID;

  //Assuming that only the United States and Belize use the Fahrenheit scale
  if (
LangID = {English - United States} 1033) or        
     (LangID = {English - Belize} 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

3 comments:

  1. If only it were that simple.

    In this sort of example you have to allow for the fact that the measurement system is configured separately from the language so if you wish to reflect the system settings correctly then you need to make a determination as to whether or not your message should reflect those and potentially apply a different localization to that applied to the text of the message itself.

    ReplyDelete
  2. Hi Jolyon, thanks for your comments.

    "...you have to allow for the fact that the measurement system is configured separately from the language so if you wish to reflect the system settings correctly then you need to make a determination as to whether or not your message should reflect those..."

    Yes, the measurement system does not have to be wired to the language, or even the locale, which was the one I used. What I wanted to show in the example is that we can’t let issues like the unit conventions go undetected when internationalization is on the plate. We as developers decide what level of internationalization is the proper one for our app. In my example, just for simplicity, I used the locale, which is sufficient for the most purposes.

    "...potentially apply a different localization to that applied to the text of the message itself."

    Note that localizing the measurement unit is not enough. The numbers accompanying those units change depending on the measurement system been used. It does not make sense to say 37.2°F for a guy with fever (the 37.2 has to be taken care of as well). So, handling unit conversions is something to keep in mind when internationalizing. Yes, it is not that simple, but hey, this was an ABC example ;-)

    ReplyDelete
  3. Hi! If you're involved in software localization projects, you should consider checking out this online localization tool: https://poeditor.com/

    ReplyDelete