]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_errors/src/translation.rs
Auto merge of #100404 - BelovDV:linker_group, r=petrochenkov
[rust.git] / compiler / rustc_errors / src / translation.rs
1 use crate::snippet::Style;
2 use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
3 use rustc_data_structures::sync::Lrc;
4 use rustc_error_messages::FluentArgs;
5 use std::borrow::Cow;
6
7 pub trait Translate {
8     /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
9     /// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
10     /// should be used.
11     fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>;
12
13     /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
14     /// Used when the user has not requested a specific language or when a localized diagnostic is
15     /// unavailable for the requested locale.
16     fn fallback_fluent_bundle(&self) -> &FluentBundle;
17
18     /// Convert diagnostic arguments (a rustc internal type that exists to implement
19     /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
20     ///
21     /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
22     /// passed around as a reference thereafter.
23     fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
24         FromIterator::from_iter(args.iter().cloned())
25     }
26
27     /// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
28     fn translate_messages(
29         &self,
30         messages: &[(DiagnosticMessage, Style)],
31         args: &FluentArgs<'_>,
32     ) -> Cow<'_, str> {
33         Cow::Owned(
34             messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
35         )
36     }
37
38     /// Convert a `DiagnosticMessage` to a string, performing translation if necessary.
39     fn translate_message<'a>(
40         &'a self,
41         message: &'a DiagnosticMessage,
42         args: &'a FluentArgs<'_>,
43     ) -> Cow<'_, str> {
44         trace!(?message, ?args);
45         let (identifier, attr) = match message {
46             DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg),
47             DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
48         };
49
50         let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> {
51             let message = bundle.get_message(&identifier)?;
52             let value = match attr {
53                 Some(attr) => message.get_attribute(attr)?.value(),
54                 None => message.value()?,
55             };
56             debug!(?message, ?value);
57
58             let mut errs = vec![];
59             let translated = bundle.format_pattern(value, Some(&args), &mut errs);
60             debug!(?translated, ?errs);
61             Some((translated, errs))
62         };
63
64         self.fluent_bundle()
65             .and_then(|bundle| translate_with_bundle(bundle))
66             // If `translate_with_bundle` returns `None` with the primary bundle, this is likely
67             // just that the primary bundle doesn't contain the message being translated, so
68             // proceed to the fallback bundle.
69             //
70             // However, when errors are produced from translation, then that means the translation
71             // is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided).
72             //
73             // In debug builds, assert so that compiler devs can spot the broken translation and
74             // fix it..
75             .inspect(|(_, errs)| {
76                 debug_assert!(
77                     errs.is_empty(),
78                     "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
79                     identifier,
80                     attr,
81                     args,
82                     errs
83                 );
84             })
85             // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
86             // just hide it and try with the fallback bundle.
87             .filter(|(_, errs)| errs.is_empty())
88             .or_else(|| translate_with_bundle(self.fallback_fluent_bundle()))
89             .map(|(translated, errs)| {
90                 // Always bail out for errors with the fallback bundle.
91                 assert!(
92                     errs.is_empty(),
93                     "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
94                     identifier,
95                     attr,
96                     args,
97                     errs
98                 );
99                 translated
100             })
101             .expect("failed to find message in primary or fallback fluent bundles")
102     }
103 }