canonical / ubuntu-desktop-installer

Ubuntu Desktop Installer
GNU General Public License v3.0
536 stars 94 forks source link

flutter_localizations does not support all installer languages #465

Closed jpnurmi closed 1 year ago

jpnurmi commented 2 years ago

There are a few languages (Cymraeg, Esperanto, ...) that cause Flutter to throw an exception because MaterialLocalizations doesn't support all languages that the installer's AppLocalizations includes:

Screenshot from 2021-11-03 14-07-21

oSoMoN commented 2 years ago

For those we'll need to do the same as for Occitan, i.e. implement a suitable MaterialLocalization and a LocalizationsDelegate (see https://github.com/canonical/ubuntu-desktop-installer/blob/main/packages/ubuntu_wizard/lib/l10n.dart#L119).

Mejans commented 2 years ago

Many thanks @oSoMoN for this by the way ;)

jpnurmi commented 2 years ago

In addition to oc, MaterialLocalizations is missing for:

jpnurmi commented 2 years ago

Out of these, the intl package has built-in support for cy and ga:

import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';

Future<void> main(List<String> arguments) async {
  await initializeDateFormatting();
  print('cy: ${DateFormat.localeExists("cy")}'); // true
  print('eo: ${DateFormat.localeExists("eo")}'); // false
  print('ga: ${DateFormat.localeExists("ga")}'); // true
  print('ku: ${DateFormat.localeExists("ku")}'); // false
  print('nn_NO: ${DateFormat.localeExists("nn_NO")}'); // false
  print('se_NO: ${DateFormat.localeExists("se_NO")}'); // false
  print('tg: ${DateFormat.localeExists("tg")}'); // false
  print('ug: ${DateFormat.localeExists("ug")}'); // false
  print('bo: ${DateFormat.localeExists("bo")}'); // false
  print('dz: ${DateFormat.localeExists("dz")}'); // false
}
jpnurmi commented 2 years ago

At first, it looks somewhat straightforward when you start reading the Adding support for a new language section in the Flutter docs. Then, you notice that nnLocaleDatePatterns and nnDateSymbols are just stubs. It starts getting more interesting when looking at the complete add_language example.

Relevant issues:

It boils down to missing locale data in the intl package. Unfortunately, the tools, that could be used for generating the missing data from CLDR, are not public.

jpnurmi commented 2 years ago

For reference

flutter/lib/src/material/material_localizations.dart:

abstract class MaterialLocalizations {
  String get cancelButtonLabel;
  static MaterialLocalizations of(BuildContext context)
}

class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations>{
  bool isSupported(Locale locale) => locale.languageCode == 'en';
  Future<MaterialLocalizations> load(Locale locale) => DefaultMaterialLocalizations.load(locale);
}

class DefaultMaterialLocalizations implements MaterialLocalizations {
  String get cancelButtonLabel => 'CANCEL';
  static Future<MaterialLocalizations> load(Locale locale) {
    return SynchronousFuture<MaterialLocalizations>(const DefaultMaterialLocalizations());
  }
  static const LocalizationsDelegate<MaterialLocalizations> delegate = _MaterialLocalizationsDelegate();
}

flutter_localizations/lib/src/material_localizations.dart:

abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
  static const LocalizationsDelegate<MaterialLocalizations> delegate = _MaterialLocalizationsDelegate();
  static const List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[
    GlobalCupertinoLocalizations.delegate,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
  ];
}

class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
  bool isSupported(Locale locale) => kMaterialSupportedLanguages.contains(locale.languageCode);
  Future<MaterialLocalizations> load(Locale locale) {
    final String localeName = intl.Intl.canonicalizedLocale(locale.toString());
    return SynchronousFuture<MaterialLocalizations>(...);
  }
}

flutter_localizations/lib/src/l10n/generated_material_localizations.dart:

class MaterialLocalizationFi extends GlobalMaterialLocalizations {
  String get cancelButtonLabel => 'PERUUTA';
}

P.S. Notice two different _MaterialLocalizationsDelegate classes.

jpnurmi commented 2 years ago

Solved by dart-lang/intl#506

jpnurmi commented 1 year ago

The issue is back. We need the same delegates for Cupertino due to Material's dependency on Cupertino through the various "adaptive" widgets.

Mejans commented 1 year ago

Many thanks for taking care of our languages!

jpnurmi commented 1 year ago

np :) fixed again by #1392