Rust 诊断工作组正在主导一项工作,旨在为编译器中的错误消息添加国际化支持,使编译器能够输出非英语语言的信息。
例如,考虑以下诊断信息,其中用户使用冒号而非箭头来指定函数的返回类型
error: return types are denoted using `->`
--> src/main.rs:1:21
|
1 | fn meaning_of_life(): u32 { 42 }
| ^ help: use `->` instead
我们可以用中文输出该诊断信息
错误: 返回类型使用`->`表示
--> src/main.rs:1:21
|
1 | fn meaning_of_life(): u32 { 42 }
| ^ 帮助: 使用`->`来代替
甚至用西班牙语
error: el tipo de retorno se debe indicar mediante `->`
--> src/main.rs:1:21
|
1 | fn meaning_of_life(): u32 { 42 }
| ^ ayuda: utilice `->` en su lugar
翻译后的错误消息将允许非英语母语用户以他们偏好的语言使用 Rust。
当前状态如何?
诊断信息翻译的实现已经开始,但我们正在寻求帮助!
诊断信息翻译的核心基础设施已在 rustc
中实现;这使得 Rust 能够发出包含翻译后消息的诊断信息。然而,rustc
中的每个诊断信息都必须移植到使用这个新的基础设施,否则它们就无法被翻译。这是大量工作,因此诊断工作组决定将翻译工作与向“诊断结构体”(后续会详细介绍)的过渡结合起来,并同时完成这两项工作。
一旦大多数诊断信息消息被移植到新的基础设施,诊断工作组就会开始创建工作流程,供翻译团队将所有诊断信息消息翻译成不同的语言。
每一个与诊断信息翻译相关的 Pull Request 都被打上了标签 A-translation
。
如何参与
诊断信息翻译工作量很大,但好消息是很多工作可以并行完成,而且不需要有编译器开发背景或熟悉 rustc
就可以做出贡献!
如果你有兴趣参与,请查看 #100717 了解如何开始! 你可以在 #t-compiler/wg-diagnostics
中寻求帮助或联系 @davidtwco
。
注意:随着工作组迭代和改进诊断信息翻译的工作流程,这篇文章不会更新,因此请始终参考开发者指南获取最新的 诊断结构体 或 诊断信息翻译 文档。
1. 设置本地开发环境
在帮助诊断信息翻译工作之前,你需要设置好你的开发环境,因此请按照 rustc
开发者指南上的说明进行操作。
2. 准备移植你的第一个诊断信息
rustc
中几乎所有的诊断信息都使用传统的 DiagnosticBuilder
API 实现,其形式如下
self.struct_span_err
.span_suggestion_short
.emit;
struct_span_err
创建一个新的诊断信息,需要两个东西 - 一个 Span
和一条消息。struct_span_err
并不是你在编译器源代码中遇到的唯一诊断函数,但其他函数都非常相似。你可以在 rustc
开发者指南中 阅读更多关于 rustc
诊断基础设施的信息。
Span
仅用于标识用户源代码中的某个位置,你可以在整个编译器中找到它们用于报告诊断信息(例如,前面例子中 main.rs:1:21
的位置就是 self.prev_token.span
)。
在这个例子中,消息只是一个字符串字面量(一个 &'static str
),它需要被一个标识符替换,以表示在请求的任何语言中对应的同一条消息。
将诊断信息移植到新的基础设施有两种方法
- 如果是简单的诊断信息,没有任何决定是否添加建议、注意、帮助或标签的逻辑,就像上面的例子一样,那么...
- 否则...
在这两种情况下,诊断信息都表示为类型。使用类型表示诊断信息是诊断工作组的一个目标,因为这有助于将诊断逻辑与主要代码路径分离。
每种诊断类型都应该实现 SessionDiagnostic
(手动或自动)。在 SessionDiagnostic
trait 中,有一个成员函数将该 trait 转换为要发出的 Diagnostic
。
使用诊断派生宏...
诊断派生宏(可以是用于整个诊断信息的 SessionDiagnostic
,用于诊断信息部分的 SessionSubdiagnostic
,或用于 lint 的 DecorateLint
)可用于自动实现诊断 trait。
首先,在当前 crate 的 errors
模块中创建一个以你的诊断信息命名的新类型(例如 rustc_typeck::errors
或 rustc_borrowck::errors
)。在我们的例子中,可能看起来像
接下来,我们需要添加包含所有需要信息的字段 - 对我们来说,这只是一个 Span
。
在大多数情况下,这将只是原始诊断信息发出逻辑使用的 Span
以及插入到诊断信息消息中的值。
之后,我们应该添加派生宏,添加我们的错误属性,并标注主 Span
(即传递给 struct_span_err
的那个)。
每个诊断信息都应该有一个唯一的 slug。按照惯例,它们总是以错误所属的 crate 名称开头(本例中为 parser
)。这个 slug 将被用来在我们的翻译资源中查找实际的诊断信息消息,我们很快就会看到。
最后,我们需要添加任何标签、注意、帮助或建议
在这个例子中,只有一个建议 - 将 :
替换为 ->
。
在完成之前,我们必须将诊断信息消息添加到翻译资源中..
有关诊断派生宏的更多文档,请参阅rustc
开发者指南中的诊断结构体章节。
SessionDiagnostic
...
手动实现 有些诊断信息过于复杂,无法使用诊断派生宏从诊断类型生成。在这些情况下,可以手动实现 SessionDiagnostic
。
使用与“使用诊断派生宏...”中相同的类型,我们可以如下实现 SessionDiagnostic
use ;
不再像原始的诊断信息发出逻辑那样使用字符串作为消息,而是使用指向翻译资源的类型化标识符。现在我们只需将诊断信息消息添加到翻译资源中..。
示例
有关已移植到使用诊断派生宏或手动编写的诊断信息的更多示例,请参阅以下 Pull Request
更多示例,请参阅打上标签 A-translation
的 Pull Request。
添加翻译资源...
诊断派生宏中的每个 slug 或手动实现中的类型化标识符都需要对应翻译资源中的一条消息。
rustc
的翻译使用Fluent,这是一个非对称翻译系统。对于编译器中每个发出诊断信息的 crate,在 compiler/rustc_error_messages/locales/en-US/$crate.ftl
都有一个相应的 Fluent 资源。
错误消息需要添加到这个资源中(一个宏将随后生成对应于该消息的类型化标识符)。
对于我们的例子,我们应该将以下 Fluent 内容添加到 compiler/rustc_error_messages/locales/en-US/parser.ftl
parser_return_type_arrow = return types are denoted using `->`
.suggestion = use `->` instead
parser_return_type_arrow
将生成一个 parser::return_type_arrow
类型(在 rustc_errors::fluent
中),可以与诊断结构体和诊断构建器一起使用。
子诊断信息是主要 Fluent 消息的“属性” - 按照惯例,属性的名称就是子诊断信息的类型,例如“suggestion”,但当同一种子诊断信息有多个时,这可以改变。
现在 Fluent 资源包含该消息,我们的诊断信息就移植好了!更复杂的消息,带有插值,将能够引用诊断类型中的其他字段(手动实现时,这些作为参数提供)。有关更多示例,请参阅rustc
开发者指南中的诊断信息翻译文档。
3. 移植诊断信息
现在你已经大致了解要做什么了,你需要找到一些要移植的诊断信息。有很多诊断信息需要移植,所以诊断工作组已经将工作分派开来,以避免有人处理与他人相同的诊断信息 - 但目前参与的人不多,所以随便选一个 crate 开始移植吧 :)
请将 A-translation
标签添加到你提交的任何 pull request 中,这样我们可以追踪谁做出了贡献!你可以使用 rustbot
给你的 PR 打标签(如果 triagebot
没有自动给你打标签的话)
@rustbot label +A-translation
你还可以通过发布以下内容的评论(或将其包含在 PR 描述中)来指定诊断工作组的一名成员来评审你的 PR
r? rust-lang/diagnostics
即使你不确定具体如何进行,也可以尝试一下,并且可以在 #t-compiler/wg-diagnostics
中寻求帮助或联系 @davidtwco
。查看 #100717 获取关于如何开始的指导!
常见问题
有人需要这个功能吗?
是的!一些语言社区偏好使用本地化资源,而另一些则不偏好(并且偏好在这些社区内部也会有所不同)。例如,中文社区拥有成熟的编程语言资源生态系统,这些资源不需要了解英语。
翻译 X 不会更有价值吗?
Rust 项目中有许多不同的领域,国际化会有益处。诊断信息并没有比项目的其他任何部分更优先,只是编译器团队对支持这个功能有兴趣。
编译器开发者的时间花在其他地方不是更好吗?
编译器实现不是零和博弈:在编译器其他部分的工作不受这些努力的影响,并且进行诊断信息翻译工作不会阻止贡献者进行其他工作。
翻译是选择加入的吗?
翻译将是选择加入的,如果你不想使用它们,则无需使用。
用户如何选择语言?
用户将如何选择使用翻译后的错误消息尚未决定。