mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Localizations for Material (#11832)
This commit is contained in:
parent
0323f2ddd2
commit
541afae45b
127
dev/tools/gen_localizations.dart
Normal file
127
dev/tools/gen_localizations.dart
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// Given a directory that contains localized ".arb" (application resource
|
||||||
|
// bundle) files, generates a Dart "localizations" Map definition that combines
|
||||||
|
// the contents of the arb files. The map can be used to lookup a localized
|
||||||
|
// string: localizations[localeString][resourceId].
|
||||||
|
//
|
||||||
|
// See *.arb and localizations.dart in packages/flutter/lib/src/material/i18n/.
|
||||||
|
//
|
||||||
|
// The arb (JSON) format files must contain a single map indexed by locale.
|
||||||
|
// Each map value is itself a map with resource identifier keys and localized
|
||||||
|
// resource string values.
|
||||||
|
//
|
||||||
|
// The arb filenames are assumed to end in "prefix_lc.arb" or "prefix_lc_cc.arb",
|
||||||
|
// where prefix is the 2nd command line argument, lc is a language code and cc
|
||||||
|
// is the country code. In most cases both codes are just two characters. A typical
|
||||||
|
// filename would be "material_en.arb".
|
||||||
|
//
|
||||||
|
// This app is typically run by hand when a module's .arb files have been
|
||||||
|
// updated.
|
||||||
|
//
|
||||||
|
// Usage: dart gen_localizations.dart directory prefix
|
||||||
|
|
||||||
|
import 'dart:convert' show JSON;
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
const String outputHeader = '''
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file has been automatically generated. Please do not edit it manually.
|
||||||
|
// To regenerate the file, use:
|
||||||
|
// @(regenerate)
|
||||||
|
''';
|
||||||
|
|
||||||
|
final Map<String, Map<String, String>> localeToResources = <String, Map<String, String>>{};
|
||||||
|
|
||||||
|
// Return s as a Dart-parseable raw string in double quotes. Expand double quotes:
|
||||||
|
// foo => r"foo"
|
||||||
|
// foo "bar" => r"foo " '"' r"bar" '"'
|
||||||
|
String generateString(String s) {
|
||||||
|
if (!s.contains('"'))
|
||||||
|
return 'r"$s"';
|
||||||
|
|
||||||
|
final StringBuffer output = new StringBuffer();
|
||||||
|
bool started = false; // Have we started writing a raw string.
|
||||||
|
for (int i = 0; i < s.length; i++) {
|
||||||
|
if (s[i] == '"') {
|
||||||
|
if (started)
|
||||||
|
output.write('"');
|
||||||
|
output.write(' \'"\' ');
|
||||||
|
started = false;
|
||||||
|
} else if (!started) {
|
||||||
|
output.write('r"${s[i]}');
|
||||||
|
started = true;
|
||||||
|
} else {
|
||||||
|
output.write(s[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (started)
|
||||||
|
output.write('"');
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String generateLocalizationsMap() {
|
||||||
|
final StringBuffer output = new StringBuffer();
|
||||||
|
|
||||||
|
output.writeln('const Map<String, Map<String, String>> localizations = const <String, Map<String, String>> {');
|
||||||
|
|
||||||
|
final String lastLocale = localeToResources.keys.last;
|
||||||
|
for (String locale in localeToResources.keys) {
|
||||||
|
output.writeln(' "$locale": const <String, String>{');
|
||||||
|
|
||||||
|
final Map<String, String> resources = localeToResources[locale];
|
||||||
|
final String lastName = resources.keys.last;
|
||||||
|
for (String name in resources.keys) {
|
||||||
|
final String comma = name == lastName ? "" : ",";
|
||||||
|
final String value = generateString(resources[name]);
|
||||||
|
output.writeln(' "$name": $value$comma');
|
||||||
|
}
|
||||||
|
final String comma = locale == lastLocale ? "" : ",";
|
||||||
|
output.writeln(' }$comma');
|
||||||
|
}
|
||||||
|
|
||||||
|
output.writeln('};');
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processBundle(File file, String locale) {
|
||||||
|
localeToResources[locale] ??= <String, String>{};
|
||||||
|
final Map<String, String> resources = localeToResources[locale];
|
||||||
|
final Map<String, dynamic> bundle = JSON.decode(file.readAsStringSync());
|
||||||
|
for (String key in bundle.keys) {
|
||||||
|
// The ARB file resource "attributes" for foo are called @foo.
|
||||||
|
if (key.startsWith('@'))
|
||||||
|
continue;
|
||||||
|
resources[key] = bundle[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(List<String> args) {
|
||||||
|
if (args.length != 2)
|
||||||
|
stderr.writeln('Usage: dart gen_localizations.dart directory prefix');
|
||||||
|
|
||||||
|
// filenames are assumed to end in "prefix_lc.arb" or "prefix_lc_cc.arb", where prefix
|
||||||
|
// is the 2nd command line argument, lc is a language code and cc is the country
|
||||||
|
// code. In most cases both codes are just two characters.
|
||||||
|
|
||||||
|
final Directory directory = new Directory(args[0]);
|
||||||
|
final String prefix = args[1];
|
||||||
|
final RegExp filenameRE = new RegExp('${prefix}_(\\w+)\\.arb\$');
|
||||||
|
|
||||||
|
for (FileSystemEntity entity in directory.listSync()) {
|
||||||
|
final String path = entity.path;
|
||||||
|
if (FileSystemEntity.isFileSync(path) && filenameRE.hasMatch(path)) {
|
||||||
|
final String locale = filenameRE.firstMatch(path)[1];
|
||||||
|
processBundle(new File(path), locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final String regenerate = 'dart gen_localizations ${directory.path} ${args[1]}';
|
||||||
|
print(outputHeader.replaceFirst('@(regenerate)', regenerate));
|
||||||
|
print(generateLocalizationsMap());
|
||||||
|
}
|
@ -26,19 +26,17 @@ const TextStyle _errorTextStyle = const TextStyle(
|
|||||||
decorationStyle: TextDecorationStyle.double
|
decorationStyle: TextDecorationStyle.double
|
||||||
);
|
);
|
||||||
|
|
||||||
// Delegate that fetches the default (English) strings.
|
|
||||||
class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
|
class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
|
||||||
const _MaterialLocalizationsDelegate();
|
const _MaterialLocalizationsDelegate();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MaterialLocalizations> load(Locale locale) {
|
Future<MaterialLocalizations> load(Locale locale) => DefaultMaterialLocalizations.load(locale);
|
||||||
return new SynchronousFuture<MaterialLocalizations>(const MaterialLocalizations());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldReload(_MaterialLocalizationsDelegate old) => false;
|
bool shouldReload(_MaterialLocalizationsDelegate old) => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// An application that uses material design.
|
/// An application that uses material design.
|
||||||
///
|
///
|
||||||
/// A convenience widget that wraps a number of widgets that are commonly
|
/// A convenience widget that wraps a number of widgets that are commonly
|
||||||
|
80
packages/flutter/lib/src/material/i18n/localizations.dart
Normal file
80
packages/flutter/lib/src/material/i18n/localizations.dart
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file has been automatically generated. Please do not edit it manually.
|
||||||
|
// To regenerate the file, use:
|
||||||
|
// dart gen_localizations packages/flutter/lib/src/material/i18n material
|
||||||
|
|
||||||
|
const Map<String, Map<String, String>> localizations = const <String, Map<String, String>> {
|
||||||
|
"ar": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"افتح قائمة التنقل",
|
||||||
|
"backButtonTooltip": r"الى الخلف",
|
||||||
|
"closeButtonTooltip": r"إغلا",
|
||||||
|
"nextMonthTooltip": r"الشهر القادم",
|
||||||
|
"previousMonthTooltip": r"الشهر الماضى"
|
||||||
|
},
|
||||||
|
"it": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"Apri il menu di navigazione",
|
||||||
|
"backButtonTooltip": r"Indietro",
|
||||||
|
"closeButtonTooltip": r"Chiudi",
|
||||||
|
"nextMonthTooltip": r"Il prossimo mese",
|
||||||
|
"previousMonthTooltip": r"Il mese scorso"
|
||||||
|
},
|
||||||
|
"pt": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"Abrir menu de navegação",
|
||||||
|
"backButtonTooltip": r"Costas",
|
||||||
|
"closeButtonTooltip": r"Fechar",
|
||||||
|
"nextMonthTooltip": r"Próximo mês",
|
||||||
|
"previousMonthTooltip": r"Mês anterior"
|
||||||
|
},
|
||||||
|
"es": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"Abrir el menú de navegación",
|
||||||
|
"backButtonTooltip": r"Espalda",
|
||||||
|
"closeButtonTooltip": r"Cerrar",
|
||||||
|
"nextMonthTooltip": r"Próximo mes",
|
||||||
|
"previousMonthTooltip": r"mes anterior"
|
||||||
|
},
|
||||||
|
"fr": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"Ouvrir le menu de navigation",
|
||||||
|
"backButtonTooltip": r"Arrière",
|
||||||
|
"closeButtonTooltip": r"Fermer",
|
||||||
|
"nextMonthTooltip": r"Mois Suivant",
|
||||||
|
"previousMonthTooltip": r"Le mois précédent"
|
||||||
|
},
|
||||||
|
"zh": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"打开导航菜单",
|
||||||
|
"backButtonTooltip": r"背部",
|
||||||
|
"closeButtonTooltip": r"关",
|
||||||
|
"nextMonthTooltip": r"-下月就29了。",
|
||||||
|
"previousMonthTooltip": r"前一个月"
|
||||||
|
},
|
||||||
|
"en": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"Open navigation menu",
|
||||||
|
"backButtonTooltip": r"Back",
|
||||||
|
"closeButtonTooltip": r"Close",
|
||||||
|
"nextMonthTooltip": r"Next month",
|
||||||
|
"previousMonthTooltip": r"Previous month"
|
||||||
|
},
|
||||||
|
"de": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"Navigationsmenü öffnen",
|
||||||
|
"backButtonTooltip": r"Zurück",
|
||||||
|
"closeButtonTooltip": r"Schließen ",
|
||||||
|
"nextMonthTooltip": r"Nächster Monat",
|
||||||
|
"previousMonthTooltip": r"Letzter Monat"
|
||||||
|
},
|
||||||
|
"ja": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"ナビゲーションメニューを開く",
|
||||||
|
"backButtonTooltip": r"バック",
|
||||||
|
"closeButtonTooltip": r"閉じる",
|
||||||
|
"nextMonthTooltip": r"来月",
|
||||||
|
"previousMonthTooltip": r"前の月"
|
||||||
|
},
|
||||||
|
"ru": const <String, String>{
|
||||||
|
"openAppDrawerTooltip": r"Открыть меню навигации",
|
||||||
|
"backButtonTooltip": r"назад",
|
||||||
|
"closeButtonTooltip": r"Закрыть",
|
||||||
|
"nextMonthTooltip": r"В следующем месяце",
|
||||||
|
"previousMonthTooltip": r"Предыдущий месяц"
|
||||||
|
}
|
||||||
|
};
|
27
packages/flutter/lib/src/material/i18n/material_ar.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_ar.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "افتح قائمة التنقل",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "الى الخلف",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "إغلا",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "الشهر القادم",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "الشهر الماضى",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
27
packages/flutter/lib/src/material/i18n/material_de.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_de.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "Navigationsmenü öffnen",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "Zurück",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "Schließen ",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "Nächster Monat",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "Letzter Monat",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
31
packages/flutter/lib/src/material/i18n/material_en.arb
Normal file
31
packages/flutter/lib/src/material/i18n/material_en.arb
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "Open navigation menu",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
|
||||||
|
"backButtonTooltip": "Back",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
|
||||||
|
"closeButtonTooltip": "Close",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
|
||||||
|
"nextMonthTooltip": "Next month",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
|
||||||
|
"previousMonthTooltip": "Previous month",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
27
packages/flutter/lib/src/material/i18n/material_es.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_es.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "Abrir el menú de navegación",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "Espalda",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "Cerrar",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "Próximo mes",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "mes anterior",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
27
packages/flutter/lib/src/material/i18n/material_fr.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_fr.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "Ouvrir le menu de navigation",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "Arrière",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "Fermer",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "Mois Suivant",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "Le mois précédent",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
27
packages/flutter/lib/src/material/i18n/material_it.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_it.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "Apri il menu di navigazione",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "Indietro",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "Chiudi",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "Il prossimo mese",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "Il mese scorso",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
27
packages/flutter/lib/src/material/i18n/material_ja.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_ja.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "ナビゲーションメニューを開く",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "バック",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "閉じる",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "来月",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "前の月",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
27
packages/flutter/lib/src/material/i18n/material_pt.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_pt.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "Abrir menu de navegação",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "Costas",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "Fechar",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "Próximo mês",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "Mês anterior",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
27
packages/flutter/lib/src/material/i18n/material_ru.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_ru.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "Открыть меню навигации",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "назад",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "Закрыть",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "В следующем месяце",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "Предыдущий месяц",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
27
packages/flutter/lib/src/material/i18n/material_zh.arb
Normal file
27
packages/flutter/lib/src/material/i18n/material_zh.arb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"openAppDrawerTooltip": "打开导航菜单",
|
||||||
|
"@openAppDrawerTooltip": {
|
||||||
|
"description": "The tooltip for the leading AppBar menu (aka 'hamburger') button",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"backButtonTooltip": "背部",
|
||||||
|
"@backButtonTooltip": {
|
||||||
|
"description": "The BackButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"closeButtonTooltip": "关",
|
||||||
|
"@closeButtonTooltip": {
|
||||||
|
"description": "The CloseButton's tooltip",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"nextMonthTooltip": "-下月就29了。",
|
||||||
|
"@nextMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'next month' button.",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"previousMonthTooltip": "前一个月",
|
||||||
|
"@previousMonthTooltip": {
|
||||||
|
"description": "The tooltip for the MonthPicker's 'previous month' button.",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
}
|
@ -2,35 +2,34 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
/// Interface for localized resource values for the material widgets.
|
import 'i18n/localizations.dart';
|
||||||
|
|
||||||
|
/// Defines the localized resource values used by the Material widgts.
|
||||||
///
|
///
|
||||||
/// This class provides a default placeholder implementation that returns
|
/// See also:
|
||||||
/// hard-coded American English values.
|
///
|
||||||
class MaterialLocalizations {
|
/// * [DefaultMaterialLocalizations], which implements this interface and
|
||||||
/// Create a placeholder object for the localized resources of material widgets
|
/// and supports a variety of locales.
|
||||||
/// which only provides American English strings.
|
abstract class MaterialLocalizations {
|
||||||
const MaterialLocalizations();
|
|
||||||
|
|
||||||
/// The locale for which the values of this class's localized resources
|
|
||||||
/// have been translated.
|
|
||||||
Locale get locale => const Locale('en', 'US');
|
|
||||||
|
|
||||||
/// The tooltip for the leading [AppBar] menu (aka 'hamburger') button
|
/// The tooltip for the leading [AppBar] menu (aka 'hamburger') button
|
||||||
String get openAppDrawerTooltip => 'Open navigation menu';
|
String get openAppDrawerTooltip;
|
||||||
|
|
||||||
/// The [BackButton]'s tooltip.
|
/// The [BackButton]'s tooltip.
|
||||||
String get backButtonTooltip => 'Back';
|
String get backButtonTooltip;
|
||||||
|
|
||||||
/// The [CloseButton]'s tooltip.
|
/// The [CloseButton]'s tooltip.
|
||||||
String get closeButtonTooltip => 'Close';
|
String get closeButtonTooltip;
|
||||||
|
|
||||||
/// The tooltip for the [MonthPicker]'s "next month" button.
|
/// The tooltip for the [MonthPicker]'s "next month" button.
|
||||||
String get nextMonthTooltip => 'Next month';
|
String get nextMonthTooltip;
|
||||||
|
|
||||||
/// The tooltip for the [MonthPicker]'s "previous month" button.
|
/// The tooltip for the [MonthPicker]'s "previous month" button.
|
||||||
String get previousMonthTooltip => 'Previous month';
|
String get previousMonthTooltip;
|
||||||
|
|
||||||
/// The `MaterialLocalizations` from the closest [Localizations] instance
|
/// The `MaterialLocalizations` from the closest [Localizations] instance
|
||||||
/// that encloses the given context.
|
/// that encloses the given context.
|
||||||
@ -48,3 +47,46 @@ class MaterialLocalizations {
|
|||||||
return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
|
return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Localized strings for the material widgets.
|
||||||
|
class DefaultMaterialLocalizations implements MaterialLocalizations {
|
||||||
|
/// Construct an object that defines the material widgets' localized strings
|
||||||
|
/// for the given `locale`.
|
||||||
|
DefaultMaterialLocalizations(this.locale) {
|
||||||
|
assert(locale != null);
|
||||||
|
_nameToValue = localizations[locale.toString()]
|
||||||
|
?? localizations[locale.languageCode]
|
||||||
|
?? localizations['en']
|
||||||
|
?? <String, String>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> _nameToValue;
|
||||||
|
|
||||||
|
/// The locale for which the values of this class's localized resources
|
||||||
|
/// have been translated.
|
||||||
|
final Locale locale;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get openAppDrawerTooltip => _nameToValue["openAppDrawerTooltip"];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get backButtonTooltip => _nameToValue["backButtonTooltip"];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get closeButtonTooltip => _nameToValue["closeButtonTooltip"];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get nextMonthTooltip => _nameToValue["nextMonthTooltip"];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get previousMonthTooltip => _nameToValue["previousMonthTooltip"];
|
||||||
|
|
||||||
|
/// Creates an object that provides localized resource values for the
|
||||||
|
/// for the widgets of the material library.
|
||||||
|
///
|
||||||
|
/// This method is typically used to create a [LocalizationsDelegate].
|
||||||
|
/// The [MaterialApp] does so by default.
|
||||||
|
static Future<MaterialLocalizations> load(Locale locale) {
|
||||||
|
return new SynchronousFuture<MaterialLocalizations>(new DefaultMaterialLocalizations(locale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,10 +16,17 @@ import 'framework.dart';
|
|||||||
// class Intl { static String message(String s, { String name, String locale }) => ''; }
|
// class Intl { static String message(String s, { String name, String locale }) => ''; }
|
||||||
// Future<Null> initializeMessages(String locale) => null;
|
// Future<Null> initializeMessages(String locale) => null;
|
||||||
|
|
||||||
|
// Used by loadAll() to record LocalizationsDelegate.load() futures we're
|
||||||
|
// waiting for.
|
||||||
|
class _Pending {
|
||||||
|
_Pending(this.delegate, this.futureValue);
|
||||||
|
final LocalizationsDelegate<dynamic> delegate;
|
||||||
|
final Future<dynamic> futureValue;
|
||||||
|
}
|
||||||
|
|
||||||
// A utility function used by Localizations to generate one future
|
// A utility function used by Localizations to generate one future
|
||||||
// that completes when all of the LocalizationsDelegate.load() futures
|
// that completes when all of the LocalizationsDelegate.load() futures
|
||||||
// complete. The returned map is indexed by the type of each input
|
// complete. The returned map is indexed by each delegate's type.
|
||||||
// future's value.
|
|
||||||
//
|
//
|
||||||
// The input future values must have distinct types.
|
// The input future values must have distinct types.
|
||||||
//
|
//
|
||||||
@ -31,35 +38,38 @@ import 'framework.dart';
|
|||||||
// This is more complicated than just applying Future.wait to input
|
// This is more complicated than just applying Future.wait to input
|
||||||
// because some of the input.values may be SynchronousFutures. We don't want
|
// because some of the input.values may be SynchronousFutures. We don't want
|
||||||
// to Future.wait for the synchronous futures.
|
// to Future.wait for the synchronous futures.
|
||||||
Future<Map<Type, dynamic>> _loadAll(Iterable<Future<dynamic>> inputValues) {
|
Future<Map<Type, dynamic>> _loadAll(Locale locale, Iterable<LocalizationsDelegate<dynamic>> delegates) {
|
||||||
final Map<Type, dynamic> output = <Type, dynamic>{};
|
final Map<Type, dynamic> output = <Type, dynamic>{};
|
||||||
List<Future<dynamic>> outputFutures;
|
List<_Pending> pendingList;
|
||||||
|
|
||||||
for (Future<dynamic> inputValue in inputValues) {
|
for (LocalizationsDelegate<dynamic> delegate in delegates) {
|
||||||
|
final Future<dynamic> inputValue = delegate.load(locale);
|
||||||
dynamic completedValue;
|
dynamic completedValue;
|
||||||
final Future<dynamic> futureValue = inputValue.then<dynamic>((dynamic value) {
|
final Future<dynamic> futureValue = inputValue.then<dynamic>((dynamic value) {
|
||||||
return completedValue = value;
|
return completedValue = value;
|
||||||
});
|
});
|
||||||
if (completedValue != null) { // inputValue was a SynchronousFuture
|
if (completedValue != null) { // inputValue was a SynchronousFuture
|
||||||
final Type type = completedValue.runtimeType;
|
final Type type = delegate.type;
|
||||||
assert(!output.containsKey(type));
|
assert(!output.containsKey(type));
|
||||||
output[type] = completedValue;
|
output[type] = completedValue;
|
||||||
} else {
|
} else {
|
||||||
outputFutures ??= <Future<dynamic>>[];
|
pendingList ??= <_Pending>[];
|
||||||
outputFutures.add(futureValue);
|
pendingList.add(new _Pending(delegate, futureValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All of the input.values were synchronous futures, we're done.
|
// All of the delegate.load() values were synchronous futures, we're done.
|
||||||
if (outputFutures == null)
|
if (pendingList == null)
|
||||||
return new SynchronousFuture<Map<Type, dynamic>>(output);
|
return new SynchronousFuture<Map<Type, dynamic>>(output);
|
||||||
|
|
||||||
// Some of input.values were asynchronous futures. Wait for them.
|
// Some of delegate.load() values were asynchronous futures. Wait for them.
|
||||||
return Future.wait<dynamic>(outputFutures).then<Map<Type, dynamic>>((List<dynamic> values) {
|
return Future.wait<dynamic>(pendingList.map((_Pending p) => p.futureValue))
|
||||||
for (dynamic value in values) {
|
.then<Map<Type, dynamic>>((List<dynamic> values) {
|
||||||
final Type type = value.runtimeType;
|
assert(values.length == pendingList.length);
|
||||||
|
for (int i = 0; i < values.length; i += 1) {
|
||||||
|
final Type type = pendingList[i].delegate.type;
|
||||||
assert(!output.containsKey(type));
|
assert(!output.containsKey(type));
|
||||||
output[type] = value;
|
output[type] = values[i];
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
});
|
});
|
||||||
@ -92,8 +102,10 @@ abstract class LocalizationsDelegate<T> {
|
|||||||
/// after [load] has completed.
|
/// after [load] has completed.
|
||||||
bool shouldReload(covariant LocalizationsDelegate<T> old);
|
bool shouldReload(covariant LocalizationsDelegate<T> old);
|
||||||
|
|
||||||
|
Type get type => T;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => '$runtimeType';
|
String toString() => '$runtimeType[$type]';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interface for localized resource values for the lowest levels of the Flutter
|
/// Interface for localized resource values for the lowest levels of the Flutter
|
||||||
@ -346,12 +358,8 @@ class _LocalizationsState extends State<Localizations> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Iterable<Future<dynamic>> allResources = delegates.map((LocalizationsDelegate<dynamic> delegate) {
|
|
||||||
return delegate.load(locale);
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<Type, dynamic> typeToResources;
|
Map<Type, dynamic> typeToResources;
|
||||||
final Future<Map<Type, dynamic>> typeToResourcesFuture = _loadAll(allResources)
|
final Future<Map<Type, dynamic>> typeToResourcesFuture = _loadAll(locale, delegates)
|
||||||
.then((Map<Type, dynamic> value) {
|
.then((Map<Type, dynamic> value) {
|
||||||
return typeToResources = value;
|
return typeToResources = value;
|
||||||
});
|
});
|
||||||
@ -383,7 +391,6 @@ class _LocalizationsState extends State<Localizations> {
|
|||||||
T resourcesFor<T>(Type type) {
|
T resourcesFor<T>(Type type) {
|
||||||
assert(type != null);
|
assert(type != null);
|
||||||
final T resources = _typeToResources[type];
|
final T resources = _typeToResources[type];
|
||||||
assert(resources.runtimeType == type);
|
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
52
packages/flutter/test/material/localizations_test.dart
Normal file
52
packages/flutter/test/material/localizations_test.dart
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
Widget buildFrame({
|
||||||
|
Locale locale,
|
||||||
|
WidgetBuilder buildContent,
|
||||||
|
}) {
|
||||||
|
return new MaterialApp(
|
||||||
|
color: const Color(0xFFFFFFFF),
|
||||||
|
locale: locale,
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return new MaterialPageRoute<Null>(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return buildContent(context);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final Key textKey = new UniqueKey();
|
||||||
|
|
||||||
|
testWidgets('sanity check', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
buildFrame(
|
||||||
|
buildContent: (BuildContext context) {
|
||||||
|
return new Text(
|
||||||
|
MaterialLocalizations.of(context).backButtonTooltip,
|
||||||
|
key: textKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tester.widget<Text>(find.byKey(textKey)).data, 'Back');
|
||||||
|
|
||||||
|
// Spanish Bolivia locale, falls back to just 'es'
|
||||||
|
await tester.binding.setLocale('es', 'bo');
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<Text>(find.byKey(textKey)).data, 'Espalda');
|
||||||
|
|
||||||
|
// Unrecognized locale falls back to 'en'
|
||||||
|
await tester.binding.setLocale('foo', 'bar');
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<Text>(find.byKey(textKey)).data, 'Back');
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user