1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use crate::snippet::Style;
use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
use rustc_data_structures::sync::Lrc;
use rustc_error_messages::{
fluent_bundle::resolver::errors::{ReferenceKind, ResolverError},
FluentArgs, FluentError,
};
use std::borrow::Cow;
pub fn to_fluent_args<'iter, 'arg: 'iter>(
iter: impl Iterator<Item = DiagnosticArg<'iter, 'arg>>,
) -> FluentArgs<'arg> {
let mut args = if let Some(size) = iter.size_hint().1 {
FluentArgs::with_capacity(size)
} else {
FluentArgs::new()
};
for (k, v) in iter {
args.set(k.clone(), v.clone());
}
args
}
pub trait Translate {
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>;
fn fallback_fluent_bundle(&self) -> &FluentBundle;
fn translate_messages(
&self,
messages: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
) -> Cow<'_, str> {
Cow::Owned(
messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
)
}
fn translate_message<'a>(
&'a self,
message: &'a DiagnosticMessage,
args: &'a FluentArgs<'_>,
) -> Cow<'_, str> {
trace!(?message, ?args);
let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
return Cow::Borrowed(msg);
}
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
};
let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> {
let message = bundle.get_message(identifier)?;
let value = match attr {
Some(attr) => message.get_attribute(attr)?.value(),
None => message.value()?,
};
debug!(?message, ?value);
let mut errs = vec![];
let translated = bundle.format_pattern(value, Some(args), &mut errs);
debug!(?translated, ?errs);
Some((translated, errs))
};
self.fluent_bundle()
.and_then(|bundle| translate_with_bundle(bundle))
.inspect(|(_, errs)| {
debug_assert!(
errs.is_empty(),
"identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
identifier,
attr,
args,
errs
);
})
.filter(|(_, errs)| errs.is_empty())
.or_else(|| translate_with_bundle(self.fallback_fluent_bundle()))
.map(|(translated, errs)| {
let mut help_messages = vec![];
if !errs.is_empty() {
for error in &errs {
match error {
FluentError::ResolverError(ResolverError::Reference(
ReferenceKind::Message { id, .. },
)) if args.iter().any(|(arg_id, _)| arg_id == id) => {
help_messages.push(format!("Argument `{id}` exists but was not referenced correctly. Try using `{{${id}}}` instead"));
}
_ => {}
}
}
panic!(
"Encountered errors while formatting message for `{identifier}`\n\
help: {}\n\
attr: `{attr:?}`\n\
args: `{args:?}`\n\
errors: `{errs:?}`",
help_messages.join("\nhelp: ")
);
}
translated
})
.expect("failed to find message in primary or fallback fluent bundles")
}
}