编程开发与计算机科学

Lazarus 项目支持多语言国际化(进阶篇)

在之前的一篇文章中简单介绍了如何在 Lazarus 项目中启用多语言国际化。不过使用 DefaultTranslatorSetDefaultLang() 无法实现运行时动态切换 GUI 的语言。更准确地说,在程序中首次调用 LCLTranslator.SetDefaultLang(),并将参数 ForceUpdate 设为 True,是能够在运行时修改 GUI 语言的,但若再次调用便无效了。

查看 SetDefaultLang() 源代码:

pascalfunction SetDefaultLang(Lang: string; Dir: string = ''; LocaleFileName: string = ''; ForceUpdate: boolean = true): string;
{ Arguments:
  Lang - language (e.g. 'ru', 'de', 'zh_CN'); empty argument is default language.
  Dir - custom translation files subdirectory (e.g. 'mylng'); empty argument means searching only in predefined subdirectories.
  LocaleFileName - custom translation file name; empty argument means that the name is the same as the one of executable.
  ForceUpdate - true means forcing immediate interface update. Only should be set to false when the procedure is
    called from unit Initialization section. User code normally should not specify it.
}
var
  lcfn: string;
  LocalTranslator: TUpdateTranslator;
  i: integer;

begin
  Result := '';
  LocalTranslator := nil;
  // search first po translation resources
  try
    lcfn := FindLocaleFileName('.po', Lang, Dir, LocaleFileName, Result);
    if lcfn <> '' then
    begin
      Translations.TranslateResourceStrings(lcfn);
      LocalTranslator := TPOTranslator.Create(lcfn);
    end
    else
    begin
      // try now with MO translation resources
      lcfn := FindLocaleFileName('.mo', Lang, Dir, LocaleFileName, Result);
      if lcfn <> '' then
      begin
        GetText.TranslateResourceStrings(UTF8ToSys(lcfn));
        LocalTranslator := TDefaultTranslator.Create(lcfn);
      end;
    end;
  except
    Result := '';
    lcfn := '';
  end;

  if lcfn<>'' then
    TranslateLCLResourceStrings(Lang, lcfn);

  if LocalTranslator<>nil then
  begin
    if Assigned(LRSTranslator) then
      LRSTranslator.Free;
    LRSTranslator := LocalTranslator;

    // Do not update the translations when this function is called from within
    // the unit initialization.
    if ForceUpdate=true then
    begin
      for i := 0 to Screen.CustomFormCount-1 do
        LocalTranslator.UpdateTranslation(Screen.CustomForms[i]);
      for i := 0 to Screen.DataModuleCount-1 do
        LocalTranslator.UpdateTranslation(Screen.DataModules[i]);
    end;
  end;
end;
lcltranslator.pas

该函数首先查找本地的 .po.mo 翻译文件;然后,将程序的资源字符串翻译成本地语言;接下来,创建一个新的 TUpdateTranslator 对象替代全局对象。

……

Free Pascal Hack 之访问对象的 protected 成员

默认情况下,Free Pascal 类中 protected 成员只能被当前类、子类以及同一个 Unit 中的代码访问。然而在实践中,往往会碰到访问其他 Unit 中定义的类实例对象的 protected 成员的需求。通常我们会通过类继承以及修改原始类成员的代码来暴露 protected 成员,然而当该对象来自第三方库,甚至 FCL 和 LCL 时,情况就会比较棘手。有一种 Hack 方法可以实现这个需求,且不用修改原始 Unit 的代码,没有额外副作用。

……

GetSystemMenu() 可能损坏其他进程的窗口菜单

通过 Windows API GetSystemMenu 获取窗口系统菜单(即窗口标题栏右键菜单)句柄,可以实现自定义窗口菜单的功能。但若获取的窗口菜单句柄来自其他进程的窗口,便会引发问题。

……

有趣的编程语言 Red

Red 项目最早始于 2011 年,项目由 Nenad Rakocevic 主导。Red 语言是一种同时具有函数式、声明式、符号式特性的现代编程语言,其目标是构建成一个全栈编程语言。Red 语言受到 Rebol 语言启发,两者语法高度相似。和 Rebol 语言只能解释执行不同,Red 语言还提供了 AOT 编译(生成独立的可执行文件)功能,在未来还将支持 JIT 编译。

Red 语言的主要特性:

……

Free Pascal 链接 C/C++ 静态库

本文提供的方法仅在 Windows 中测试通过,未在 Linux 中进行过测试,但理论上也能使用。

Free Pascal 中可以用非常简单的代码链接到动态库:

pascalfunction MyAdd(x, y: Integer): Integer; external 'mylib.dll' name 'MyAdd';

另外也可以链接到静态库,这样分发程序的时候就不用带上一堆 DLL 文件了。

首先将 C 代码编译为静态库,然后在 Lazarus > 项目选项 > 编译器选项 > 路径 > 库 (-Fl) 中输入静态库存放的路径。此处以 C 编写的静态库导出 MyAdd() 函数为例,演示如何在 Free Pascal 中调用它。

……

Zig 语言:从入门到放弃

一直想用 Zig 来写一点什么。正好看到一个 Zig 的 GUI 项目 Capy,想着用它来试试手。

首先,用 scoop install zig 命令安装了 Zig,版本是 0.14.0(后续会提到)。然后,按照 Capy 的官方文档下载了项目模板,使用 zig build 命令编译。接下来,就遇到第一个扑面而来的问题:

error: unable to discover remote git server capabilities: ConnectionTimedOut

可以确信的是,命令执行环境中已经正确地配置了代理,包括 Git 全局配置、环境变量和系统代理。Zig 没能正确使用代理设置。最后只能使用虚拟网卡实现代理。

……

为什么说鼓吹零基础 AI 编程的都是骗子

前阵子在社交媒体上看到一条消息:「我完全不懂编程,N 小时内写了一个 APP,荣登 App Store 排行榜」。我可以言之凿凿地论断:这就是一个卖课的骗子,所谓的排行榜也是靠作弊刷出来的。因为软件开发的内核从来不是你能否掌握一门编程语言,而是你能否像程序一样思考。目前的 AI 本质上不过是一个玩文字接龙游戏的程序罢了,它无法代替人进行思考(所谓强人工智能)。

……

编译 libmagic.dll

libmagic 是由 file 命令提供的一个开源库,用于检测文件的类型,最新版本为 5.46,发布于 2024年 11 月。PyPI 上有一个名为 python-magic-bin 的包提供了编译好的 Windows 二进制版本,然而最后一次更新已是 8 年前。Scoop 的 main 仓库中也包含了一个由第三方编译的 file 命令,版本号为 5.45,但没有提供 libmagic 的动态链接库。此外,5.45 的 Magic 文件版本是 19,而 5.46 已经升级到 20,两者不兼容。看来是时候考虑自己编译一个 Windows 版本了。

……

如何判断一个窗口句柄是否为桌面顶层窗口

在微软官方文档中,并没有提供一个 API 用于判断一个窗口句柄是否为桌面的顶层窗口。通过类似 NULL == GetParent(hwnd) 或者 hwnd == GetAncestor(hWnd,GA_ROOT) 等简单代码都无法做出正确判断。经过测试,找到两种靠谱的解决方案。

……

Free Pascal 平台无关的原生数据类型

可能是历史原因,Free Pascal 编程环境中的数据类型数量浩瀚如繁星,其中又有很多类型是运行时定义的别名。为了去繁从简,本文整理了 Free Pascal 编译器支持的平台无关且和其他语言二进制兼容的原生数据类型。

……