1 use crate::snippet::Style;
2 use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
3 use rustc_data_structures::sync::Lrc;
4 use rustc_error_messages::FluentArgs;
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`
11 fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>;
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;
18 /// Convert diagnostic arguments (a rustc internal type that exists to implement
19 /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
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.to_vec().drain(..))
27 /// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
28 fn translate_messages(
30 messages: &[(DiagnosticMessage, Style)],
31 args: &FluentArgs<'_>,
34 messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
38 /// Convert a `DiagnosticMessage` to a string, performing translation if necessary.
39 fn translate_message<'a>(
41 message: &'a DiagnosticMessage,
42 args: &'a FluentArgs<'_>,
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),
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()?,
56 debug!(?message, ?value);
58 let mut errs = vec![];
59 let translated = bundle.format_pattern(value, Some(&args), &mut errs);
60 debug!(?translated, ?errs);
61 Some((translated, errs))
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.
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).
73 // In debug builds, assert so that compiler devs can spot the broken translation and
75 .inspect(|(_, errs)| {
78 "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
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.
93 "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
101 .expect("failed to find message in primary or fallback fluent bundles")