如何将 gettext 翻译应用于 case 语句中的字符串文字?

Ray*_*ger 18 python gettext structural-pattern-matching

我需要将 gettext 翻译添加到代码中的所有字符串文字中,但它不适用于 case 语句中的文字。

这次失败的尝试给出SyntaxError: Expected ':'

from gettext import gettext as _

direction = input(_('Enter a direction: '))   # <-- This works
match direction:
    case _('north'):                          # <-- This fails
        adj = 1, 0
    case _('south'):
        adj = -1, 0
    case _('east'):
        adj = 0, 1
    case _('west'):
        adj = 0, -1
    case _:
        raise ValueError(_('Unknown direction'))
Run Code Online (Sandbox Code Playgroud)

该错误是什么意思以及如何标记翻译方向?

Ray*_*ger 18

错误是什么意思?

match/case 语句的语法将 视为_通配符模式。后面唯一可接受的标记是冒号。由于您的代码使用左括号,因此会引发SyntaxError 。

如何修复它

文字模式(例如)切换case "north": ...值模式(例如case Directions.north: ...使用点运算符)。

然后可以在 case 语句之外的上游执行翻译:

from gettext import gettext as _

class Directions:
    north = _('north')
    south = _('south')
    east = _('east')
    west = _('west')

direction = input(_('Enter a direction: '))
match direction:
    case Directions.north:
        adj = 1, 0
    case Directions.south:
        adj = -1, 0
    case Directions.east:
        adj = 0, 1
    case Directions.west:
        adj = 0, -1
    case _:
        raise ValueError(_('Unknown direction'))
Run Code Online (Sandbox Code Playgroud)

不仅字符串文字得到翻译,case 语句也更具可读性。

更先进、动态的解决方案

上述解决方案仅在语言选择不变的情况下才有效。如果语言可以更改(也许在为来自不同国家/地区的用户提供服务的在线应用程序中),则需要动态查找。

首先,我们需要一个描述符来动态地将模式属性查找转发到函数调用:

class FuncCall:
    "Descriptor to convert fc.name to func(name)."

    def __init__(self, func):
        self.func = func

    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, obj, objtype=None):
        return self.func(self.name)
Run Code Online (Sandbox Code Playgroud)

我们这样使用它:

class Directions:
    north = FuncCall(_)  # calls _('north') for every lookup
    south = FuncCall(_)
    east = FuncCall(_)
    west = FuncCall(_)

def convert(direction):
    match direction:
        case Directions.north:
            return 1, 0
        case Directions.south:
            return -1, 0
        case Directions.east:
            return 0, 1
        case Directions.west:
            return 0, -1
        case _:
            raise ValueError(_('Unknown direction'))
    print('Adjustment:', adj)
Run Code Online (Sandbox Code Playgroud)

这是一个示例会话:

>>> set_language('es')   # Spanish
>>> convert('sur')
(-1, 0)
>>> set_language('fr')   # French
>>> convert('nord')
(1, 0)
Run Code Online (Sandbox Code Playgroud)

值模式的命名空间

任何带有点查找的命名空间都可以在值模式中使用:SimpleNamespace、Enum、模块、类、实例等。

这里选择一个类是因为它很简单并且可以使用更高级解决方案所需的描述符。

没有考虑枚举,因为它要复杂得多,而且它的元类逻辑会干扰描述符。此外,Enum 旨在为预定义常量提供符号名称,而不是像我们在这里使用的那样动态计算值。