PsychoPy 本地化(i18n)机制全解析:翻译文件构建、_translate 使用与多语言贡献指南

发布时间:2026/10/15 3:02:07
PsychoPy 本地化(i18n)机制全解析:翻译文件构建、_translate 使用与多语言贡献指南
科研教育【免费下载链接】psychopyFor running psychology and neuroscience experiments项目地址https://gitcode.com/gh_mirrors/ps/psychopy点击查看免费下载PsychoPy 是一个用于运行心理学与神经科学实验的开源实验构建工具其图形界面Builder/Studio、Coder 等的菜单、参数标签、提示文本都依赖一套内置的本地化翻译体系。本文以仓库内psychopy/localization/locales与psychopy/localization的官方文档为骨架结合 translation.py、extract.py 等源码实现系统讲解 PsychoPy 翻译系统的整体工作流翻译文件为什么在源码仓库里是空的、构建时如何被注入、代码中如何用_translate标记可翻译字符串、底层 gettext 机制如何工作以及开发者如何为某一种语言补充翻译并提交贡献。读完你将能够判断哪些字符串该翻译、正确调用_translate、手动向 locales 目录注入翻译数据并理解整个翻译管线的数据流。一、整体工作流翻译数据从哪里来、到哪里去PsychoPy 的本地化体系由三个环节组成源码标记开发者在 PsychoPy 源码中用_translate()函数包住所有用户可见的字符串主要是 Builder 界面中Param对象的hint与label数据生成与注入可翻译的字符串清单与各语言译文以.json文件形式存放在独立的psychopy-translation仓库中当 PsychoPy 被打包构建时构建脚本会把这些文件拉取下来并注入到psychopy/localization/locales目录其中一部分会被编译为 gettext 使用的.mo格式运行时查表PsychoPy 启动时根据系统 locale 或用户在偏好设置中指定的语言通过 gettext 在 locales 目录中查找对应语言的译文找不到译文时原样返回英文字符串。从源码结构看负责运行时查表的模块是 psychopy/localization/init.py它只向外暴露四个符号localedir、getLocale、setLocale与_translate。整个翻译系统的核心入口非常收敛。二、为什么源码仓库里的 locales 目录是空的psychopy/localization/locales/README.md开篇就点明了这个令许多开发者困惑的问题除非你正在阅读的是已构建的 PsychoPy 安装包内的文件否则locales目录下所有翻译文件都是空的。原因在于真正存放译文数据的仓库是独立的psychopy-translation仓库文档中给出了其 GitHub 地址。PsychoPy 源码仓库刻意不携带这些翻译数据而是在构建时build 阶段从该仓库拉取翻译文件并插入到locales目录中。这是一种典型的数据与代码分离做法翻译数据体积大、更新频繁不适合随源码主仓库一起维护翻译贡献者只需维护翻译仓库不必处理 PsychoPy 主仓库的合并冲突主仓库保持精简locales目录在源码形态下仅包含一个说明用的README.md即当前这份文档。当前仓库中 psychopy/localization/locales 目录的实际内容也印证了这一点它只含README.md没有任何.json或.mo翻译文件。手动注入翻译文件的命令如果你确实需要在自己本地例如开发环境或调试构建流程得到翻译数据可以先把 psychopy-translation 作为 Python 包 clone 并安装然后执行python -m psychopy_translation.generate --folder path/to/this/folder --format mo其中--folder指向本仓库的psychopy/localization/locales目录--format mo表示把 JSON 译文编译为 gettext 的.mo二进制格式下文会说明.mo与运行时机制的关系。文档特别强调生成出来的这些文件不要提交回 PsychoPy 主仓库。因为它们是构建产物应在构建流程中自动生成而不是作为源码的一部分入库。三、运行时底层机制gettext、localedir 与翻译器要理解_translate为什么能自动按语言返回译文需要看 psychopy/localization/translation.py 的实现。这个模块只有约 75 行逻辑非常清晰。1. localedir翻译文件的查找根目录localedir str(Path(__file__).parent / locales)模块把locales目录即psychopy/localization/locales固定为翻译数据的根目录并调用gettext.bindtextdomain(messages, localedirlocaledir)这里绑定的 domain 名为messages。结合 Python 标准库 gettext 的查找约定可以推断构建产物形态下locales 目录内部应按语言代码/LC_MESSAGES/messages.mo的组织方式存放各语言的.mo文件这也是--format mo命令的意义所在。2. setLocale 与 getLocale语言的选择与回退def setLocale(value): global currentLocale global translator # if requested system, get system locale if value in (None, system locale, system): value locale.getlocale()[0] # use English as a fallback if locale is undetectable if value is None: logging.warning(Could not detect system locale, using en-US) value en-US # sanitize value value.replace(_, -) # set currentLocale value # recreate translator try: translator gettext.translation( messages, localedirlocaledir, languages[currentLocale] ) except FileNotFoundError: # if theres no translations for this language, dont translate translator None几个值得注意的细节系统 locale 优先setLocale(None)或传system locale/system会读取locale.getlocale()[0]即跟随操作系统的语言设置兜底为 en-US如果系统 locale 无法检测到会记录一条 warning 日志并回退到en-US语言代码规范化会把_替换为-如en_US→en-US使代码中传入的语言代码与文档约定的en-GB这类写法保持一致无译文即不翻译如果该语言没有对应的.mo文件抛出FileNotFoundErrortranslator被置为None后续所有_translate调用都会原样返回输入字符串——这正是找不到翻译就退回英文原文的机制。模块文件末尾执行了setLocale(None)即默认跟随系统语言getLocale()则返回当前生效的currentLocale字符串。3. _translate翻译器的薄封装def _translate(value): if translator: return translator.gettext(value) else: return value_translate本质上是对 gettexttranslator.gettext的一层封装有可用翻译器就查表翻译没有就原样返回。这也是为什么文档建议即使你不确定某个字符串有没有译文也应当用_translate包裹它——不会有任何副作用。4. 语言配置入口prefs 中的 locale 选项用户或实验者可以在偏好设置中指定界面语言。各平台的偏好规格文件中都定义了该选项例如 psychopy/preferences/baseNoArch.spec 中的[app]段# language to use in menus etc; not all translations are available. Select a value, then restart the app. locale string(default)即app.locale为空字符串时默认跟随系统指定某个语言代码后需要重启 PsychoPy 应用才能生效该提示也出现在 psychopy/preferences/hints.py 的_translate包裹文本中说明偏好设置本身的提示文本也参与本地化。从 psychopy/preferences/preferences.py 的注释可以看到locale 的实际设置动作已交由psychopy.localization模块在导入时完成。四、什么时候该翻译只翻译用户可见的字符串psychopy/localization/README.md给出了一个非常实用的判断准则大多数字符串不需要翻译。仅在代码内部定义和使用、用户永远看不到的字符串翻译它们没有意义更危险的是翻译可能破坏代码逻辑。文档举了一个具体例子TextBox2组件内部判断direction horiz如果direction的值被翻译成了另一种语言这段判断就永远无法命中组件行为直接出错。因此需要翻译的字符串是用户可见的那部分最常见的就是 BuilderStudio界面中Param对象的hint鼠标悬停提示与label参数标签。从源码看Param的构造函数确实预留了这两个字段psychopy/experiment/params.py 中hint、label是默认参数它们最终被序列化并展示在组件属性面板中。五、如何在代码中标记可翻译字符串用法极其简单只需从psychopy.localization导入_translatefrom psychopy.localization import _translate myTranslatedString _translate(This string will be translated)运行时myTranslatedString的值取决于偏好设置prefs.app[locale]或系统 locale 所对应语言中的译文如果没有译文则原样使用传入的英文。源码中的典型用法在 psychopy/experiment/components/_base.py 中所有 Builder 组件的通用参数Name、Start、Stop、单位、颜色等都是这样声明的例如msg _translate(When does the Component start?) ... label_translate(Start) msg _translate(Units of dimensions for this stimulus) ... label_translate(Spatial units)在 psychopy/experiment/_experiment.py 中加载实验时对 Saved data folder 这类旧参数的处理同样使用_translate包住hintparams[name] Param( val, valTypecode, allowedTypes[], hint_translate(Name of the folder in which to save data and log files (blank defaults to the builder pref)), categData)甚至连加载实验文件时给用户的警告消息也是可翻译的psychopy/experiment/_experiment.pymsg _translate( Parameters not known to this version of PsychoPy have come from your experiment file: %s. This experiment may not run correctly in the current version. )此外psychopy/experiment/localization.py 专门集中存放会被频繁使用的翻译调用提前翻译一次、多处复用避免重复查询。一个值得借鉴的工程实践告警消息的自动本地化PsychoPy 的告警系统同样接入翻译体系。psychopy/alerts/alertsCatalogue/下的告警消息本体定义在 YAML 目录中而 psychopy/alerts/alertsCatalogue/generateAlertmsg.py 会自动遍历告警目录把每条告警的msg转成一行_translate(...)并写入 psychopy/alerts/alertsCatalogue/alertmsg.pyfrom psychopy.localization import _translate # Alert 2115 _translate(Your stimulus size exceeds the {dimension} dimension of your window.)生成的脚本会转义反斜杠与双引号、跳过 9998/9999 两个内部告警最后用autopep8格式化。而 psychopy/alerts/_alerts.py 运行时导入_translate来翻译告警文本。这种做法把可翻译字符串清单从大量业务代码中抽离出来集中在一个自动生成的文件里与下面的键提取思路一脉相承。六、翻译键的自动提取与维护psychopy/localization/extract.py 提供了一套基于 Pythonast抽象语法树的翻译键提取工具用于保持源码中的_translate调用与翻译文件中的条目同步它遍历指定模块默认psychopy.experiment目录下所有*.py文件用 AST 解析出所有_translate(硬编码字符串)形式的调用仅收录第一个参数为字面量hasattr(node.args[0], value)的调用跳过变量传参等无法静态确定的情况提取出的键集合会合并进 locales 目录下每一个.json翻译文件新键的译文留空dict.fromkeys(keys, )等待人工翻译。也就是说当你新增一个_translate(...)调用后运行提取脚本就能自动把新字符串以空值追加到所有语言的翻译文件中避免遗漏。这也解释了翻译文件的结构每个.json文件以字符串为键、以对应语言的译文为值空值条目即为尚未翻译的待办项。七、如何为某种语言添加翻译1. 获取翻译仓库副本译文数据维护在独立的psychopy-translation仓库中。要贡献翻译标准做法是先在 GitHub 上对该仓库创建 fork副本再把 fork clone 到本地。文档推荐初学者使用 GitHub Desktop 这类图形化工具熟悉命令行的话也可以直接git clone。2. 翻译文件的结构与命名翻译文件是.json格式每个文件对应一种语言其中每个条目以 PsychoPy 中的字符串原文为键、以该语言的译文为值。文件命名遵循 ISO 语言代码如en与 ISO 国家/地区代码如GB的组合中间用连字符分隔例如en-GB.json英国英语en-US.json美国英语这类文件位于psychopy/localization/locales目录即本文反复提到的 locales 目录构建期从翻译仓库注入的就是这些数据。3. 编辑工具.json文件用任何文本编辑器都能直接改。如果希望有更直观的可视化界面文档推荐poedit免费、易用、跨平台可以直接打开翻译文件逐条填写译文。4. 空白条目是你的目标文档明确指出翻译文件中空白的条目就是还没有译文的字符串。想为某个语言做贡献打开对应语言的.json把这些空值逐条填上译文即可——这些条目就是贡献的靶子。八、提交翻译完成翻译后用 git 提交并 push 到你的分支然后以 Pull Request 的形式提交给上游文档指向了官方 PR 指南。提交时需要注意只提交翻译数据仓库psychopy-translation的改动不要把生成出来的.mo等产物提交进 PsychoPy 主仓库如第二节所述遵循 JSON 格式与原有条目命名规范避免破坏提取脚本的合并逻辑。九、已知限制文档明确列出了这套翻译系统的三个边界理解它们有助于正确使用_translate是刻意选用的非标准函数名。它避开了_这个在 Python 中常被用作哑变量忽略不需要的值的名字防止命名冲突只翻译字符串内容不处理排版方向与语序。它不会影响文本在页面上的位置也不会调整句子的呈现顺序——这在某些需要从右到左RTL书写的语言中可能造成问题只适用于可以预先知道的用户可见字符串即标签、提示这类固定文本它不适用于用户自己写的实验脚本。从 psychopy/locale_setup.py 的模块注释也可以看到PsychoPy 对实验脚本的 locale 处理初衷是规避 macOS 上的 Unicode 崩溃问题该模块只做了locale.setlocale并回退en_US.UTF-8而不是为实验内容本身提供多语言切换文档明确说几乎不会有人希望实验里显示的文字随系统语言变化如果确有需要直接为不同语言制作多个实验版本更简单可靠。总结PsychoPy 的本地化体系是一套代码与数据分离、构建期注入、运行期 gettext 查表的完整管线开发者只负责在源码中用_translate标记用户可见字符串翻译贡献者通过 fork 翻译仓库、编辑按 ISO 语言代码命名的 JSON 文件来提供译文构建时由psychopy_translation.generate --format mo把数据注入locales目录并编译为 gettext 的.mo格式运行时则由 translation.py 依据系统 locale 或偏好设置中的app.locale完成查表与回退。掌握这套机制后无论是为 PsychoPy 界面补充一种语言的翻译还是在自己的实验中正确使用可翻译字符串你都能清晰判断什么该翻、怎么标记、数据放哪、如何生效。赞分享科研教育【免费下载链接】psychopyFor running psychology and neuroscience experiments项目地址https://gitcode.com/gh_mirrors/ps/psychopy点击查看免费下载相关推荐PsychoPy 多语言本地化指南_translate 函数、翻译文件与贡献工作流PsychoPy 多语言本地化指南 _translate 函数、翻译文件与贡献工作流 PsychoPy用于心理与神经科学实验的 Python 库通过内置的科研教育ShellInABox与容器化部署Docker集成完整教程ShellInABox与容器化部署Docker集成完整教程 ShellInABox是一款革命性的网页版终端工具它能将Linux/Unix命令行界面直接嵌入到后端Starward 本地化贡献指南文档翻译与应用内文本多语言工作流全解析Starward 本地化贡献指南文档翻译与应用内文本多语言工作流全解析 Starward 作为一款面向全球玩家的米家游戏启动器其多语言能力来自一套清晰的两段桌面应用上一篇Hudi DeltaStreamer Protobuf 支持实战指南从 Kafka Protobuf 消息到湖仓数据的全链路解析下一篇NextTrace Deploy 模式验证指南启动检查、认证冒烟测试与 CLI 回归覆盖创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考