是否可以在Delphi中组合资源字符串?

时间:2016-06-15 07:03:15

标签: delphi delphi-xe4

我想替换看起来像

的代码
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.';        

有这样的事情:

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;

但这会导致错误E2026 Constant expression expected.,我明白了。

尽管如此,我想知道是否有解决方案,允许我以上述方式定义RESSTR_ERRx_TRYAGAIN。 (目标是在不触及使用RESSTR_ERRx_TRYAGAIN的所有位置的情况下消除其他翻译)。

直到现在我唯一的想法是以下,但我不想使用它,因为这是相当丑陋的:

var
    RESSTR_ERR1_TRYAGAIN: string;
    //...

initialization
    RESSTR_ERR1_TRYAGAIN := RESSTR_ERR1 + ' ' + RESSTR_TRYAGAIN;
    //...

2 个答案:

答案 0 :(得分:5)

resourcestring个字符串在运行时处解析。每次引用resourcestring时,实际发生的是您正在调用 LoadResString() API以从应用程序资源加载(可能)翻译的字符串。

const声明是常量,必须在编译时完全定义。

即使是类型化常量(技术上是变量,受编译器设置限制)也必须在编译时初始完全定义,即使它可能稍后在运行时被修改。没有基于在运行时应用的转换自动更新常量的机制。

即使您可以按照您尝试的方式组合资源字符串,也不保存任何翻译,因为任何声明的资源字符串组合本身都必须是资源字符串,需要为该组合单独翻译:

resourcestring
   foo = 'foo.';  // Requires translation of 'foo.'
   bar = 'bar';   // Requires translation of 'bar'
   foobar = foo + bar   // Would require translation of 'foo.bar'

当然,正如您所发现的那样,第三个声明是不可能的,但即使它已经存在,它也不会为您节省额外的翻译。

您不能使用常量来保存组合的翻译值的原因是这些不是常数:

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

如果您主要关注减少声明常量的工作,那么您可以使用它:

const
  foo = 'foo.';
  bar = 'bar';

resourcestring
  foobar = foo + bar;

但是,您仍然需要为所有生成的资源字符串提供完整的常量部分,因此无法实现避免其他翻译的目标。

恒定解决方案的问题

任何旨在宣布合并resourcestring的解决方案都需要针对特定​​组合单独翻译,从而实现很少/没有任何好处(实际创造更多工作:额外翻译)。

任何试图使用资源字符串的编译时值声明常量的解决方案都不会在运行时进行转换。

使用初始化的处理方法会遇到一个相当复杂的复杂问题,即初始化的伪常量将包含初始化时资源字符串的转换值。如果您正在使用Sisulizer之类的东西,它允许运行时更改i18n资源,那么任何此类更改都不会反映在您的伪常量中。

您仍然可以使用此技术,但是将初始化代码放在您在初始化 时调用的函数中,如果/何时在运行时更改了翻译语言

运行时问题的运行时解决方案

如上所述,根本问题在于您尝试声明编译时常量,该常量仅由运行时解析的值组成。

您需要的是在运行时组合两个运行时值的方便可靠的机制。

如果再次尝试'示例是一个单独的案例,然后一个简单的函数将特定的资源字符串附加到某个其他指定的字符串(可能是一个资源字符串,或可能不是)可能就足够了:

function MessageWithTranslatedTryAgain(const aMessage: String): String;
begin
  if aMessage[Length(aMessage)] <> '.' then
    result := aMessage + '. ' + RESSTR_TRYAGAIN
  else
    result := aMessage + ' ' + RESSTR_TRYAGAIN;
end;

如果您有许多这样的可能组合,那么您可能会选择使用许多静态实用程序方法来实现一个类:

type
  Translate = class
  public
    class function MessageWithTryAgain(const aMessage: String): String;
    class function MessageWithContinue(const aMessage: String): String;
    // etc
  end;

答案 1 :(得分:2)

可以使用记录和运算符重载:

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;

用法:

ShowMessage(RESSTR_ERR1_TRYAGAIN);

我用dxgettext测试了这个并且它被正确翻译了。

(将Part1Part2定义为string的最初想法并不总是有效(正如Deltics所指出的):它确实编译但如果语言在运行时切换。)