You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用GnuGetText时,如何正确本地化Delphi默认ResourceStrings?

使用GnuGetText时本地化Delphi默认ResourceStrings的正确方法

问题背景

原本通过内存修改的方式替换Delphi默认ResourceStrings来本地化错误信息,在空白应用中可以正常工作:

implementation

uses System.SysConst;

procedure HookResourceString(rs: PResStringRec; newStr: PChar);
var
  oldprotect: DWORD;
begin
  VirtualProtect(rs, SizeOf(rs^), PAGE_EXECUTE_READWRITE, @oldProtect);
  rs^.Identifier := Integer(newStr);
  VirtualProtect(rs, SizeOf(rs^), oldProtect, @oldProtect);
end;

procedure TForm1.FormCreate(Sender: TObject);
var test: integer;
begin
  HookResourceString(@SInvalidInteger, '''%s'' no es un valor entero valido');
  test := StrToInt('test');  // 触发InvalidInteger错误
end;

但在项目的uses中添加gnuGetText后,代码会在System.SysUtils.ConvertErrorFmt处抛出"ERROR"异常,无法触发自定义翻译的异常。需要保留GnuGetText用于应用其他部分的本地化,因此需要找到兼容的系统ResourceStrings本地化方案。

原因分析

GnuGetText会接管Delphi的ResourceString加载逻辑(比如修改LoadResString的行为),导致直接修改ResStringRec.Identifier的内存Hook方法失效——GnuGetText会优先从自身翻译表读取字符串,而非使用你修改的内存地址指向的内容。

正确解决方案

方案1:利用GnuGetText原生机制翻译系统ResourceStrings

这是最推荐的方式,完全兼容GnuGetText的工作流程:

  1. 提取系统ResourceString的原始文本:比如SInvalidInteger的默认文本是'%s is not a valid integer',可通过LoadResString(@SInvalidInteger)获取原始值。
  2. 在PO翻译文件中添加对应条目:
    msgid "%s is not a valid integer"
    msgstr "'%s' no es un valor entero valido"
    
  3. 注册系统ResourceString到GnuGetText:
    在应用初始化阶段(比如initialization块),调用RegisterResourceString将系统ResourceString变量与msgid关联:
    uses
      System.SysConst, gnugettext;
    
    initialization
      RegisterResourceString(@SInvalidInteger, '%s is not a valid integer');
      // 其他需要本地化的系统ResourceStrings也按此方式注册
    end.
    
    这样GnuGetText在加载SInvalidInteger时会自动使用翻译后的文本,无需内存Hook。

方案2:替换系统错误处理函数

如果方案1无法覆盖所有场景,可以通过HookConvertErrorFmt函数,在抛出异常前手动翻译错误信息:

  1. 实现自定义错误处理函数:
    uses
      System.SysUtils, gnugettext;
    
    procedure CustomConvertErrorFmt(ResString: PResStringRec; const Args: array of const);
    var
      OriginalStr, TranslatedStr: string;
    begin
      OriginalStr := LoadResString(ResString);
      // 使用GnuGetText翻译原始字符串
      TranslatedStr := _(OriginalStr);
      // 格式化翻译后的内容
      TranslatedStr := Format(TranslatedStr, Args);
      raise EConvertError.Create(TranslatedStr);
    end;
    
  2. Hook系统函数:
    使用内存Hook库(如Delphi Detours)或手动修改函数地址,将ConvertErrorFmt替换为自定义函数:
    uses
      Detours;
    
    initialization
      InterceptCreate(@ConvertErrorFmt, @CustomConvertErrorFmt);
    finalization
      InterceptRemove(@ConvertErrorFmt);
    end.
    

方案3:配置GnuGetText忽略系统ResourceStrings(可选)

部分版本的GnuGetText提供了忽略系统单元ResourceStrings的选项,可通过设置全局变量禁用其对系统ResourceStrings的接管,这样你原来的内存Hook方法就能继续生效:

uses
  gnugettext;

initialization
  IgnoreSystemResourceStrings := True; // 需确认你的GnuGetText版本是否支持该变量
end.

内容的提问来源于stack exchange,提问作者Marc Guillot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 11:22:37