@@ -273,40 +273,58 @@ pub trait Emitter {
273
273
DiagnosticMessage :: FluentIdentifier ( identifier, attr) => ( identifier, attr) ,
274
274
} ;
275
275
276
- let bundle = match self . fluent_bundle ( ) {
277
- Some ( bundle) if bundle. has_message ( & identifier) => bundle,
278
- _ => self . fallback_fluent_bundle ( ) ,
279
- } ;
276
+ let translate_with_bundle = |bundle : & ' a FluentBundle | -> Option < ( Cow < ' _ , str > , Vec < _ > ) > {
277
+ let message = bundle. get_message ( & identifier) ?;
278
+ let value = match attr {
279
+ Some ( attr) => message. get_attribute ( attr) ?. value ( ) ,
280
+ None => message. value ( ) ?,
281
+ } ;
282
+ debug ! ( ?message, ?value) ;
280
283
281
- let message = bundle. get_message ( & identifier) . expect ( "missing diagnostic in fluent bundle" ) ;
282
- let value = match attr {
283
- Some ( attr) => {
284
- if let Some ( attr) = message. get_attribute ( attr) {
285
- attr. value ( )
286
- } else {
287
- panic ! ( "missing attribute `{attr}` in fluent message `{identifier}`" )
288
- }
289
- }
290
- None => {
291
- if let Some ( value) = message. value ( ) {
292
- value
293
- } else {
294
- panic ! ( "missing value in fluent message `{identifier}`" )
295
- }
296
- }
284
+ let mut errs = vec ! [ ] ;
285
+ let translated = bundle. format_pattern ( value, Some ( & args) , & mut errs) ;
286
+ debug ! ( ?translated, ?errs) ;
287
+ Some ( ( translated, errs) )
297
288
} ;
298
289
299
- let mut err = vec ! [ ] ;
300
- let translated = bundle. format_pattern ( value, Some ( & args) , & mut err) ;
301
- trace ! ( ?translated, ?err) ;
302
- debug_assert ! (
303
- err. is_empty( ) ,
304
- "identifier: {:?}, args: {:?}, errors: {:?}" ,
305
- identifier,
306
- args,
307
- err
308
- ) ;
309
- translated
290
+ self . fluent_bundle ( )
291
+ . and_then ( |bundle| translate_with_bundle ( bundle) )
292
+ // If `translate_with_bundle` returns `None` with the primary bundle, this is likely
293
+ // just that the primary bundle doesn't contain the message being translated, so
294
+ // proceed to the fallback bundle.
295
+ //
296
+ // However, when errors are produced from translation, then that means the translation
297
+ // is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided).
298
+ //
299
+ // In debug builds, assert so that compiler devs can spot the broken translation and
300
+ // fix it..
301
+ . inspect ( |( _, errs) | {
302
+ debug_assert ! (
303
+ errs. is_empty( ) ,
304
+ "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}" ,
305
+ identifier,
306
+ attr,
307
+ args,
308
+ errs
309
+ ) ;
310
+ } )
311
+ // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
312
+ // just hide it and try with the fallback bundle.
313
+ . filter ( |( _, errs) | errs. is_empty ( ) )
314
+ . or_else ( || translate_with_bundle ( self . fallback_fluent_bundle ( ) ) )
315
+ . map ( |( translated, errs) | {
316
+ // Always bail out for errors with the fallback bundle.
317
+ assert ! (
318
+ errs. is_empty( ) ,
319
+ "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}" ,
320
+ identifier,
321
+ attr,
322
+ args,
323
+ errs
324
+ ) ;
325
+ translated
326
+ } )
327
+ . expect ( "failed to find message in primary or fallback fluent bundles" )
310
328
}
311
329
312
330
/// Formats the substitutions of the primary_span
0 commit comments