贡献诊断翻译
翻译的诊断
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
中的每个诊断都必须移植才能使用这个新的基础设施,否则它们无法被翻译。
这是一项大量的工作,因此诊断工作组选择将翻译工作与向“诊断结构”(稍后将详细介绍)的过渡结合起来,并同时完成这两项工作。
一旦大多数诊断消息被移植到新的基础设施,诊断工作组就开始为翻译团队创建工作流程,来把所有诊断消息翻译成不同的语言。
此外,这份文档列出了与诊断翻译相关的每个 PR。
请参与进来
在诊断翻译方面有很多工作要做,但好消息是,许多工作可以并行完成,而且它不需要编译器开发背景或熟悉 rustc
就能做出贡献!
如果你感兴趣,尽管开始吧!你可以在 zulipchat 的 #t-compiler/wg-diagnostics
中寻求帮助,或者联系 @davidtwco
。
注意:本文章不会随工作组对诊断翻译工作流程的迭代和改进而更新,因此请始终参考开发人员指南的 《diagnostic structs》或《diagnostic translation》。
(一) 设置本地开发环境
在协助诊断翻译工作之前,你需要设置你的开发环境,因此请遵循 rustc
开发指南 上的说明。
(二) 准备移植诊断
rustc
中几乎所有的诊断都是使用传统的 DiagnosticBuilder
接口实现的,如下所示:
self.struct_span_err(self.prev_token.span, "return types are denoted using `->`")
.span_suggestion_short(
self.prev_token.span,
"use `->` instead",
"->".to_string(),
Applicability::MachineApplicable,
)
.emit();
struct_span_err
在给定两件事的情况下创建一个新的诊断:一个 Span
和一条消息。
struct_span_err
不会是你在编译器的源代码中遇到的唯一诊断函数,但其他函数都非常相似。
你可以在 rustc
开发指南 中阅读有关诊断基础设施的更多信息。
Span
只是识别用户源代码中的某个位置,整个编译器中都使用 Span
来报告诊断:例如,前面示例中
self.prev_token.span
应该是位置 main.rs:1:21
。
在这个例子中,消息只是一个字符串字面值 (&'static str
),需要用同一消息的标识符来替换,不管请求的是哪种语言。
有两种方式可以将诊断程序移植到新的基础设施:
在这两种情况下,诊断都表示为类型。使用类型表示诊断是诊断工作组的一个目标,因为它有助于将诊断逻辑与主代码路径分开。
每种诊断类型都应该实现 SessionDiagnostic
(无论手动或自动)。在
SessionDiagnostic
trait 中,有一个成员函数,它把此 trait 转换为要发出的 Diagnostic
。
使用诊断派生宏
诊断派生宏有:
- 用于整体诊断的
SessionDiagnostic
- 用于部分诊断的
SessionSubdiagnostic
- 用于 lints 的
DecorateLint
使用它们来自动实现诊断 trait。
首先,在以你的诊断命名的当前 crate 的 errors
模块中创建一个新类型,如
rustc_typeck::errors
或 rustc_borrowck::errors
。这可能类似于:
struct ReturnTypeArrow {
}
接下来,添加包含我们需要的所有信息的字段,比如一个简单 Span
:
struct ReturnTypeArrow {
span: Span,
}
大多数情况下,字段是用来发出原始诊断逻辑的 Span
和插入到诊断消息中的值。
然后,添加派生宏和错误属性,并注释主要的 Span
(它被传给 struct_span_err
)。
#[derive(SessionDiagnostic)]
#[error(parser::return_type_arrow)]
struct ReturnTypeArrow {
#[primary_span]
span: Span,
}
每个诊断都应该有一个唯一的路径 (slug,本例中为 parser::return_type_arrow
)。
习惯上,总是以与错误相关的 crate 开头(本例中为 parser
)。
这个路径将用于在翻译资源中查找实际的诊断消息,很快就会介绍到。
最后,添加任何标签 (labels)、注意 (notes)、帮助 (helps) 或建议 (suggestions):
#[derive(SessionDiagnostic)]
#[error(parser::return_type_arrow)]
struct ReturnTypeArrow {
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = "->")]
span: Span,
}
在本例中,只有一个建议:将 :
替换为 ->
。
在完成之前,还必须将诊断消息 添加到翻译资源 中。
有关诊断派生宏的更多文档,请参考 rustc
开发指南的 diagnostic structs 一章。
手动实现 SessionDiagnostic
有些诊断太复杂,无法使用派生宏从诊断类型生成诊断。此时,可以手动实现 SessionDiagnostic
。
使用与诊断派生宏相同的类型,像下面那样来手动实现 SessionDiagnostic
:
use rustc_errors::{fluent, SessionDiagnostic};
struct ReturnTypeArrow { span: Span }
impl SessionDiagnostic for ReturnTypeArrow {
fn into_diagnostic(self, sess: &'_ rustc_session::Session) -> DiagnosticBuilder<'_> {
sess.struct_span_err(
self.span,
fluent::parser::return_type_arrow,
)
.span_suggestion_short(
self.span,
fluent::parser::suggestion,
"->".to_string(),
Applicability::MachineApplicable,
)
}
}
不是像在原始诊断发出逻辑中那样对消息使用字符串,而是使用引用翻译资源的类型化标识符。
现在我们只需将诊断消息添加到翻译资源。
PR 案例
针对移植到使用诊断派生宏或手动编写的诊断的更多示例,请参考以下 PR:
- https://github.com/rust-lang/rust/pull/98353
- https://github.com/rust-lang/rust/pull/98415
- https://github.com/rust-lang/rust/pull/97093
- https://github.com/rust-lang/rust/pull/99213
更多示例,请参考标记为 A-Translation
的 PR。
添加翻译资源
在诊断派生宏或手动实现的类型化标识符中,其 slug 都需要与翻译资源中的消息相对应。
rustc
使用的是 Fluent,这是一种非对称翻译系统。
编译器中发出诊断的每个 crate 都有一个对应的 Fluent 资源 compiler/rustc_error_messages/locales/en-US/$crate.ftl
。
需要将错误消息添加到此资源,然后宏会生成对应于该消息的类型化标识符。
对于上述示例,我们应该向 compiler/rustc_error_messages/locales/en-US/parser.ftl
添加以下 Fluent 内容:
parser_return_type_arrow = return types are denoted using `->`
.suggestion = use `->` instead
parser_return_type_arrow
将(在 rustc_error::fluent
中)生成 parser::return_type_arrow
类型,用来与诊断结构体
(diagnostic struct) 和诊断构建器 (diagnostic builder) 一起使用。
子诊断 (subdiagnostics) 主要是 Fluent 消息的“属性”,习惯上,属性的名称是子诊断的类型,例如
suggestion
,但如果一种子诊断存在多个时,属性的名称可以更改。
现在,Fluent 资源包含了该消息,我们的诊断程序被移植了!
使用插值的更复杂的消息能够引用诊断类型中的其他字段,当手动实现时,这些字段作为参数提供。
有关更多示例,请参考 rustc
开发指南中的 diagnostic translation 文档。
(三) 移植诊断
你已经大致知道要做什么了,现在你需要找到一些诊断程序来移植。
有很多诊断需要移植,所以诊断工作组已经将工作分开,以避免任何人与其他人从事相同的诊断工作。
但现在,参与的人并不多,所以只需选择一个 crate 并开始移植 :)
请在你创建的任何 PR 中添加 A-translation
标签,这样我们就可以跟踪谁做出了贡献!
如果 PR 不是 triagebot
自动标记的话,你可以使用 rustbot
来标记 PR:
@rustbot label +A-translation
你还可以指定诊断工作组的成员来审查你的 PR,做法是发布包含以下内容的评论(或将其包括在 PR 描述中):
r? rust-lang/diagnostics
即使你不确定如何继续,也尝试一下,你可以在 #t-compiler/wg-diagnostics
寻求帮助,或者联系 @davidtwco
。
FAQ
有人想要诊断翻译功能吗?
是!有些语言社区喜欢本地资源,有些则不喜欢(这些社区的偏好也会有所不同)。
例如,中文社区拥有成熟的编程语言资源生态系统,不需要懂任何英语。1
译者注:嗯,说法有点绝对了 :)
翻译 xx 不是更有价值吗?
在 Rust 项目中有许多不同的领域,国际化将是有益的。
诊断并没有优先于项目的任何其他部分,只是编译器团队对支持这一功能感兴趣。
把编译器开发人员的时间花在其他地方不是更好吗?
编译器实现不是零和游戏:编译器其他部分的工作不会受到这些工作的影响,从事诊断翻译的工作也不会阻止贡献者从事其他工作。
翻译会是可选的吗?
是可选的。如果你不想看翻译,你就不需要使用它们。
使用者将如何选择语言?
使用者将如何选择使用翻译后的错误消息尚未确定。