rustc_errors/
translation.rs1use std::borrow::Cow;
2use std::env;
3use std::error::Report;
4use std::sync::Arc;
5
6pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle};
7use tracing::{debug, trace};
8
9use crate::error::{TranslateError, TranslateErrorKind};
10use crate::snippet::Style;
11use crate::{DiagArg, DiagMessage, FluentBundle};
12
13pub fn to_fluent_args<'iter>(iter: impl Iterator<Item = DiagArg<'iter>>) -> FluentArgs<'static> {
19 let mut args = if let Some(size) = iter.size_hint().1 {
20 FluentArgs::with_capacity(size)
21 } else {
22 FluentArgs::new()
23 };
24
25 for (k, v) in iter {
26 args.set(k.clone(), v.clone());
27 }
28
29 args
30}
31
32#[derive(Clone)]
33pub struct Translator {
34 pub fluent_bundle: Option<Arc<FluentBundle>>,
37 pub fallback_fluent_bundle: LazyFallbackBundle,
41}
42
43impl Translator {
44 pub fn with_fallback_bundle(
45 resources: Vec<&'static str>,
46 with_directionality_markers: bool,
47 ) -> Translator {
48 Translator {
49 fluent_bundle: None,
50 fallback_fluent_bundle: crate::fallback_fluent_bundle(
51 resources,
52 with_directionality_markers,
53 ),
54 }
55 }
56
57 pub fn translate_messages(
59 &self,
60 messages: &[(DiagMessage, Style)],
61 args: &FluentArgs<'_>,
62 ) -> Cow<'_, str> {
63 Cow::Owned(
64 messages
65 .iter()
66 .map(|(m, _)| self.translate_message(m, args).map_err(Report::new).unwrap())
67 .collect::<String>(),
68 )
69 }
70
71 pub fn translate_message<'a>(
73 &'a self,
74 message: &'a DiagMessage,
75 args: &'a FluentArgs<'_>,
76 ) -> Result<Cow<'a, str>, TranslateError<'a>> {
77 trace!(?message, ?args);
78 let (identifier, attr) = match message {
79 DiagMessage::Str(msg) | DiagMessage::Translated(msg) => {
80 return Ok(Cow::Borrowed(msg));
81 }
82 DiagMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
83 };
84 let translate_with_bundle =
85 |bundle: &'a FluentBundle| -> Result<Cow<'_, str>, TranslateError<'_>> {
86 let message = bundle
87 .get_message(identifier)
88 .ok_or(TranslateError::message(identifier, args))?;
89 let value = match attr {
90 Some(attr) => message
91 .get_attribute(attr)
92 .ok_or(TranslateError::attribute(identifier, args, attr))?
93 .value(),
94 None => message.value().ok_or(TranslateError::value(identifier, args))?,
95 };
96 debug!(?message, ?value);
97
98 let mut errs = vec![];
99 let translated = bundle.format_pattern(value, Some(args), &mut errs);
100 debug!(?translated, ?errs);
101 if errs.is_empty() {
102 Ok(translated)
103 } else {
104 Err(TranslateError::fluent(identifier, args, errs))
105 }
106 };
107
108 try {
109 match self.fluent_bundle.as_ref().map(|b| translate_with_bundle(b)) {
110 Some(Ok(t)) => t,
112
113 Some(Err(
117 primary @ TranslateError::One {
118 kind: TranslateErrorKind::MessageMissing, ..
119 },
120 )) => translate_with_bundle(&self.fallback_fluent_bundle)
121 .map_err(|fallback| primary.and(fallback))?,
122
123 Some(Err(primary))
128 if cfg!(debug_assertions)
129 && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() =>
130 {
131 do yeet primary
132 }
133
134 Some(Err(primary)) => translate_with_bundle(&self.fallback_fluent_bundle)
137 .map_err(|fallback| primary.and(fallback))?,
138
139 None => translate_with_bundle(&self.fallback_fluent_bundle)
141 .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
142 }
143 }
144 }
145}