[simple_browser][i18n] Adds i18n for simple_browser
Adds L10N support for simple_browser. Adds sample languages and
translations.
Screenshot with Dutch locale:
https://photos.app.goo.gl/cwwhvAcYZy8xoonB6
Bug: 35579
Bug: 39359
Change-Id: I613b8e5bf71b0e83c7ad57319a099fd8300b2d8a
diff --git a/bin/simple_browser/BUILD.gn b/bin/simple_browser/BUILD.gn
index 4030bd7..fcad778 100644
--- a/bin/simple_browser/BUILD.gn
+++ b/bin/simple_browser/BUILD.gn
@@ -21,18 +21,23 @@
manifest = "pubspec.yaml"
sources = [
+ "main.dart",
"src/blocs/tabs_bloc.dart",
"src/models/tabs_action.dart",
]
deps = [
+ "//sdk/fidl/fuchsia.intl",
"//sdk/fidl/fuchsia.web",
+ "//src/experiences/bin/simple_browser_internationalization:internationalization",
"//third_party/dart-pkg/git/flutter/packages/flutter",
"//third_party/dart-pkg/pub/html_unescape",
"//third_party/dart-pkg/pub/http",
+ "//third_party/dart/third_party/pkg/intl",
"//topaz/public/dart/fuchsia_logger",
"//topaz/public/dart/fuchsia_modular",
"//topaz/public/dart/fuchsia_scenic_flutter",
+ "//topaz/public/dart/fuchsia_services",
"//topaz/public/dart/widgets:lib.widgets",
"//topaz/public/lib/webview",
]
diff --git a/bin/simple_browser/lib/app.dart b/bin/simple_browser/lib/app.dart
index bf756f6..275ba13 100644
--- a/bin/simple_browser/lib/app.dart
+++ b/bin/simple_browser/lib/app.dart
@@ -4,6 +4,7 @@
import 'package:flutter/material.dart';
import 'package:fuchsia_scenic_flutter/child_view.dart' show ChildView;
+import 'package:internationalization/strings.dart';
import 'package:simple_browser/src/blocs/webpage_bloc.dart';
import 'src/blocs/tabs_bloc.dart';
import 'src/models/tabs_action.dart';
@@ -19,39 +20,56 @@
class App extends StatelessWidget {
final TabsBloc<WebPageBloc> tabsBloc;
- const App({@required this.tabsBloc});
+ final Locale locale;
+
+ final Iterable<LocalizationsDelegate<dynamic>> localizationsDelegates;
+
+ final Iterable<Locale> supportedLocales;
+
+ /// The [locale], [localizationsDelegates] and [supportedLocales] parameters
+ /// are the same as in [MaterialApp].
+ const App(
+ {@required this.tabsBloc,
+ this.locale,
+ this.localizationsDelegates,
+ this.supportedLocales});
@override
- Widget build(BuildContext context) => MaterialApp(
- title: 'Browser',
- theme: ThemeData(
- fontFamily: 'RobotoMono',
- textSelectionColor: _kSelectionColor,
- textSelectionHandleColor: _kForegroundColor,
- hintColor: _kForegroundColor,
- cursorColor: _kForegroundColor,
- primaryColor: _kBackgroundColor,
- canvasColor: _kBackgroundColor,
- accentColor: _kForegroundColor,
- textTheme: TextTheme(
- body1: _kTextStyle,
- subhead: _kTextStyle,
- ),
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: Strings.browser,
+ theme: ThemeData(
+ fontFamily: 'RobotoMono',
+ textSelectionColor: _kSelectionColor,
+ textSelectionHandleColor: _kForegroundColor,
+ hintColor: _kForegroundColor,
+ cursorColor: _kForegroundColor,
+ primaryColor: _kBackgroundColor,
+ canvasColor: _kBackgroundColor,
+ accentColor: _kForegroundColor,
+ textTheme: TextTheme(
+ body1: _kTextStyle,
+ subhead: _kTextStyle,
),
- home: Scaffold(
- body: Column(
- children: <Widget>[
- AnimatedBuilder(
- animation: tabsBloc.currentTabNotifier,
- builder: (_, __) =>
- NavigationBar(bloc: tabsBloc.currentTab, newTab: newTab),
- ),
- TabsWidget(bloc: tabsBloc),
- Expanded(child: _buildContent()),
- ],
- ),
+ ),
+ locale: locale,
+ localizationsDelegates: localizationsDelegates,
+ supportedLocales: supportedLocales,
+ home: Scaffold(
+ body: Column(
+ children: <Widget>[
+ AnimatedBuilder(
+ animation: tabsBloc.currentTabNotifier,
+ builder: (_, __) =>
+ NavigationBar(bloc: tabsBloc.currentTab, newTab: newTab),
+ ),
+ TabsWidget(bloc: tabsBloc),
+ Expanded(child: _buildContent()),
+ ],
),
- );
+ ),
+ );
+ }
Widget _buildContent() => AnimatedBuilder(
animation: tabsBloc.currentTabNotifier,
diff --git a/bin/simple_browser/lib/main.dart b/bin/simple_browser/lib/main.dart
index 21ecc0f..47558d7 100644
--- a/bin/simple_browser/lib/main.dart
+++ b/bin/simple_browser/lib/main.dart
@@ -3,12 +3,24 @@
// found in the LICENSE file.
import 'dart:convert';
+
import 'package:flutter/material.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+
+import 'package:fidl_fuchsia_intl/fidl_async.dart';
+import 'package:fuchsia_internationalization_flutter/internationalization.dart';
import 'package:fuchsia_logger/logger.dart';
import 'package:fuchsia_modular/module.dart' as modular;
+import 'package:fuchsia_services/services.dart';
+import 'package:internationalization/localizations_delegate.dart'
+ as localizations;
+import 'package:internationalization/supported_locales.dart'
+ as supported_locales;
+import 'package:intl/intl.dart';
import 'package:simple_browser/src/models/tabs_action.dart';
import 'package:simple_browser/src/models/webpage_action.dart';
import 'package:webview/webview.dart';
+
import 'app.dart';
import 'src/blocs/tabs_bloc.dart';
import 'src/blocs/webpage_bloc.dart';
@@ -50,5 +62,45 @@
},
);
modular.Module().registerIntentHandler(RootIntentHandler(tabsBloc));
- runApp(App(tabsBloc: tabsBloc));
+
+ final _intl = PropertyProviderProxy();
+ StartupContext.fromStartupInfo().incoming.connectToService(_intl);
+
+ final locales = LocaleSource(_intl);
+
+ runApp(Localized(tabsBloc, locales.stream()));
+}
+
+/// This is a localized version of the browser app. It is the same as the
+/// original App, but it also has the current locale injected.
+class Localized extends StatelessWidget {
+ // The tabs bloc to use for the underlying widget.
+ final TabsBloc _tabsBloc;
+
+ // The stream of locale updates.
+ final Stream<Locale> _localeStream;
+
+ const Localized(this._tabsBloc, this._localeStream);
+
+ @override
+ Widget build(BuildContext context) {
+ return StreamBuilder<Locale>(
+ stream: _localeStream,
+ builder: (BuildContext context, AsyncSnapshot<Locale> snapshot) {
+ final Locale locale = snapshot.data;
+ // This is required so app parts which don't depend on the flutter
+ // locale have access to it.
+ Intl.defaultLocale = locale.toString();
+ return App(
+ tabsBloc: _tabsBloc,
+ locale: locale,
+ localizationsDelegates: [
+ localizations.delegate(),
+ GlobalMaterialLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ ],
+ supportedLocales: supported_locales.locales,
+ );
+ });
+ }
}
diff --git a/bin/simple_browser/lib/src/widgets/history_buttons.dart b/bin/simple_browser/lib/src/widgets/history_buttons.dart
index 24fe462..82b08f1 100644
--- a/bin/simple_browser/lib/src/widgets/history_buttons.dart
+++ b/bin/simple_browser/lib/src/widgets/history_buttons.dart
@@ -3,6 +3,8 @@
// found in the LICENSE file.
import 'package:flutter/material.dart';
+import 'package:internationalization/strings.dart';
+
import '../blocs/webpage_bloc.dart';
import '../models/webpage_action.dart';
@@ -16,31 +18,33 @@
final WebPageBloc bloc;
@override
- Widget build(BuildContext context) => Row(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: <Widget>[
- AnimatedBuilder(
- animation: bloc.backStateNotifier,
- builder: (_, __) => _HistoryButton(
- title: 'BCK',
- onTap: () => bloc.request.add(GoBackAction()),
- isEnabled: bloc.backState)),
- SizedBox(width: 8.0),
- AnimatedBuilder(
- animation: bloc.forwardStateNotifier,
- builder: (_, __) => _HistoryButton(
- title: 'FWD',
- onTap: () => bloc.request.add(GoForwardAction()),
- isEnabled: bloc.forwardState)),
- SizedBox(width: 8.0),
- AnimatedBuilder(
- animation: bloc.urlNotifier,
- builder: (_, __) => _HistoryButton(
- title: 'RFRSH',
- onTap: () => bloc.request.add(RefreshAction()),
- isEnabled: bloc.pageType == PageType.normal)),
- ],
- );
+ Widget build(BuildContext context) {
+ return Row(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: <Widget>[
+ AnimatedBuilder(
+ animation: bloc.backStateNotifier,
+ builder: (_, __) => _HistoryButton(
+ title: Strings.back.toUpperCase(),
+ onTap: () => bloc.request.add(GoBackAction()),
+ isEnabled: bloc.backState)),
+ SizedBox(width: 8.0),
+ AnimatedBuilder(
+ animation: bloc.forwardStateNotifier,
+ builder: (_, __) => _HistoryButton(
+ title: Strings.forward.toUpperCase(),
+ onTap: () => bloc.request.add(GoForwardAction()),
+ isEnabled: bloc.forwardState)),
+ SizedBox(width: 8.0),
+ AnimatedBuilder(
+ animation: bloc.urlNotifier,
+ builder: (_, __) => _HistoryButton(
+ title: Strings.refresh.toUpperCase(),
+ onTap: () => bloc.request.add(RefreshAction()),
+ isEnabled: bloc.pageType == PageType.normal)),
+ ],
+ );
+ }
}
class _HistoryButton extends StatelessWidget {
diff --git a/bin/simple_browser/lib/src/widgets/navigation_field.dart b/bin/simple_browser/lib/src/widgets/navigation_field.dart
index 014eda2..92497a5 100644
--- a/bin/simple_browser/lib/src/widgets/navigation_field.dart
+++ b/bin/simple_browser/lib/src/widgets/navigation_field.dart
@@ -3,6 +3,8 @@
// found in the LICENSE file.
import 'package:flutter/material.dart';
+import 'package:internationalization/strings.dart';
+
import '../blocs/webpage_bloc.dart';
import '../models/webpage_action.dart';
@@ -82,10 +84,9 @@
keyboardType: TextInputType.url,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
- // Extra spaces before SEARCH are meant to visually center the S,
- // so the cursor's position will sit in the beginning of the hint,
- // rather than halfway between the A and the R.
- hintText: ' SEARCH',
+ // In general, do not use space characters to move graphical elements
+ // around, or you are going to have a bad time. :)
+ hintText: ' ${Strings.search.toUpperCase()}',
border: InputBorder.none,
isDense: true,
),
diff --git a/bin/simple_browser/meta/simple_browser.cmx b/bin/simple_browser/meta/simple_browser.cmx
index 93d3f6c..b87a486 100644
--- a/bin/simple_browser/meta/simple_browser.cmx
+++ b/bin/simple_browser/meta/simple_browser.cmx
@@ -24,23 +24,24 @@
"fuchsia.cobalt.LoggerFactory",
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.fonts.Provider",
+ "fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",
"fuchsia.media.Audio",
"fuchsia.mediacodec.CodecFactory",
"fuchsia.modular.Clipboard",
+ "fuchsia.modular.ComponentContext",
"fuchsia.modular.ModuleContext",
- "fuchsia.posix.socket.Provider",
"fuchsia.net.NameLookup",
"fuchsia.netstack.Netstack",
+ "fuchsia.posix.socket.Provider",
"fuchsia.process.Launcher",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.sysmem.Allocator",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.input.ImeVisibilityService",
- "fuchsia.ui.scenic.Scenic",
"fuchsia.ui.policy.Presenter",
- "fuchsia.modular.ComponentContext",
+ "fuchsia.ui.scenic.Scenic",
"fuchsia.vulkan.loader.Loader",
"fuchsia.web.ContextProvider"
]
diff --git a/bin/simple_browser/test/simple_browser_test.dart b/bin/simple_browser/test/simple_browser_test.dart
index 6f19acc..f2ba8f8 100644
--- a/bin/simple_browser/test/simple_browser_test.dart
+++ b/bin/simple_browser/test/simple_browser_test.dart
@@ -4,13 +4,19 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
-import 'package:test/test.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:fuchsia_logger/logger.dart';
// ignore_for_file: implementation_imports
+import 'package:simple_browser/main.dart' show Localized;
import 'package:simple_browser/src/blocs/tabs_bloc.dart';
+import 'package:simple_browser/src/blocs/webpage_bloc.dart';
import 'package:simple_browser/src/models/tabs_action.dart';
void main() {
+ setupLogger(name: 'simple_browser_test');
+
// Add a tab, and see that there is 1 tab
test('add 1 tab', () async {
final tb = TabsBloc(
@@ -58,6 +64,28 @@
);
expect(tb.currentTab, 'tab 0', reason: 'unexpected tab content');
});
+
+ testWidgets('localized text is displayed in the widgets',
+ (WidgetTester tester) async {
+ var locales = Stream.fromIterable(
+ [Locale.fromSubtags(languageCode: 'sr', countryCode: 'RS')]);
+ TabsBloc<WebPageBloc> tabsBloc;
+ tabsBloc = TabsBloc(
+ tabFactory: () => WebPageBloc(
+ popupHandler: (tab) =>
+ tabsBloc.request.add(AddTabAction<WebPageBloc>(tab: tab))),
+ disposeTab: (tab) {
+ tab.dispose();
+ },
+ );
+ await tester.pumpWidget(Localized(tabsBloc, locales));
+ await tester.pump();
+ expect(
+ find.byWidgetPredicate(
+ (Widget widget) => widget is Title && widget.title == 'Прегледач',
+ description: 'A widget with a localized title was displayed'),
+ findsOneWidget);
+ });
}
/// awaits for a single callback from a [Listenable]
diff --git a/bin/simple_browser_internationalization/BUILD.gn b/bin/simple_browser_internationalization/BUILD.gn
new file mode 100644
index 0000000..7f3f54c
--- /dev/null
+++ b/bin/simple_browser_internationalization/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2019 The Fuchsia 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("//build/dart/dart_library.gni")
+import("//src/modular/build/modular_config/modular_config.gni")
+import("//topaz/runtime/dart/flutter_test.gni")
+import("//topaz/runtime/flutter_runner/flutter_app.gni")
+
+dart_library("internationalization") {
+ package_name = "internationalization"
+
+ # Disabling analysis for the time being, this is mostly generated code to begin
+ # with, and there are issues like this one that make things harder than necessary:
+ # https://github.com/dart-lang/sdk/issues/38598.
+ disable_analysis = true
+
+ sources = [
+ "localization/messages_all.dart",
+ "localization/messages_messages.dart",
+ "localization/messages_nl.dart",
+ "localization/messages_sr.dart",
+ "localizations_delegate.dart",
+ "strings.dart",
+ "supported_locales.dart",
+ ]
+
+ deps = [
+ "//third_party/dart-pkg/git/flutter/packages/flutter",
+ "//third_party/dart-pkg/git/flutter/packages/flutter_localizations",
+ "//third_party/dart/third_party/pkg/intl",
+ "//topaz/public/dart/fuchsia_internationalization_flutter",
+ "//topaz/public/dart/widgets:lib.widgets",
+ ]
+}
diff --git a/bin/simple_browser_internationalization/README.md b/bin/simple_browser_internationalization/README.md
new file mode 100644
index 0000000..f30004e
--- /dev/null
+++ b/bin/simple_browser_internationalization/README.md
@@ -0,0 +1,42 @@
+# `simple_browser` internationalization and localization
+
+This Dart package contains the support for `simple_browser` localization and
+internationalization.
+
+At its current state the support is in its very early phase. There is a lot of
+manual scaffolding that could be replaced by auto-generating the code of
+interest. Doing so will be the topic of followup work.
+
+# How to use this package
+
+This package is purpose-built for the `simple_browser`. The central part is
+the file `lib/strings.dart` which by design contains all the strings that the
+simple browser needs to vary based on the system locale.
+
+Add a function to this file whenever you need to introduce a new text message.
+
+## Translating
+
+The translation process amounts to making ARB files for each of the supported
+locales of interest and providing the translations for the messages and
+strings. The basic tool for doing so would be a simple text editor. While
+there also exists a wealth of tools that help software translators, it is out
+of scope of this file to go into specific details. So the choice of the
+translation environment is left to the developer and translator.
+
+## Generating Dart runtime format for the translations
+
+Once translated you can use the file `./scripts/run_generate_from_arb.sh` from
+`fuchsia_internationalization` library to generate the Dart code that wires up
+the translation. You will need to rebuild the entire project to take the new
+translations in.
+
+# Further reading
+
+Internationalization and localization are topic unto themselves, and it is out
+of scope of this file to go into all the details. Please see the section
+on [Internationalizing Flutter apps][1] for many more details that are directly
+relevant to Dart and Flutter app localization.
+
+[1]: https://flutter.dev/docs/development/accessibility-and-localization/internationalization
+[arb]: https://github.com/google/app-resource-bundle
diff --git a/bin/simple_browser_internationalization/analysis_options.yaml b/bin/simple_browser_internationalization/analysis_options.yaml
new file mode 100644
index 0000000..86fb3da
--- /dev/null
+++ b/bin/simple_browser_internationalization/analysis_options.yaml
@@ -0,0 +1,6 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+include: ../../../analysis_options.yaml
+
diff --git a/bin/simple_browser_internationalization/lib/localization/messages_all.dart b/bin/simple_browser_internationalization/lib/localization/messages_all.dart
new file mode 100644
index 0000000..1bc2700
--- /dev/null
+++ b/bin/simple_browser_internationalization/lib/localization/messages_all.dart
@@ -0,0 +1,70 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that looks up messages for specific locales by
+// delegating to the appropriate library.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:implementation_imports, file_names, unnecessary_new
+// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
+// ignore_for_file:argument_type_not_assignable, invalid_assignment
+// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
+// ignore_for_file:comment_references
+
+import 'dart:async';
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+import 'package:intl/src/intl_helpers.dart';
+
+import 'messages_messages.dart' deferred as messages_messages;
+import 'messages_nl.dart' deferred as messages_nl;
+import 'messages_sr.dart' deferred as messages_sr;
+
+typedef Future<dynamic> LibraryLoader();
+Map<String, LibraryLoader> _deferredLibraries = {
+ 'messages': messages_messages.loadLibrary,
+ 'nl': messages_nl.loadLibrary,
+ 'sr': messages_sr.loadLibrary,
+};
+
+MessageLookupByLibrary _findExact(String localeName) {
+ switch (localeName) {
+ case 'messages':
+ return messages_messages.messages;
+ case 'nl':
+ return messages_nl.messages;
+ case 'sr':
+ return messages_sr.messages;
+ default:
+ return null;
+ }
+}
+
+/// User programs should call this before using [localeName] for messages.
+Future<bool> initializeMessages(String localeName) async {
+ var availableLocale = Intl.verifiedLocale(
+ localeName, (locale) => _deferredLibraries[locale] != null,
+ onFailure: (_) => null);
+ if (availableLocale == null) {
+ return new Future.value(false);
+ }
+ var lib = _deferredLibraries[availableLocale];
+ await (lib == null ? new Future.value(false) : lib());
+ initializeInternalMessageLookup(() => new CompositeMessageLookup());
+ messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
+ return new Future.value(true);
+}
+
+bool _messagesExistFor(String locale) {
+ try {
+ return _findExact(locale) != null;
+ } catch (e) {
+ return false;
+ }
+}
+
+MessageLookupByLibrary _findGeneratedMessagesFor(String locale) {
+ var actualLocale =
+ Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
+ if (actualLocale == null) return null;
+ return _findExact(actualLocale);
+}
diff --git a/bin/simple_browser_internationalization/lib/localization/messages_messages.dart b/bin/simple_browser_internationalization/lib/localization/messages_messages.dart
new file mode 100644
index 0000000..c60187c
--- /dev/null
+++ b/bin/simple_browser_internationalization/lib/localization/messages_messages.dart
@@ -0,0 +1,30 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that provides messages for a messages locale. All the
+// messages from the main program should be duplicated here with the same
+// function name.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
+// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
+// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
+// ignore_for_file:unused_import, file_names
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+
+final messages = new MessageLookup();
+
+typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
+
+class MessageLookup extends MessageLookupByLibrary {
+ String get localeName => 'messages';
+
+ final messages = _notInlinedMessages(_notInlinedMessages);
+ static _notInlinedMessages(_) => <String, Function>{
+ "back": MessageLookupByLibrary.simpleMessage("Bck"),
+ "browser": MessageLookupByLibrary.simpleMessage("Browser"),
+ "forward": MessageLookupByLibrary.simpleMessage("Fwd"),
+ "refresh": MessageLookupByLibrary.simpleMessage("Rfrsh"),
+ "search": MessageLookupByLibrary.simpleMessage("Search")
+ };
+}
diff --git a/bin/simple_browser_internationalization/lib/localization/messages_nl.dart b/bin/simple_browser_internationalization/lib/localization/messages_nl.dart
new file mode 100644
index 0000000..f458900
--- /dev/null
+++ b/bin/simple_browser_internationalization/lib/localization/messages_nl.dart
@@ -0,0 +1,30 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that provides messages for a nl locale. All the
+// messages from the main program should be duplicated here with the same
+// function name.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
+// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
+// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
+// ignore_for_file:unused_import, file_names
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+
+final messages = new MessageLookup();
+
+typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
+
+class MessageLookup extends MessageLookupByLibrary {
+ String get localeName => 'nl';
+
+ final messages = _notInlinedMessages(_notInlinedMessages);
+ static _notInlinedMessages(_) => <String, Function>{
+ "back": MessageLookupByLibrary.simpleMessage("Trg"),
+ "browser": MessageLookupByLibrary.simpleMessage("Browser"),
+ "forward": MessageLookupByLibrary.simpleMessage("Vor"),
+ "refresh": MessageLookupByLibrary.simpleMessage("Vrvrs"),
+ "search": MessageLookupByLibrary.simpleMessage("Zoek")
+ };
+}
diff --git a/bin/simple_browser_internationalization/lib/localization/messages_sr.dart b/bin/simple_browser_internationalization/lib/localization/messages_sr.dart
new file mode 100644
index 0000000..2c1e744
--- /dev/null
+++ b/bin/simple_browser_internationalization/lib/localization/messages_sr.dart
@@ -0,0 +1,30 @@
+// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+// This is a library that provides messages for a sr locale. All the
+// messages from the main program should be duplicated here with the same
+// function name.
+
+// Ignore issues from commonly used lints in this file.
+// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
+// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
+// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
+// ignore_for_file:unused_import, file_names
+
+import 'package:intl/intl.dart';
+import 'package:intl/message_lookup_by_library.dart';
+
+final messages = new MessageLookup();
+
+typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
+
+class MessageLookup extends MessageLookupByLibrary {
+ String get localeName => 'sr';
+
+ final messages = _notInlinedMessages(_notInlinedMessages);
+ static _notInlinedMessages(_) => <String, Function>{
+ "back": MessageLookupByLibrary.simpleMessage("Наз"),
+ "browser": MessageLookupByLibrary.simpleMessage("Прегледач"),
+ "forward": MessageLookupByLibrary.simpleMessage("Нпр"),
+ "refresh": MessageLookupByLibrary.simpleMessage("Освж"),
+ "search": MessageLookupByLibrary.simpleMessage("Претрага")
+ };
+}
diff --git a/bin/simple_browser_internationalization/lib/localizations_delegate.dart b/bin/simple_browser_internationalization/lib/localizations_delegate.dart
new file mode 100644
index 0000000..f8d782d
--- /dev/null
+++ b/bin/simple_browser_internationalization/lib/localizations_delegate.dart
@@ -0,0 +1,47 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Should the localizations delegate be generated instead?
+
+import 'dart:async';
+import 'dart:ui' show Locale;
+
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'package:fuchsia_logger/logger.dart';
+
+import 'localization/messages_all.dart' as messages_all;
+import 'supported_locales.dart' as supported_locales;
+
+LocalizationsDelegate<void> delegate() => _LocalizationsDelegate();
+
+class _LocalizationsDelegate extends LocalizationsDelegate<void> {
+ static Future<void> loadLocale(Locale locale) async {
+ final String name =
+ (locale.countryCode == null || locale.countryCode.isEmpty)
+ ? locale.languageCode
+ : locale.toString();
+ final String localeName = Intl.canonicalizedLocale(name);
+ await messages_all.initializeMessages(localeName);
+ log.info(
+ '_LocalizationsDelegate: current default locale: ${Intl.defaultLocale}');
+ }
+
+ const _LocalizationsDelegate();
+
+ @override
+ Future<void> load(Locale locale) => loadLocale(locale);
+
+ // For the time being, never reload.
+ @override
+ bool shouldReload(_LocalizationsDelegate __) => false;
+
+ @override
+ bool isSupported(Locale locale) {
+ bool supported = supported_locales.locales.contains(locale);
+ log.finer(
+ '_LocalizationsDelegate: locale: ${locale.toString()}; isSupported: ${supported.toString()}');
+ return supported;
+ }
+}
diff --git a/bin/simple_browser_internationalization/lib/strings.dart b/bin/simple_browser_internationalization/lib/strings.dart
new file mode 100644
index 0000000..7c4b6ba
--- /dev/null
+++ b/bin/simple_browser_internationalization/lib/strings.dart
@@ -0,0 +1,48 @@
+// Copyright 2019 The Fuchsia 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 is an L10N developer experience study fragment.
+// Let's see how it works when you offload all strings to a file.
+
+// The Intl.message texts have been offloaded to a separate file so as to
+// minimize the amount of code needed to be exported for localization.
+// While the individual static String get messages have been left in their original form,
+// the case-ness of the text is part of the presentation so should probably be
+// handled in the view code.
+
+import 'package:intl/intl.dart';
+
+/// Provides access to localized strings used in Ermine code.
+class Strings {
+ static final Strings instance = Strings._internal();
+ factory Strings() => instance;
+ Strings._internal();
+
+ static String get back => Intl.message(
+ 'Bck',
+ name: 'back',
+ desc: 'A very short label meaning "Go back (to previous web page)"',
+ );
+ static String get forward => Intl.message(
+ 'Fwd',
+ name: 'forward',
+ desc: 'A very short label meaning "Go forward (to the next web page)"',
+ );
+ static String get refresh => Intl.message(
+ 'Rfrsh',
+ name: 'refresh',
+ desc: 'A very short label meaning "Refresh the web page"',
+ );
+ static String get search => Intl.message(
+ 'Search',
+ name: 'search',
+ desc:
+ 'A regular length string label appearing in the browser search bar',
+ );
+ static String get browser => Intl.message(
+ 'Browser',
+ name: 'browser',
+ desc: 'As in: web browser',
+ );
+}
diff --git a/bin/simple_browser_internationalization/lib/supported_locales.dart b/bin/simple_browser_internationalization/lib/supported_locales.dart
new file mode 100644
index 0000000..84d978f
--- /dev/null
+++ b/bin/simple_browser_internationalization/lib/supported_locales.dart
@@ -0,0 +1,10 @@
+// Copyright 2019 The Fuchsia 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 'dart:ui' show Locale;
+
+List<Locale> locales = [
+ const Locale.fromSubtags(languageCode: 'sr'),
+ const Locale.fromSubtags(languageCode: 'nl'),
+];
diff --git a/bin/simple_browser_internationalization/pubspec.yaml b/bin/simple_browser_internationalization/pubspec.yaml
new file mode 100644
index 0000000..4c61966
--- /dev/null
+++ b/bin/simple_browser_internationalization/pubspec.yaml
@@ -0,0 +1,12 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+name: internationalization
+description: The internationalization library for Ermine.
+
+flutter:
+
+dependencies:
+ # Needed for the ARB extraction scripts until this is fully automated.
+ intl_translation: ^0.17.2
+
diff --git a/bin/simple_browser_internationalization/resources/intl_messages.arb b/bin/simple_browser_internationalization/resources/intl_messages.arb
new file mode 100644
index 0000000..9556f92
--- /dev/null
+++ b/bin/simple_browser_internationalization/resources/intl_messages.arb
@@ -0,0 +1,33 @@
+{
+ "@@last_modified": "2019-11-11T17:42:01.328995",
+ "back": "Bck",
+ "@back": {
+ "description": "A very short label meaning \"Go back (to previous web page)\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "forward": "Fwd",
+ "@forward": {
+ "description": "A very short label meaning \"Go forward (to the next web page)\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "refresh": "Rfrsh",
+ "@refresh": {
+ "description": "A very short label meaning \"Refresh the web page\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "search": "Search",
+ "@search": {
+ "description": "A regular length string label appearing in the browser search bar",
+ "type": "text",
+ "placeholders": {}
+ },
+ "browser": "Browser",
+ "@browser": {
+ "description": "As in: web browser",
+ "type": "text",
+ "placeholders": {}
+ }
+}
diff --git a/bin/simple_browser_internationalization/resources/intl_nl.arb b/bin/simple_browser_internationalization/resources/intl_nl.arb
new file mode 100644
index 0000000..9728252
--- /dev/null
+++ b/bin/simple_browser_internationalization/resources/intl_nl.arb
@@ -0,0 +1,33 @@
+{
+ "@@last_modified": "2019-11-06T17:31:05.454814",
+ "back": "Trg",
+ "@back": {
+ "description": "A very short label meaning \"Go back (to previous web page)\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "forward": "Vor",
+ "@forward": {
+ "description": "A very short label meaning \"Go forward (to the next web page)\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "refresh": "Vrvrs",
+ "@refresh": {
+ "description": "A very short label meaning \"Refresh the web page\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "search": "Zoek",
+ "@search": {
+ "description": "A regular length string label appearing in the browser search bar",
+ "type": "text",
+ "placeholders": {}
+ },
+ "browser": "Browser",
+ "@browser": {
+ "description": "As in: web browser",
+ "type": "text",
+ "placeholders": {}
+ }
+}
diff --git a/bin/simple_browser_internationalization/resources/intl_sr.arb b/bin/simple_browser_internationalization/resources/intl_sr.arb
new file mode 100644
index 0000000..7aca5cd
--- /dev/null
+++ b/bin/simple_browser_internationalization/resources/intl_sr.arb
@@ -0,0 +1,33 @@
+{
+ "@@last_modified": "2019-11-06T17:31:05.454814",
+ "back": "Наз",
+ "@back": {
+ "description": "A very short label meaning \"Go back (to previous web page)\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "forward": "Нпр",
+ "@forward": {
+ "description": "A very short label meaning \"Go forward (to the next web page)\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "refresh": "Освж",
+ "@refresh": {
+ "description": "A very short label meaning \"Refresh the web page\"",
+ "type": "text",
+ "placeholders": {}
+ },
+ "search": "Претрага",
+ "@search": {
+ "description": "A regular length string label appearing in the browser search bar",
+ "type": "text",
+ "placeholders": {}
+ },
+ "browser": "Прегледач",
+ "@browser": {
+ "description": "As in: web browser",
+ "type": "text",
+ "placeholders": {}
+ }
+}
diff --git a/bin/simple_browser_internationalization/scripts/run_extract_to_arb.sh b/bin/simple_browser_internationalization/scripts/run_extract_to_arb.sh
new file mode 100755
index 0000000..d847f84
--- /dev/null
+++ b/bin/simple_browser_internationalization/scripts/run_extract_to_arb.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+set -x
+
+$FUCHSIA_DIR/third_party/dart-pkg/git/flutter/bin/flutter \
+ packages pub get intl_translation:extract_to_arb
+
+$FUCHSIA_DIR/third_party/dart-pkg/git/flutter/bin/flutter \
+ packages pub run intl_translation:extract_to_arb \
+ --output-dir=resources lib/*strings.dart
+
+rm .packages pubspec.lock
+rm -fr .dart_tool
diff --git a/bin/simple_browser_internationalization/scripts/run_generate_from_arb.sh b/bin/simple_browser_internationalization/scripts/run_generate_from_arb.sh
new file mode 100755
index 0000000..93f7b81
--- /dev/null
+++ b/bin/simple_browser_internationalization/scripts/run_generate_from_arb.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+set -x
+
+$FUCHSIA_DIR/third_party/dart-pkg/git/flutter/bin/flutter \
+ packages pub get intl_translation:generate_from_arb
+
+$FUCHSIA_DIR/third_party/dart-pkg/git/flutter/bin/flutter \
+ packages pub run intl_translation:generate_from_arb \
+ --output-dir=lib/localization \
+ lib/*strings.dart \
+ resources/*.arb
+
+rm .packages pubspec.lock
+rm -fr .dart_tool