【发布时间】:2021-12-22 16:17:02
【问题描述】:
我已经阅读了很多关于使用 Python gettext 的帖子,但没有一篇涉及在运行时更改语言的问题。
使用gettext,字符串由函数_() 翻译,该函数全局添加到builtins。 _ 的定义是特定于语言的,并且会在语言设置更改时在执行期间更改。在代码中的某些点,我需要将对象中的字符串翻译成某种语言。这发生在:
- (重新)定义
builtins中的_函数以翻译为所选语言 - (重新)使用新的
_函数评估所需对象 - 保证对象定义中对@987654334@ 的任何调用都使用_的当前定义进行评估。 - 返回对象
我想知道第 2 步的不同方法。我想到了几种方法,但它们似乎都存在根本缺陷。
- 在实践中实现第 2 步的最佳方法是什么?
- 理论上是否可以在不了解其实现的情况下为任意对象实现第 2 步?
可能的方法
如果所有翻译文本都定义在可以在步骤 2 中调用的函数中,那么它很简单:调用该函数将使用 _ 的当前定义进行评估。但在很多情况下并非如此,例如,翻译后的字符串可能是在导入时评估的模块级变量,或者是在实例化对象时评估的属性。
此问题与模块级变量的最小示例是here。
重新评估
手动重新加载模块
可以使用importlib.reload 在所需时间重新评估模块级变量。如果模块导入另一个也具有翻译字符串的模块,这会变得更加复杂。您必须重新加载每个(嵌套)依赖项的模块。
了解模块的实现后,您可以按照正确的顺序手动重新加载依赖项:如果 A 导入 B,
importlib.reload(B)
importlib.reload(A)
# use A...
问题:需要了解模块的实现。仅重新加载模块级变量。
自动重新加载模块
在不了解模块实现的情况下,您需要以正确的顺序自动重新加载依赖项。您可以对包中的每个模块或just the (recursive) dependencies 执行此操作。要处理更复杂的情况,您需要生成依赖关系图并从根以广度优先顺序重新加载模块。
问题:需要复杂的重新加载算法。可能存在不可能的边缘情况(循环依赖、不寻常的包结构、from X import Y-style 导入)。仅重新加载模块级变量。
只重新评估所需的对象?
eval 允许您评估动态生成的表达式。相反,您能否在给定动态上下文 (builtins._) 的情况下重新评估现有对象的静态表达式?我想这将涉及递归地重新评估对象,以及在其定义中引用的每个对象,以及在其定义中引用的每个对象......
我查看了inspect 模块并没有找到任何明显的解决方案。
问题:不确定这是否可行。 eval 和类似的安全问题。
延迟评估
懒惰评估
Flask-Babel 项目提供了一个LazyString,它会延迟对已翻译字符串的评估。如果它可以完全延迟到第 2 步,那似乎是最干净的解决方案。
问题:LazyString 仍然可以在它应该被评估之前得到评估。很多事情都可能调用它的__str__ 函数并触发评估,例如字符串格式化和连接。
延迟翻译
python gettext docs 演示临时重新定义_ 函数,只有在需要翻译字符串时才调用实际翻译函数。
问题:需要了解对象的结构和为每个对象定制的代码,才能找到要翻译的字符串。不允许连接或格式化已翻译的字符串。
重构
所有翻译后的字符串都可以分解到一个单独的模块中,或者移到函数中,以便在给定时间对其进行完全评估。
问题: 据我了解,gettext 和全局 _ 函数的意义在于尽量减少翻译对现有代码的影响。像这样的重构可能需要重大的设计更改并使代码更加混乱。
【问题讨论】:
标签: python architecture internationalization gettext