Most applications communicate with the user through some messages on the graphical screen or console. Storing these messages in special constants allows to store them in a uniform way in separate files, which can be used for translation. A programmers interface exists to manipulate the actual values of the constant strings at runtime, and a utility tool comes with the Free Pascal compiler to convert the resource string files to whatever format is wanted by the programmer. Both these things are discussed in the following sections.
For each unit that is compiled and that contains a resourcestring section, the compiler generates a file that has the name of the unit, and an extension .rst. The format of this file is as follows:
For example, the following unit:
unit rsdemo; {$mode delphi} {$H+} interface resourcestring First = 'First'; Second = 'A Second very long string that should cover more than 1 line'; implementation end.Will result in the following resource string file:
# hash value = 5048740 rsdemo.first='First' # hash value = 171989989 rsdemo.second='A Second very long string that should cover more than 1 li'+ 'ne'The hash value is calculated with the function Hash. It is present in the objpas unit. The value is the same value that the GNU gettext mechanism uses. It is in no way unique, and can only be used to speed up searches.
The rstconv utility that comes with the Free Pascal compiler allows to manipulate these resource string files. At the moment, it can only be used to make a .po file that can be fed to the GNU msgfmt program. If someone wishes to have another format (Win32 resource files spring to mind) he/she can enhance the rstconv program so it can generate other types of files as well. GNU gettext was chosen because it is available on all platforms, and is already widely used in the Unix and free software community. Since the Free Pascal team doesn't want to restrict the use of resource strings, the .rst format was chosen to provide a neutral method, not restricted to any tool.
If you use resource strings in your units, and you want people to be able to translate the strings, you must provide the resource string file. Currently, there is no way to extract them from the unit file, though this is in principle possible. It is not required to do this, the program can be compiled without it, but then the translation of the strings isn't possible.
The objpas unit provides the mechanism to correctly initialize the string tables. There is no need to include this unit in a uses clause, since it is automatically loaded when a program or unit is compiled in Delphi or objfpc mode. Since this is required to use resource strings, the unit is always loaded when needed.
The resource strings are stored in tables, one per unit, and one for the program, if it contains a resourcestring section as well. Each resourcestring is stored with it's name, hash value, default value, and the current value, all as AnsiStrings.
The objpas unit offers methods to retrieve the number of resourcestring tables, the number of strings per table, and the above information for each string. It also offers a method to set the current value of the strings.
Here are the declarations of all the functions:
Function ResourceStringTableCount : Longint; Function ResourceStringCount(TableIndex : longint) : longint; Function GetResourceStringName(TableIndex, StringIndex : Longint) : Ansistring; Function GetResourceStringHash(TableIndex, StringIndex : Longint) : Longint; Function GetResourceStringDefaultValue(TableIndex, StringIndex : Longint) : AnsiString; Function GetResourceStringCurrentValue(TableIndex, StringIndex : Longint) : AnsiString; Function SetResourceStringValue(TableIndex, StringIndex : longint; Value : Ansistring) : Boolean; Procedure SetResourceStrings (SetFunction : TResourceIterator);Two other function exist, for convenience only:
Function Hash(S : AnsiString) : longint; Procedure ResetResourceTables;Here is a short explanation of what each function does. A more detailed explanation of the functions can be found in the Reference guide.
Given some Translate function, the following code would initialize all resource strings:
Var I,J : Longint; S : AnsiString; begin For I:=0 to ResourceStringTableCount-1 do For J:=0 to ResourceStringCount(i)-1 do begin S:=Translate(GetResourceStringDefaultValue(I,J)); SetResourceStringValue(I,J,S); end; end;Other methods are of course possible, and the Translate function can be implemented in a variety of ways.
for a given application, the following steps must be followed:
TranslateResourcestrings('intl/restest.%s.mo');the %s specifier will be replaced by the contents of the LANG environment variable. This call should happen at program startup.
Consider the following example:
Const help = 'With a little help of a programmer.'; Var A : AnsiString; begin { lots of code } A:=Help; { Again some code} TranslateStrings; { More code }After the call to TranslateStrings, the value of A will remain unchanged. This means that the assignment A:=Help must be executed again in order for the change to become visible. This is important, especially for GUI programs which have e.g. a menu. In order for the change in resource strings to become visible, the new values must be reloaded by program code into the menus...