]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_errors/src/translation.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / compiler / rustc_errors / src / translation.rs
CommitLineData
9ffffee4 1use crate::error::{TranslateError, TranslateErrorKind};
f2b60f7d
FG
2use crate::snippet::Style;
3use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
4use rustc_data_structures::sync::Lrc;
9c376795 5use rustc_error_messages::FluentArgs;
f2b60f7d 6use std::borrow::Cow;
9ffffee4 7use std::env;
9c376795 8use std::error::Report;
f2b60f7d 9
2b03887a
FG
10/// Convert diagnostic arguments (a rustc internal type that exists to implement
11/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
12///
13/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
14/// passed around as a reference thereafter.
15pub fn to_fluent_args<'iter, 'arg: 'iter>(
16 iter: impl Iterator<Item = DiagnosticArg<'iter, 'arg>>,
17) -> FluentArgs<'arg> {
18 let mut args = if let Some(size) = iter.size_hint().1 {
19 FluentArgs::with_capacity(size)
20 } else {
21 FluentArgs::new()
22 };
23
24 for (k, v) in iter {
25 args.set(k.clone(), v.clone());
26 }
27
28 args
29}
30
f2b60f7d
FG
31pub trait Translate {
32 /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
33 /// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
34 /// should be used.
35 fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>;
36
37 /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
38 /// Used when the user has not requested a specific language or when a localized diagnostic is
39 /// unavailable for the requested locale.
40 fn fallback_fluent_bundle(&self) -> &FluentBundle;
41
f2b60f7d
FG
42 /// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
43 fn translate_messages(
44 &self,
45 messages: &[(DiagnosticMessage, Style)],
46 args: &FluentArgs<'_>,
47 ) -> Cow<'_, str> {
48 Cow::Owned(
9c376795
FG
49 messages
50 .iter()
51 .map(|(m, _)| self.translate_message(m, args).map_err(Report::new).unwrap())
52 .collect::<String>(),
f2b60f7d
FG
53 )
54 }
55
56 /// Convert a `DiagnosticMessage` to a string, performing translation if necessary.
57 fn translate_message<'a>(
58 &'a self,
59 message: &'a DiagnosticMessage,
60 args: &'a FluentArgs<'_>,
9c376795 61 ) -> Result<Cow<'_, str>, TranslateError<'_>> {
f2b60f7d
FG
62 trace!(?message, ?args);
63 let (identifier, attr) = match message {
2b03887a 64 DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
9c376795 65 return Ok(Cow::Borrowed(msg));
2b03887a 66 }
f2b60f7d
FG
67 DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
68 };
9c376795
FG
69 let translate_with_bundle =
70 |bundle: &'a FluentBundle| -> Result<Cow<'_, str>, TranslateError<'_>> {
71 let message = bundle
72 .get_message(identifier)
73 .ok_or(TranslateError::message(identifier, args))?;
74 let value = match attr {
75 Some(attr) => message
76 .get_attribute(attr)
77 .ok_or(TranslateError::attribute(identifier, args, attr))?
78 .value(),
79 None => message.value().ok_or(TranslateError::value(identifier, args))?,
80 };
81 debug!(?message, ?value);
f2b60f7d 82
9c376795
FG
83 let mut errs = vec![];
84 let translated = bundle.format_pattern(value, Some(args), &mut errs);
85 debug!(?translated, ?errs);
86 if errs.is_empty() {
87 Ok(translated)
88 } else {
89 Err(TranslateError::fluent(identifier, args, errs))
90 }
f2b60f7d 91 };
f2b60f7d 92
9c376795
FG
93 try {
94 match self.fluent_bundle().map(|b| translate_with_bundle(b)) {
95 // The primary bundle was present and translation succeeded
96 Some(Ok(t)) => t,
487cf647 97
9c376795 98 // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely
9ffffee4
FG
99 // just that the primary bundle doesn't contain the message being translated, so
100 // proceed to the fallback bundle.
101 Some(Err(
102 primary @ TranslateError::One {
103 kind: TranslateErrorKind::MessageMissing, ..
104 },
105 )) => translate_with_bundle(self.fallback_fluent_bundle())
106 .map_err(|fallback| primary.and(fallback))?,
107
108 // Always yeet out for errors on debug (unless
109 // `RUSTC_TRANSLATION_NO_DEBUG_ASSERT` is set in the environment - this allows
110 // local runs of the test suites, of builds with debug assertions, to test the
111 // behaviour in a normal build).
112 Some(Err(primary))
113 if cfg!(debug_assertions)
114 && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() =>
115 {
116 do yeet primary
117 }
118
119 // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
120 // just hide it and try with the fallback bundle.
9c376795
FG
121 Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle())
122 .map_err(|fallback| primary.and(fallback))?,
487cf647 123
9c376795
FG
124 // The primary bundle is missing, proceed to the fallback bundle
125 None => translate_with_bundle(self.fallback_fluent_bundle())
126 .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
127 }
128 }
f2b60f7d
FG
129 }
130}