app localization structure

This commit is contained in:
Daniel Borges 2019-08-08 15:59:20 -03:00
parent 9270ade460
commit fec650f970
17 changed files with 258 additions and 79 deletions

43
lang/en.json Normal file
View File

@ -0,0 +1,43 @@
{
"tab_service": "SERVICE",
"tab_slides": "SLIDES",
"floating_button_search": "New service item",
"button_cancel": "CANCEL",
"button_show": "SHOW",
"button_ok": "OK",
"alert": "Alert",
"dialog_alert_title": "Type your alert",
"display": "Display",
"display_option_blank": "Blank screen",
"display_option_theme": "Theme background",
"display_option_desktop": "Desktop",
"display_option_show": "Full projection",
"plugin_songs_singular": "Song",
"plugin_songs_plural": "Songs",
"plugin_bibles_singular": "Bible",
"plugin_bibles_plural": "Bibles",
"plugin_presentations_singular": "Presentation",
"plugin_presentations_plural": "Presentations",
"plugin_images_singular": "Image",
"plugin_images_plural": "Images",
"plugin_media_singular": "Media",
"plugin_media_plural": "Medias",
"plugin_custom_singular": "Custom",
"plugin_custom_plural": "Customs",
"search_results_go": "Go live",
"search_results_add": "Add to service",
"search_results_add_and_go": "Add & Go to service",
"service_list_empty": "Any item added to service.\nPlease, add a new service item tapping the add button.",
"settings": "Settings",
"settings_server_ip": "Server IP",
"settings_server_port": "Server port",
"settings_use_https": "User HTTPS",
"settings_needs_auth": "Needs auth",
"settings_user_id": "User ID",
"settings_user_pass": "User password",
"settings_about_openlp": "About OpenLP",
"dialog_server_ip_title": "Type the IP",
"dialog_server_port_title": "Type the port",
"dialog_server_user_id_title": "Type the ID",
"dialog_server_user_pass_title": "Type the password"
}

3
lang/pt.json Normal file
View File

@ -0,0 +1,3 @@
{
"tab_service_label": "CULTO"
}

View File

@ -21,15 +21,36 @@
// DEALINGS IN THE SOFTWARE.
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'src/openlp_mobile_remote_app.dart';
import 'src/app_theme.dart';
import 'src/configurations/app_theme.dart';
import 'src/configurations/app_localizations.dart';
import 'src/screens/settings.dart';
void main() => runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
theme: appTheme,
supportedLocales: [
Locale('en'),
Locale('pt'),
],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode) {
// if (supportedLocale.countryCode == locale.countryCode) {
return supportedLocale;
// }
}
}
return supportedLocales.first;
},
routes: <String, WidgetBuilder>{
'/': (context) => OpenLPMobileRemoteApp(),
'/settings': (context) => Settings(),

View File

@ -0,0 +1,56 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AppLocalizations {
final Locale locale;
AppLocalizations(this.locale);
static AppLocalizations of(BuildContext context) =>
Localizations.of<AppLocalizations>(context, AppLocalizations);
Map<String, String> _localizedString;
Future<void> load() async {
String jsonFileName = 'lang/${locale.languageCode}';
if (locale.countryCode != null) {
jsonFileName += '_${locale.countryCode}';
}
jsonFileName += '.json';
String mainJsonString = await rootBundle.loadString('lang/en.json');
Map<String, dynamic> jsonMap = json.decode(mainJsonString);
String localizedJsonString = await rootBundle.loadString(jsonFileName);
jsonMap.addAll(json.decode(localizedJsonString));
_localizedString =
jsonMap.map((key, value) => MapEntry(key, value.toString()));
}
String translate(String key) => _localizedString[key];
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) => true;
@override
Future<AppLocalizations> load(Locale locale) async {
AppLocalizations localizations = new AppLocalizations(locale);
await localizations.load();
return localizations;
}
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}

View File

