Repository: flutter
Issue: [gen-l10n] Allow specifying language fallback for untranslated messages 
Labels: c: new feature,framework,a: internationalization,c: proposal,P3,team-framework,triaged-framework

### Use case

When a ARB file is missing a translation, we might want to use a fallback that is different than the template language. I can see two scenarios where that could be useful.

## Scenario 1: Template language for translation is different from the app's default language

As a French developer, I want my templating file to be the `fr` one as this is the one that my design and product team use when creating our app's specification, and our translators always use the French language as the base language for translations.

The problem is that when we start to deploy our app globally, we might want the fallback language to be English rather than French as it is more popular.

Now let's take an example on three language files: 

// app_fr.arb - Template file
```json
{
  "translatedInAllLanguages": "Traduit dans toutes les langues",
  "translatedInEnglishButNotInSpanish": "Traduit en anglais mais pas en espagnol",
  "notTranslatedAtAll": "Pas traduit du tout"
}
```

// app_en.arb 
```json
{
  "translatedInAllLanguages": "Translated in all languages",
  "translatedInEnglishButNotInSpanish": "Translated in all languages",
}
```

// app_es.arb
```json
{
  "translatedInAllLanguages": "Traducido a todos los idiomas.",
}
```

When generating the spanish translation, flutter `gen-l10n` will always take our templating language as the fallback for untraslated strings, e.g.:

```dart
class L10NEn extends L10N {
  @override
  String get translatedInAllLanguages => 'Traducido a todos los idiomas.';

  @override
  String get translatedInEnglishButNotInSpanish => 'Traduit en anglais mais pas en espagnol';

  @override
  String get notTranslatedAtAll => 'Pas traduit du tout';
}
```

But I would like to be able to specify an alternative fallback language if that fallback language has the translated value.

```dart
class L10NEn extends L10N {
  @override
  String get translatedInAllLanguages => 'Traducido a todos los idiomas.';

  // ℹ️ I would like the fallback value to be the Enligsh one here
  @override
  String get translatedInEnglishButNotInSpanish => 'Translated in English but not Spanish';

  // ℹ️ The template language (French) would still apply when no fallback is available
  @override
  String get notTranslatedAtAll => 'Pas traduit du tout';
}
```

## Scenario 2: There is a "closest" language fallback than the templating language

Say your templating language is English, but you are also supporting Catalan and Basque languages. 
In that case, you might find more appropriate that your Catalan speakers fallback to the Spanish version when the the Catalan translation is not defined but the Spanish translation is defined, and that your Basque speakers fallback to the French (or Spanish) version when the the Basque translation is not defined but the French/Spanish translation is defined.

### Proposal

We could allow specifying a specific fallback or a fallback list in each language file using a specific key, e.g.:

`"@@fallbacks": ["en"]`

In the previous scenarios that would translate to:

## Scenario 1

// app_fr.arb - Template file
```json
{
  // No need for fallback as this is the templating language
}
```

// app_en.arb 
```json
{
  // Nothing to declare, we can use templating language as the fallback here
}
```

// app_es.arb
```json
{
  "@@fallbacks": ["en"]
}
```

## Scenario 2

// app_en.arb - Template file
```json
{
  // No need for fallback as this is the templating language
}
```

// app_ca.arb (Catalan)
```json
{
   "@@fallbacks": ["es"]
}
```

// app_eu.arb (Basque)
```json
{
  "@@fallbacks": ["fr", "es"]
}
```

## Explanation

When running `flutter gen-l10n`, it could parse this special `fallbacks` key and while generating the classes, it could try to find the fallback translation rather than always going for the templating language as a fallback. The ultimate fallback must still be the templating language as it is the only one where we are sure that the translation exists.

Basically the pseudo code would be:

```dart
createTranslationMethodOrGetter() {
  if(translationExists) {
     // use translation;
  } else if(hasFallbacks && anyFallbackHasTranslation) {
     // use first fallback that has this translation
  } else {
    // use templating file translation
  }
}
```

Where the check order would be:

Language translation > Fallback 1 translation > Fallback 2 translation > Fallback N translation > Templating translation

The thing that might be tricky is that we need to make sure that there are no cyclic dependencies between fallbacks.