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
中的每个诊断都必须移植到使用这个新基础设施,否则它们无法被翻译。这是一项大量的工作,因此诊断工作组选择将翻译工作与过渡到“诊断结构体”(稍后会详细介绍)相结合,并同时完成这两项工作。
一旦大多数诊断消息已移植到新的基础设施,诊断工作组将开始创建一个工作流程,供翻译团队将所有诊断消息翻译成不同的语言。
每个与诊断翻译相关的拉取请求都使用 A-translation
标签进行标记。
参与其中
诊断翻译方面有很多工作要做,但好消息是,许多工作可以并行完成,并且不需要编译器开发的背景或熟悉 rustc
即可做出贡献!
如果您有兴趣参与其中,请查看 #100717 以了解从哪里开始! 您可以在 #t-compiler/wg-diagnostics
中寻求帮助,或联系 @davidtwco
。
注意:本帖子不会随着工作组迭代并改进诊断翻译的工作流程而更新,因此请始终查阅开发者指南,了解有关 诊断结构体 或 诊断翻译 的最新文档。
1. 设置本地开发环境
在帮助进行诊断翻译工作之前,您需要设置好您的开发环境,因此 请按照 rustc
开发者指南上的说明进行操作。
2. 准备移植您的第一个诊断
rustc
中的几乎所有诊断都使用传统的 DiagnosticBuilder
API 实现,如下所示
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
开发者指南中阅读更多关于 rustc
的诊断基础设施的信息。
Span
只是标识用户源代码中的某个位置,您可以在整个编译器中找到它们用于诊断报告(例如,来自前面示例的位置 main.rs:1:21
将是 self.prev_token.span
)。
在本例中,消息只是一个字符串字面量 (&'static str
),需要替换为所请求的任何语言中相同消息的标识符。
诊断将有两种方式移植到新的基础设施
- 如果这是一个简单的诊断,没有任何逻辑来决定是否添加建议或注释或帮助或标签,就像上面的示例一样,那么...
- 否则...
在这两种情况下,诊断都表示为类型。使用类型表示诊断是诊断工作组的目标,因为它有助于将诊断逻辑与主要代码路径分开。
每个诊断类型都应该实现 SessionDiagnostic
(手动或自动)。在 SessionDiagnostic
特性中,有一个成员函数将特性转换为要发出的 Diagnostic
。
使用诊断派生...
诊断派生(整个诊断的 SessionDiagnostic
、诊断部分的 SessionSubdiagnostic
或 lint 的 DecorateLint
)可用于自动实现诊断特性。
首先,在当前 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。按照惯例,这些总是以与错误相关的 crate 开头(本例中为 parser
)。这个 slug 将用于在我们的翻译资源中查找实际的诊断消息,我们稍后会看到。
最后,我们需要添加任何标签、注释、帮助或建议
#[derive(SessionDiagnostic)]
#[error(parser_return_type_arrow)]
struct ReturnTypeArrow {
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = "->")]
span: Span,
}
在此示例中,只有一个建议 - 将 :
替换为 ->
。
在完成之前,我们必须将诊断消息添加到翻译资源中..
有关诊断派生的更多文档,请参阅rustc
开发者指南的诊断结构体章节。
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::suggestion,
"->".to_string(),
Applicability::MachineApplicable,
)
}
}
代替像原始诊断发射逻辑中那样使用字符串作为消息,使用了指向翻译资源的类型化标识符。现在我们只需要将诊断消息添加到翻译资源中..。
示例
有关移植为使用诊断派生或手动编写的诊断的更多示例,请参阅以下拉取请求
有关更多示例,请参阅标记为 A-translation
的拉取请求。
添加翻译资源...
诊断派生中的每个 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
标签添加到您所做的任何拉取请求中,以便我们可以跟踪谁做出了贡献!您可以使用 rustbot
来标记您的 PR(如果它不是由 triagebot
自动标记的)
@rustbot label +A-translation
您还可以通过发布包含以下内容的评论(或将其包含在 PR 描述中)来分配诊断工作组的成员来审查您的 PR
r? rust-lang/diagnostics
即使您不确定如何进行,也可以试一试,您可以在 #t-compiler/wg-diagnostics
中寻求帮助,或联系 @davidtwco
。请查看 #100717 以获得有关从哪里开始的指导!
常见问题解答
这是一个有人需要的功能吗?
是的!一些语言社区更喜欢本地资源,而另一些社区则不喜欢(并且在这些社区内,偏好也会有所不同)。例如,讲中文的社区拥有成熟的编程语言资源生态系统,不需要了解任何英语。
翻译 X 不是更有价值吗?
Rust 项目中有很多不同的领域,国际化将是有益的。诊断并没有比项目中的任何其他部分优先,只是编译器团队有兴趣支持此功能。
难道不应该将编译器开发人员的时间花在其他地方吗?
编译器实现并非零和博弈:在编译器其他部分的工作不会受到这些努力的影响,并且进行诊断翻译不会阻止贡献者从事其他任何工作。
翻译会是可选的吗?
翻译将是可选的,如果你不想使用它们,你就不需要使用。
用户将如何选择语言?
用户将如何选择使用翻译后的错误消息尚未决定。