@ -22,6 +22,8 @@
import 'package:flutter/material.dart';
import '../resources/openlp_icons.dart';
class Plugin {
final String id;
@ -30,17 +32,17 @@ class Plugin {
IconData icon() {
switch (this.id) {
case 'songs':
return Icons.audiotrack;
return OpenLPIcons.songs;
case 'bibles':
return Icons.book;
return OpenLPIcons.biblies;
case 'presentations':
return Icons.present_to_all;
return OpenLPIcons.presentations;
case 'images':
return Icons.image;
return OpenLPIcons.images;
case 'media':
return Icons.ondemand_video;
return OpenLPIcons.media;
case 'custom':
return Icons.video_label;
return OpenLPIcons.custom;
}
return Icons.screen_share;
}

View File

@ -21,6 +21,7 @@
// DEALINGS IN THE SOFTWARE.
import 'package:flutter/material.dart';
import 'package:openlp_remote/src/configurations/app_localizations.dart';
import 'widgets/bottom_navigation_bar.dart';
import 'widgets/search_floating_button.dart';
@ -58,8 +59,8 @@ class _OpenLPMobileRemoteAppState extends State<OpenLPMobileRemoteApp>
],
bottom: TabBar(
tabs: <Widget>[
Tab(text: 'SERVICE'),
Tab(text: 'SLIDES'),
Tab(text: AppLocalizations.of(context).translate('tab_service')),
Tab(text: AppLocalizations.of(context).translate('tab_slides')),
],
controller: tabController,
),

View File

@ -23,9 +23,10 @@
import 'package:flutter/material.dart';
class OpenLPIcons {
final IconData songs = Icons.audiotrack;
final IconData biblies = Icons.book;
final IconData presentations = Icons.present_to_all;
final IconData media = Icons.ondemand_video;
final IconData custom = Icons.video_label;
static final IconData songs = Icons.audiotrack;
static final IconData biblies = Icons.book;
static final IconData images = Icons.image;
static final IconData presentations = Icons.present_to_all;
static final IconData media = Icons.ondemand_video;
static final IconData custom = Icons.video_label;
}

View File

@ -22,6 +22,8 @@
import 'package:flutter/material.dart';
import '../configurations/app_localizations.dart';
class SearchServiceItem extends SearchDelegate {
@override
List<Widget> buildActions(BuildContext context) {
@ -54,20 +56,23 @@ class SearchServiceItem extends SearchDelegate {
),
),
ListTile(
title: Text('Go live'),
title: Text(
AppLocalizations.of(context).translate('search_results_go')),
onTap: () {
Navigator.of(context).pop();
close(context, null);
},
),
ListTile(
title: Text('Add to service'),
title: Text(
AppLocalizations.of(context).translate('search_results_add')),
onTap: () {
Navigator.of(context).pop();
},
),
ListTile(
title: Text('Add & Go to service'),
title: Text(AppLocalizations.of(context)
.translate('search_results_add_and_go')),
onTap: () {
Navigator.of(context).pop();
close(context, null);

View File

@ -22,6 +22,7 @@
import 'package:flutter/material.dart';
import '../configurations/app_localizations.dart';
import '../models/service_item.dart';
class ServiceListView extends StatefulWidget {
@ -64,8 +65,7 @@ class _ServiceListViewState extends State<ServiceListView> {
maxWidth: 250,
),
child: Text(
'Any item added to service.\n' +
'Please, add a new service item tapping the "New service item" button.',
AppLocalizations.of(context).translate('service_list_empty'),
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black38),
),
@ -94,7 +94,8 @@ class _ServiceListViewState extends State<ServiceListView> {
),
),
title: Text(item.title),
subtitle: Text(item.plugin.id),
subtitle: Text(AppLocalizations.of(context)
.translate('plugin_${item.plugin.id}_singular')),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {},

View File

@ -23,6 +23,7 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../configurations/app_localizations.dart';
class Settings extends StatefulWidget {
@override
@ -52,32 +53,37 @@ class _SettingState extends State<Settings> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Settings'),
title: Text(AppLocalizations.of(context).translate('settings')),
),
body: ListView(
children: <Widget>[
ListTile(
title: Text('Server IP'),
title: Text(
AppLocalizations.of(context).translate('settings_server_ip')),
subtitle: Text(serverIp),
onTap: () {
showDialog<String>(
context: context,
builder: (context) => _InputDialog('Type the IP'),
builder: (context) => _InputDialog(AppLocalizations.of(context)
.translate('dialog_server_ip_title')),
);
},
),
ListTile(
title: Text('Server port'),
title: Text(
AppLocalizations.of(context).translate('settings_server_port')),
subtitle: Text('$serverPort'),
onTap: () {
showDialog<String>(
context: context,
builder: (context) => _InputDialog('Type the port'),
builder: (context) => _InputDialog(AppLocalizations.of(context)
.translate('dialog_server_port_title')),
);
},
),
CheckboxListTile(
title: Text('Use HTTPS'),
title: Text(
AppLocalizations.of(context).translate('settings_use_https')),
onChanged: (value) {
setState(() {
useHttps = value;
@ -87,7 +93,8 @@ class _SettingState extends State<Settings> {
),
Divider(),
CheckboxListTile(
title: Text('Needs auth'),
title: Text(
AppLocalizations.of(context).translate('settings_needs_auth')),
onChanged: (value) {
setState(() {
needsAuth = value;
@ -97,29 +104,34 @@ class _SettingState extends State<Settings> {
),
ListTile(
enabled: needsAuth,
title: Text('User ID'),
title: Text(
AppLocalizations.of(context).translate('settings_user_id')),
subtitle: Text(userId),
onTap: () {
showDialog<String>(
context: context,
builder: (context) => _InputDialog('Type the username'),
builder: (context) => _InputDialog(AppLocalizations.of(context)
.translate('dialog_server_user_id_title')),
);
},
),
ListTile(
enabled: needsAuth,
title: Text('User password'),
title: Text(
AppLocalizations.of(context).translate('settings_user_pass')),
subtitle: Text(userPassword),
onTap: () {
showDialog<String>(
context: context,
builder: (context) => _InputDialog('Type the password'),
builder: (context) => _InputDialog(AppLocalizations.of(context)
.translate('dialog_server_user_pass_title')),
);
},
),
Divider(),
ListTile(
title: Text('About OpenLP'),
title: Text(AppLocalizations.of(context)
.translate('settings_about_openlp')),
onTap: () {
launch('https://openlp.org/');
},
@ -143,13 +155,13 @@ class _InputDialog extends StatelessWidget {
content: TextField(autofocus: true),
actions: <Widget>[
FlatButton(
child: Text('CANCEL'),
child: Text(AppLocalizations.of(context).translate('button_cancel')),
onPressed: () {
Navigator.pop(context);
},
),
FlatButton(
child: Text('OK'),
child: Text(AppLocalizations.of(context).translate('button_ok')),
onPressed: () {
Navigator.pop(context, '');
},

View File

@ -22,6 +22,8 @@
import 'package:flutter/material.dart';
import '../configurations/app_localizations.dart';
import 'display_options_dialog.dart';
import 'show_alert_dialog.dart';
@ -36,18 +38,26 @@ class AppBottomNavigationBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final List<_Action> _actions = [
_Action(Icons.add_alert, 'Alert', () {
showDialog(
context: context,
builder: (context) => ShowAlertDialog(),
);
}),
_Action(Icons.personal_video, 'Display', () {
showDialog(
context: context,
builder: (context) => DisplayOptionsDialog(),
);
}),
_Action(
Icons.add_alert,
AppLocalizations.of(context).translate('alert'),
() {
showDialog(
context: context,
builder: (context) => ShowAlertDialog(),
);
},
),
_Action(
Icons.personal_video,
AppLocalizations.of(context).translate('display'),
() {
showDialog(
context: context,
builder: (context) => DisplayOptionsDialog(),
);
},
),
];
return BottomAppBar(
child: Row(

View File

@ -22,26 +22,40 @@
import 'package:flutter/material.dart';
import '../configurations/app_localizations.dart';
class _Action {
String title;
String titleKey;
VoidCallback callback;
_Action(this.title, this.callback);
_Action({@required this.titleKey, @required this.callback});
}
class DisplayOptionsDialog extends StatelessWidget {
final List<_Action> _actions = [
_Action('Blank screen', () {
print('Blank screen');
}),
_Action('Theme background', () {
print('Theme screen');
}),
_Action('Desktop', () {
print('Desktop screen');
}),
_Action('Full projection', () {
print('Show screen');
}),
_Action(
titleKey: 'display_option_blank',
callback: () {
print('Blank screen');
},
),
_Action(
titleKey: 'display_option_theme',
callback: () {
print('Theme screen');
},
),
_Action(
titleKey: 'display_option_desktop',
callback: () {
print('Desktop screen');
},
),
_Action(
titleKey: 'display_option_show',
callback: () {
print('Show screen');
},
),
];
@override
@ -52,7 +66,8 @@ class DisplayOptionsDialog extends StatelessWidget {
.map((action) => OutlineButton(
borderSide: BorderSide(color: Theme.of(context).primaryColor),
textColor: Theme.of(context).primaryColor,
child: Text(action.title),
child: Text(
AppLocalizations.of(context).translate(action.titleKey)),
onPressed: () {
action.callback();
Navigator.of(context).pop();

View File

@ -22,18 +22,20 @@
import 'package:flutter/material.dart';
import '../widgets/service_item_bottom_sheet.dart';
import '../configurations/app_localizations.dart';
import '../widgets/select_service_item_bottom_sheet.dart';
class SearchFloatingButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton.extended(
label: Text('New service item'),
label: Text(AppLocalizations.of(context)
.translate('floating_button_search')),
icon: Icon(Icons.search),
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (context) => ServiceItemBottomSheet(),
builder: (context) => SelectServiceItemBottomSheet(),
);
},
);

View File

@ -22,21 +22,24 @@
import 'package:flutter/material.dart';
import '../configurations/app_localizations.dart';
import '../resources/openlp_icons.dart';
import '../screens/search_service_item.dart';
class _ServiceItem {
IconData icon;
String title;
_ServiceItem({this.icon, this.title});
String titleKey;
_ServiceItem({@required this.icon, @required this.titleKey});
}
class ServiceItemBottomSheet extends StatelessWidget {
class SelectServiceItemBottomSheet extends StatelessWidget {
final List<_ServiceItem> _serviceItems = [
_ServiceItem(icon: Icons.audiotrack, title: 'Songs'),
_ServiceItem(icon: Icons.book, title: 'Bibles'),
_ServiceItem(icon: Icons.present_to_all, title: 'Presentations'),
_ServiceItem(icon: Icons.image, title: 'Images'),
_ServiceItem(icon: Icons.ondemand_video, title: 'Medias'),
_ServiceItem(icon: OpenLPIcons.songs, titleKey: 'plugin_songs_plural'),
_ServiceItem(icon: OpenLPIcons.biblies, titleKey: 'plugin_bibles_plural'),
_ServiceItem(icon: OpenLPIcons.presentations, titleKey: 'plugin_presentations_plural'),
_ServiceItem(icon: OpenLPIcons.images, titleKey: 'plugin_images_plural'),
_ServiceItem(icon: OpenLPIcons.media, titleKey: 'plugin_media_plural'),
_ServiceItem(icon: OpenLPIcons.custom, titleKey: 'plugin_custom_plural'),
];
@override
Widget build(BuildContext context) {
@ -45,7 +48,7 @@ class ServiceItemBottomSheet extends StatelessWidget {
children: _serviceItems.map((item) {
return ListTile(
leading: Icon(item.icon),
title: Text(item.title),
title: Text(AppLocalizations.of(context).translate(item.titleKey)),
onTap: () {
Navigator.of(context).pop();
showSearch(context: context, delegate: SearchServiceItem());

View File

@ -22,20 +22,22 @@
import 'package:flutter/material.dart';
import '../configurations/app_localizations.dart';
class ShowAlertDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Type your alert'),
title: Text(AppLocalizations.of(context).translate('dialog_alert_title')),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
child: Text(AppLocalizations.of(context).translate('button_cancel')),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('Show'),
child: Text(AppLocalizations.of(context).translate('button_show')),
onPressed: () {
Navigator.of(context).pop();
},

View File

@ -42,6 +42,8 @@ dependencies:
flutter:
sdk: flutter
url_launcher: ^5.1.2
flutter_localizations:
sdk: flutter
dev_dependencies:
flutter_test:
@ -60,9 +62,9 @@ flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
assets:
- lang/en.json
- lang/pt.json
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.