Internationalization 如何翻译bison中的标记名

Internationalization 如何翻译bison中的标记名,internationalization,bison,Internationalization,Bison,我有一个bison解析器,可以很好地满足我的需求。它甚至打印本地化的错误消息。但是标记名没有翻译。查看我找到的源代码,我可以将defineYY_用于我自己的gettext函数,并将YY_传递给gettext,以提供我自己对错误消息的翻译。但这不适用于令牌名称 是否有一些开关或隐藏特性可用于从解析器中提取令牌名称并对其进行转换 到目前为止,我找到了yytnamerr,它可以被重写以格式化令牌名称。由于它不仅仅是重新格式化名称,我不想碰这个函数,因为我必须将它与Bison的进度同步。另一方面,我还需

我有一个bison解析器,可以很好地满足我的需求。它甚至打印本地化的错误消息。但是标记名没有翻译。查看我找到的源代码,我可以将define
YY_
用于我自己的gettext函数,并将
YY_
传递给gettext,以提供我自己对错误消息的翻译。但这不适用于令牌名称

是否有一些开关或隐藏特性可用于从解析器中提取令牌名称并对其进行转换

到目前为止,我找到了
yytnamerr
,它可以被重写以格式化令牌名称。由于它不仅仅是重新格式化名称,我不想碰这个函数,因为我必须将它与Bison的进度同步。另一方面,我还需要一种从解析器中提取标记名的方法,以便将它们添加到语言定义文件中


如何使用Bison实现用户友好的错误报告?

如果指定
%token table
,则Bison将生成
yytname
表。此表包括所有bison符号,包括内部符号(
$end
$error
$undefined
)、终端(命名、单引号字符和双引号字符串)和非终端,其中还包括为mid规则操作生成的名称

yytname
可见时,很容易以
gettext
包可识别的格式提取令牌。例如,您可以向
.y
文件中添加如下内容:

#ifdef MAKE_TOKEN
int main(void) {
   puts("#include <libintl.h>");
   puts("#include <stdio.h>");
   puts("int main() {");
   for (const char* const* p = yytname; *p; ++p) {
     // See Note 1 below
     printf("  printf(\"%%s: %%s\\n\", \"%s\", gettext (\"%s\"));\n", *p, *p);
   }
   puts("}");
 }
 #endif
\ifdef生成令牌
内部主(空){
看跌期权(“包括”);
看跌期权(“包括”);
puts(“int main(){”);
for(常量字符*const*p=yytname;*p;++p){
//见下文注1
printf(“printf(\“%%s:%%s\\n\”,\%s\”,gettext(\%s\”);\n“,*p,*p);
}
卖出(“}”);
}
#恩迪夫
然后向Makefile添加一节(对文件名进行适当的替换):

messages.pot:my_parser.c
$(CC)$(CFLAGS)-DMAKE_TOKEN-o TOKEN_lister$<
./token\u lister>my\u parser.tokens.c
#见下文注2
$(CC)-o my_parser.tokens my_parser.tokens.c
xgettext-o$@my_parser.tokens.c
一旦有了翻译,您仍然需要弄清楚如何使用它们,因为bison不提供将翻译后的令牌名称插入其生成的错误消息的接口。可能最简单的方法是直接将翻译插入
yytname
,方法是迭代该数组并用翻译替换每个标记名(这必须在解析器启动时完成)。野牛骨架宣称
yytname
const
,这带来了烦恼;但是,可以使用非常简单的
sed
awk
调用来删除有问题的
const
。[注3]

话虽如此,我并不清楚这些自动生成的错误消息是否“用户友好”,除非用户对该语言的形式语法非常熟悉。熟悉语法的用户可能更喜欢原始标记名,以便在语法中找到它,而不是只与原始概念巧合地相似的非专家翻译。并不是说我在指责任何人

你可能会喜欢Russ Cox的这篇文章,关于他如何为Go实现友好的错误消息


注释


  • 在C字符串中直接使用令牌名称对于其表示形式包括
    \
    的令牌不起作用。尤其是任何关键字令牌(
    ”和“
    ”)感谢您的回答。遗憾的是,
    %token table
    不够,因为我无法自动提取字符串进行翻译。我已经有了使用不同人类可读标记名的不同人类语言的人类可读语法描述。我想使用这些人类可读标记名输出错误消息。Ad另外,每个保留字至少有两种风格:英语和德语。到目前为止,这是由扫描器管理的。因此,我不能无条件地只使用一种语言的关键字。@keinstein:如果关键字只出现在扫描器中,你将无法在yytname中找到它们,而据我所知,yytnamerr的唯一目的就是生成名称在yytname中,最终用户可以看到它们,因此翻译它们就可以了(当然,您必须保留并跟踪API)关键词是可呈现的,除了目前尚未翻译的问题。还有一个问题:gettext不知道
    yytname
    中的关键词。因此它无法提取它们。我如何让它们自动提取?@Keinstein:好的,答案已编辑。你可能知道,关于翻译的传统智慧(和gettext手册)不鼓励将单独翻译的单词插入到翻译的消息中,因为它不适用于所有语言。但对于您所关心的语言来说,这可能是好的。通常我对翻译的假设很少。在这一点上,我同意您的看法。但是,将bison生成的错误消息视为一个错误是错误的它们提供了其他地方无法找到的案例,因为我既没有发明语言,也没有编写语法文件。我没有时间修改bison语法(这是必要的,但其他问题的优先级更高)。因此,语言一致性错误消息是一个巨大的改进。即使翻译必须绕过人类语法。您的答案是可以接受的(定义
    yytnamerr
    )。我等了几天才找到不同的方法。
    messages.pot: my_parser.c
        $(CC) $(CFLAGS) -DMAKE_TOKEN -o token_lister $<
        ./token_lister > my_parser.tokens.c
        # See Note 2 below
        $(CC) -o my_parser.tokens my_parser.tokens.c
        xgettext -o $@ my_parser.tokens.c