|
1 | 1 | use crate::error::{TranslateError, TranslateErrorKind};
|
2 | 2 | use crate::snippet::Style;
|
3 |
| -use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle}; |
| 3 | +use crate::{DiagnosticArg, DiagnosticMessage}; |
4 | 4 | use rustc_data_structures::sync::Lrc;
|
| 5 | +use rustc_error_messages::fluent_bundle::FluentResource; |
5 | 6 | pub use rustc_error_messages::FluentArgs;
|
| 7 | +use rustc_error_messages::{langid, new_bundle, FluentBundle}; |
6 | 8 | use std::borrow::Cow;
|
7 | 9 | use std::env;
|
8 | 10 | use std::error::Report;
|
9 | 11 |
|
| 12 | +#[cfg(not(parallel_compiler))] |
| 13 | +use std::cell::LazyCell as Lazy; |
| 14 | +#[cfg(parallel_compiler)] |
| 15 | +use std::sync::LazyLock as Lazy; |
| 16 | + |
10 | 17 | /// Convert diagnostic arguments (a rustc internal type that exists to implement
|
11 | 18 | /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
|
12 | 19 | ///
|
@@ -59,72 +66,101 @@ pub trait Translate {
|
59 | 66 | message: &'a DiagnosticMessage,
|
60 | 67 | args: &'a FluentArgs<'_>,
|
61 | 68 | ) -> Result<Cow<'_, str>, TranslateError<'_>> {
|
62 |
| - trace!(?message, ?args); |
| 69 | + let fallback = |translator: &'a Self| translator.fallback_fluent_bundle(); |
63 | 70 | let (identifier, attr) = match message {
|
64 | 71 | DiagnosticMessage::Str(msg) | DiagnosticMessage::Translated(msg) => {
|
65 | 72 | return Ok(Cow::Borrowed(msg));
|
66 | 73 | }
|
67 | 74 | DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
|
68 | 75 | };
|
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); |
82 |
| - |
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 |
| - } |
| 76 | + translate_message(self, fallback, identifier, attr.as_ref(), args) |
| 77 | + } |
| 78 | + |
| 79 | + /// Translate a raw Fluent string. |
| 80 | + fn raw_translate_message<'t: 'msg, 'msg>( |
| 81 | + &'t self, |
| 82 | + slug: &'msg str, |
| 83 | + raw: &'msg str, |
| 84 | + args: &'msg FluentArgs<'_>, |
| 85 | + ) -> String { |
| 86 | + let fallback = Lazy::new(move || { |
| 87 | + let res = FluentResource::try_new(format!("{slug} = {raw}")) |
| 88 | + .expect("failed to parse fallback fluent resource"); |
| 89 | + let mut bundle = new_bundle(vec![langid!("en-US")]); |
| 90 | + // FIXME(davidtwco): get this value from the option |
| 91 | + bundle.set_use_isolating(false); |
| 92 | + bundle.add_resource(res).expect("adding resource"); |
| 93 | + bundle |
| 94 | + }); |
| 95 | + let fallback = |_| &*fallback; |
| 96 | + let slug = Cow::Borrowed(slug); |
| 97 | + let translated = translate_message(self, fallback, &slug, None, args); |
| 98 | + translated.map_err(Report::new).unwrap().to_string() |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +fn translate_message<'t: 'bundle, 'bundle: 'msg, 'msg, T: Translate + ?Sized>( |
| 103 | + translator: &'t T, |
| 104 | + fallback: impl Fn(&'t T) -> &'bundle FluentBundle, |
| 105 | + identifier: &'msg Cow<'msg, str>, |
| 106 | + attr: Option<&'msg Cow<'msg, str>>, |
| 107 | + args: &'msg FluentArgs<'msg>, |
| 108 | +) -> Result<Cow<'msg, str>, TranslateError<'msg>> { |
| 109 | + let translate_with_bundle = |
| 110 | + |bundle: &'bundle FluentBundle| -> Result<Cow<'msg, str>, TranslateError<'msg>> { |
| 111 | + let message = |
| 112 | + bundle.get_message(identifier).ok_or(TranslateError::message(identifier, args))?; |
| 113 | + let value = match attr { |
| 114 | + Some(attr) => message |
| 115 | + .get_attribute(attr) |
| 116 | + .ok_or(TranslateError::attribute(identifier, args, attr))? |
| 117 | + .value(), |
| 118 | + None => message.value().ok_or(TranslateError::value(identifier, args))?, |
91 | 119 | };
|
| 120 | + debug!(?message, ?value); |
92 | 121 |
|
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, |
97 |
| - |
98 |
| - // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely |
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. |
121 |
| - Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle()) |
122 |
| - .map_err(|fallback| primary.and(fallback))?, |
123 |
| - |
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))?, |
| 122 | + let mut errs = vec![]; |
| 123 | + let translated = bundle.format_pattern(value, Some(args), &mut errs); |
| 124 | + debug!(?translated, ?errs); |
| 125 | + if errs.is_empty() { |
| 126 | + Ok(translated) |
| 127 | + } else { |
| 128 | + Err(TranslateError::fluent(identifier, args, errs)) |
127 | 129 | }
|
| 130 | + }; |
| 131 | + |
| 132 | + try { |
| 133 | + match translator.fluent_bundle().map(|b| translate_with_bundle(b)) { |
| 134 | + // The primary bundle was present and translation succeeded |
| 135 | + Some(Ok(t)) => t, |
| 136 | + |
| 137 | + // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely |
| 138 | + // just that the primary bundle doesn't contain the message being translated, so |
| 139 | + // proceed to the fallback bundle. |
| 140 | + Some(Err( |
| 141 | + primary @ TranslateError::One { kind: TranslateErrorKind::MessageMissing, .. }, |
| 142 | + )) => translate_with_bundle(fallback(translator)) |
| 143 | + .map_err(|fallback| primary.and(fallback))?, |
| 144 | + |
| 145 | + // Always yeet out for errors on debug (unless |
| 146 | + // `RUSTC_TRANSLATION_NO_DEBUG_ASSERT` is set in the environment - this allows |
| 147 | + // local runs of the test suites, of builds with debug assertions, to test the |
| 148 | + // behaviour in a normal build). |
| 149 | + Some(Err(primary)) |
| 150 | + if cfg!(debug_assertions) |
| 151 | + && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() => |
| 152 | + { |
| 153 | + do yeet primary |
| 154 | + } |
| 155 | + |
| 156 | + // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so |
| 157 | + // just hide it and try with the fallback bundle. |
| 158 | + Some(Err(primary)) => translate_with_bundle(fallback(translator)) |
| 159 | + .map_err(|fallback| primary.and(fallback))?, |
| 160 | + |
| 161 | + // The primary bundle is missing, proceed to the fallback bundle |
| 162 | + None => translate_with_bundle(fallback(translator)) |
| 163 | + .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?, |
128 | 164 | }
|
129 | 165 | }
|
130 | 166 | }
|
0 commit comments