mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 13:04:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			138 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			138 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| 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;
 | |
| 
 | |
| /// Convert diagnostic arguments (a rustc internal type that exists to implement
 | |
| /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
 | |
| ///
 | |
| /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
 | |
| /// passed around as a reference thereafter.
 | |
| 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 {
 | |
|     /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
 | |
|     /// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
 | |
|     /// should be used.
 | |
|     fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>;
 | |
| 
 | |
|     /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
 | |
|     /// Used when the user has not requested a specific language or when a localized diagnostic is
 | |
|     /// unavailable for the requested locale.
 | |
|     fn fallback_fluent_bundle(&self) -> &FluentBundle;
 | |
| 
 | |
|     /// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
 | |
|     fn translate_messages(
 | |
|         &self,
 | |
|         messages: &[(DiagnosticMessage, Style)],
 | |
|         args: &FluentArgs<'_>,
 | |
|     ) -> Cow<'_, str> {
 | |
|         Cow::Owned(
 | |
|             messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     /// Convert a `DiagnosticMessage` to a string, performing translation if necessary.
 | |
|     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))
 | |
|             // If `translate_with_bundle` returns `None` with the primary bundle, this is likely
 | |
|             // just that the primary bundle doesn't contain the message being translated, so
 | |
|             // proceed to the fallback bundle.
 | |
|             //
 | |
|             // However, when errors are produced from translation, then that means the translation
 | |
|             // is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided).
 | |
|             //
 | |
|             // In debug builds, assert so that compiler devs can spot the broken translation and
 | |
|             // fix it..
 | |
|             .inspect(|(_, errs)| {
 | |
|                 debug_assert!(
 | |
|                     errs.is_empty(),
 | |
|                     "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
 | |
|                     identifier,
 | |
|                     attr,
 | |
|                     args,
 | |
|                     errs
 | |
|                 );
 | |
|             })
 | |
|             // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
 | |
|             // just hide it and try with the fallback bundle.
 | |
|             .filter(|(_, errs)| errs.is_empty())
 | |
|             .or_else(|| translate_with_bundle(self.fallback_fluent_bundle()))
 | |
|             .map(|(translated, errs)| {
 | |
|                 // Always bail out for errors with the fallback bundle.
 | |
| 
 | |
|                 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")
 | |
|     }
 | |
| }
 | 
