rustc_errors/
translation.rs

1use std::borrow::Cow;
2use std::env;
3use std::error::Report;
4
5pub use rustc_error_messages::FluentArgs;
6use tracing::{debug, trace};
7
8use crate::error::{TranslateError, TranslateErrorKind};
9use crate::snippet::Style;
10use crate::{DiagArg, DiagMessage, FluentBundle};
11
12/// Convert diagnostic arguments (a rustc internal type that exists to implement
13/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
14///
15/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
16/// passed around as a reference thereafter.
17pub fn to_fluent_args<'iter>(iter: impl Iterator<Item = DiagArg<'iter>>) -> FluentArgs<'static> {
18    let mut args = if let Some(size) = iter.size_hint().1 {
19        FluentArgs::with_capacity(size)
20    } else {
21        FluentArgs::new()
22    };
23
24    for (k, v) in iter {
25        args.set(k.clone(), v.clone());
26    }
27
28    args
29}
30
31pub trait Translate {
32    /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
33    /// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
34    /// should be used.
35    fn fluent_bundle(&self) -> Option<&FluentBundle>;
36
37    /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
38    /// Used when the user has not requested a specific language or when a localized diagnostic is
39    /// unavailable for the requested locale.
40    fn fallback_fluent_bundle(&self) -> &FluentBundle;
41
42    /// Convert `DiagMessage`s to a string, performing translation if necessary.
43    fn translate_messages(
44        &self,
45        messages: &[(DiagMessage, Style)],
46        args: &FluentArgs<'_>,
47    ) -> Cow<'_, str> {
48        Cow::Owned(
49            messages
50                .iter()
51                .map(|(m, _)| self.translate_message(m, args).map_err(Report::new).unwrap())
52                .collect::<String>(),
53        )
54    }
55
56    /// Convert a `DiagMessage` to a string, performing translation if necessary.
57    fn translate_message<'a>(
58        &'a self,
59        message: &'a DiagMessage,
60        args: &'a FluentArgs<'_>,
61    ) -> Result<Cow<'a, str>, TranslateError<'a>> {
62        trace!(?message, ?args);
63        let (identifier, attr) = match message {
64            DiagMessage::Str(msg) | DiagMessage::Translated(msg) => {
65                return Ok(Cow::Borrowed(msg));
66            }
67            DiagMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
68        };
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                }
91            };
92
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))?,
127            }
128        }
129    }
130}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy