Reputation: 1832
I want to replace code that looks like
resourcestring
RESSTR_ERR1_TRYAGAIN = 'Error 1. Please try again.';
RESSTR_ERR2_TRYAGAIN = 'Error 2. Please try again.';
RESSTR_ERR3_TRYAGAIN = 'Error 3. Please try again.';
with something like this:
resourcestring
RESSTR_ERR1 = 'Error 1.';
RESSTR_ERR2 = 'Error 2.';
RESSTR_ERR3 = 'Error 3.';
RESSTR_TRYAGAIN = 'Please try again.';
RESSTR_ERR1_TRYAGAIN = RESSTR_ERR1 + ' ' + RESSTR_TRYAGAIN; //error
RESSTR_ERR2_TRYAGAIN = RESSTR_ERR2 + ' ' + RESSTR_TRYAGAIN;
RESSTR_ERR3_TRYAGAIN = RESSTR_ERR3 + ' ' + RESSTR_TRYAGAIN;
But this leads to error E2026 Constant expression expected.
, what I do understand.
Nevertheless, I wonder if there is a solution, that allows me to define RESSTR_ERRx_TRYAGAIN
in the way described above. (The goal is to eliminate the additional translations without touching all the places where RESSTR_ERRx_TRYAGAIN is used).
My only idea until now is the following, but I don't want to use this, because this is rather ugly:
var
RESSTR_ERR1_TRYAGAIN: string;
//...
initialization
RESSTR_ERR1_TRYAGAIN := RESSTR_ERR1 + ' ' + RESSTR_TRYAGAIN;
//...
Upvotes: 1
Views: 837
Reputation: 23056
resourcestring
strings are resolved at runtime. Every time you reference a resourcestring what is actually happening is that you are calling the LoadResString() API to load the (potentially) translated string from the application resources.
const
declarations are constants, which must be fully defined at compile-time.
Even a typed constant (technically a variable, subject to compiler settings) must be initially fully defined at compile time, even though it may be potentially later modified at runtime. There is no mechanism to automatically update constants based on translations that are applied at runtime.
Even if you could combine resource strings in the way that you are trying, you do not save any translation because any declared combination of resource strings would itself have to be a resource string, requiring a separate translation for that combination:
resourcestring
foo = 'foo.'; // Requires translation of 'foo.'
bar = 'bar'; // Requires translation of 'bar'
foobar = foo + bar // Would require translation of 'foo.bar'
Of course, as you have discovered, that third declaration is not possible, but it would not save you the additional translation even if it were.
The reason you cannot use a constant to hold the combined, translated values is that these are not constant:
resourcestring
foo = 'foo.'; // Requires translation of 'foo.'
bar = 'bar'; // Requires translation of 'bar'
const
foobar = foo + bar // When referenced, foo and bar are actually calls to a function and so are not constant
If you are concerned primarily with reducing work in declaring your constants, then you could use this:
const
foo = 'foo.';
bar = 'bar';
resourcestring
foobar = foo + bar;
But you still then need to provide for all the resulting resource strings with their complete constant parts and so fails to achieve the goal of avoiding additional translations.
Any solution which contrives to enable declaration of a combined resourcestring
will demand a separate translation for that specific combination achieving little/no benefit (actually creating more work: the additional translations).
Any solution which contrives to declare a constant using the compile-time values of the resource string will not be translated at run-time.
Your work-around with initialization suffers from a rather more subtle complication which is that your initialized pseudo-constants will contain the translated values of the resource strings at the time of initialization. If you are using something like Sisulizer which allows runtime changes of i18n resources then any such changes will not be reflected in your pseudo-constants.
You could still use this technique,but place your initialization code in a function which you call at initialization and if/when-ever the translation language is changed at runtime.
As explained, the underlying problem is that you are trying to declare a compile-time constant composed of values that are only resolved at run-time.
What you need instead is a convenient, reliable mechanism to combine two runtime values at runtime.
If the 'try again' example is an isolated case then a simple function to append a specific resource string to some other specified string (which may be a resource string, or may not) might suffice:
function MessageWithTranslatedTryAgain(const aMessage: String): String;
begin
if aMessage[Length(aMessage)] <> '.' then
result := aMessage + '. ' + RESSTR_TRYAGAIN
else
result := aMessage + ' ' + RESSTR_TRYAGAIN;
end;
If you have a number of such possible combinations then you might instead choose to implement a class with a number of static utility methods:
type
Translate = class
public
class function MessageWithTryAgain(const aMessage: String): String;
class function MessageWithContinue(const aMessage: String): String;
// etc
end;
Upvotes: 5
Reputation: 5566
It is possible using a record and operator overloading:
interface
type
TSpaceSeparatedResourceStrings = record
Part1: PResStringRec;
Part2: PResStringRec;
class operator Implicit(From: TSpaceSeparatedResourceStrings): string;
end;
resourcestring
RESSTR_ERR1 = 'Error 1.';
RESSTR_ERR2 = 'Error 2.';
RESSTR_ERR3 = 'Error 3.';
RESSTR_TRYAGAIN = 'Please try again.';
const
RESSTR_ERR1_TRYAGAIN: TSpaceSeparatedResourceStrings
= (Part1: @RESSTR_ERR1; Part2: @RESSTR_TRYAGAIN);
RESSTR_ERR2_TRYAGAIN: TSpaceSeparatedResourceStrings
= (Part1: @RESSTR_ERR2; Part2: @RESSTR_TRYAGAIN);
RESSTR_ERR3_TRYAGAIN: TSpaceSeparatedResourceStrings
= (Part1: @RESSTR_ERR3; Part2: @RESSTR_TRYAGAIN);
implementation
class operator TSpaceSeparatedResourceStrings.Implicit(From: TSpaceSeparatedResourceStrings): string;
begin
Result := LoadResString(From.Part1) + ' ' + LoadResString(From.Part2);
end;
Usage:
ShowMessage(RESSTR_ERR1_TRYAGAIN);
I tested this with dxgettext and it is translated correctly.
(The original idea to define Part1
and Part2
as string
does not always work (as Deltics pointed out): It does compile but is not translated correctly if the language is switched at runtime.)
Upvotes: 2