diff --git a/lib/about.dart b/lib/about.dart index 869e26d..22d96c0 100644 --- a/lib/about.dart +++ b/lib/about.dart @@ -6,6 +6,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:mustache_template/mustache.dart'; import 'main.dart'; +import 'generated/l10n.dart'; Future showAbout(BuildContext context) async { final contentTemplate = await rootBundle.loadString('assets/about.md'); @@ -14,12 +15,12 @@ Future showAbout(BuildContext context) async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); String version = packageInfo.version; String code = packageInfo.buildNumber; - content += "`Version: $version+$code`"; + content += "`${S.of(context).version}: $version+$code`"; showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( - title: Text('About ${coin.app}'), + title: Text('${S.of(context).about} ${coin.app}'), contentPadding: EdgeInsets.zero, content: Container( width: double.maxFinite, @@ -30,7 +31,7 @@ Future showAbout(BuildContext context) async { onPressed: () { Navigator.of(context).pop(); }, - label: Text('OK'), + label: Text(S.of(context).ok), icon: Icon(Icons.done)) ])); } diff --git a/lib/account.dart b/lib/account.dart index 4598a88..98a0772 100644 --- a/lib/account.dart +++ b/lib/account.dart @@ -16,6 +16,7 @@ import 'package:grouped_list/grouped_list.dart'; import 'about.dart'; import 'main.dart'; +import 'generated/l10n.dart'; class AccountPage extends StatefulWidget { @override @@ -94,28 +95,28 @@ class _AccountPageState extends State builder: (context) => Text("${coin.symbol} Wallet - ${accountManager.active.name}")), bottom: TabBar(controller: _tabController, isScrollable: true, tabs: [ - Tab(text: "Account"), - Tab(text: "Notes"), - Tab(text: "History"), - Tab(text: "Budget"), - Tab(text: "Trading P&L"), - Tab(text: "Contacts"), + Tab(text: S.of(context).account), + Tab(text: S.of(context).notes), + Tab(text: S.of(context).history), + Tab(text: S.of(context).budget), + Tab(text: S.of(context).tradingPl), + Tab(text: S.of(context).contacts), ]), actions: [ Observer(builder: (context) { accountManager.canPay; return PopupMenuButton( itemBuilder: (context) => [ - PopupMenuItem(child: Text("Accounts"), value: "Accounts"), - PopupMenuItem(child: Text("Backup"), value: "Backup"), - PopupMenuItem(child: Text("Rescan"), value: "Rescan"), + PopupMenuItem(child: Text(S.of(context).accounts), value: "Accounts"), + PopupMenuItem(child: Text(S.of(context).backup), value: "Backup"), + PopupMenuItem(child: Text(S.of(context).rescan), value: "Rescan"), if (accountManager.canPay) - PopupMenuItem(child: Text("Cold Storage"), value: "Cold"), + PopupMenuItem(child: Text(S.of(context).coldStorage), value: "Cold"), if (accountManager.canPay) - PopupMenuItem(child: Text('MultiPay'), value: "MultiPay"), - PopupMenuItem(child: Text('Broadcast'), value: "Broadcast"), - PopupMenuItem(child: Text('Settings'), value: "Settings"), - PopupMenuItem(child: Text("About"), value: "About"), + PopupMenuItem(child: Text(S.of(context).multipay), value: "MultiPay"), + PopupMenuItem(child: Text(S.of(context).broadcast), value: "Broadcast"), + PopupMenuItem(child: Text(S.of(context).settings), value: "Settings"), + PopupMenuItem(child: Text(S.of(context).about), value: "About"), ], onSelected: _onMenu, ); @@ -129,7 +130,7 @@ class _AccountPageState extends State child: Column(children: [ Observer( builder: (context) => syncStatus.syncedHeight <= 0 - ? Text('Synching') + ? Text(S.of(context).synching) : syncStatus.isSynced() ? Text('${syncStatus.syncedHeight}', style: theme.textTheme.caption) @@ -145,8 +146,8 @@ class _AccountPageState extends State return Column(children: [ if (hasTAddr) Text(showTAddr - ? 'Tap QR Code for Shielded Address' - : 'Tap QR Code for Transparent Address'), + ? S.of(context).tapQrCodeForShieldedAddress + : S.of(context).tapQrCodeForTransparentAddress), Padding(padding: EdgeInsets.symmetric(vertical: 4)), GestureDetector( onTap: hasTAddr ? _onQRTap : null, @@ -167,14 +168,14 @@ class _AccountPageState extends State Padding(padding: EdgeInsets.symmetric(vertical: 4)), if (!showTAddr) OutlinedButton( - child: Text('New Snap Address'), + child: Text(S.of(context).newSnapAddress), style: OutlinedButton.styleFrom( side: BorderSide( width: 1, color: theme.primaryColor)), onPressed: _onSnapAddress), if (showTAddr) OutlinedButton( - child: Text('Shield Transp. Balance'), + child: Text(S.of(context).shieldTranspBalance), style: OutlinedButton.styleFrom( side: BorderSide(width: 1, color: theme.primaryColor)), @@ -240,7 +241,7 @@ class _AccountPageState extends State floatingActionButton: _accountTab ? FloatingActionButton( onPressed: _onSend, - tooltip: 'Send', + tooltip: S.of(context).send, backgroundColor: Theme.of(context) .accentColor .withOpacity(accountManager.canPay ? 1.0 : 0.3), @@ -268,7 +269,7 @@ class _AccountPageState extends State _onAddressCopy() { Clipboard.setData(ClipboardData(text: _address())); - final snackBar = SnackBar(content: Text('Address copied to clipboard')); + final snackBar = SnackBar(content: Text(S.of(context).addressCopiedToClipboard)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar); } @@ -277,16 +278,16 @@ class _AccountPageState extends State context: this.context, barrierDismissible: false, builder: (context) => AlertDialog( - title: Text('Shield Transparent Balance'), + title: Text(S.of(context).shieldTransparentBalance), content: Text( - 'Do you want to transfer your entire transparent balance to your shielded address?'), + S.of(context).doYouWantToTransferYourEntireTransparentBalanceTo), actions: confirmButtons(context, () async { Navigator.of(this.context).pop(); final snackBar1 = - SnackBar(content: Text('Shielding in progress...')); + SnackBar(content: Text(S.of(context).shieldingInProgress)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar1); final txid = await WarpApi.shieldTAddr(accountManager.active.id); - final snackBar2 = SnackBar(content: Text("TX ID: $txid")); + final snackBar2 = SnackBar(content: Text(S.of(context).txId + txid)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar2); })), ); @@ -406,7 +407,7 @@ class _AccountPageState extends State final localAuth = LocalAuthentication(); try { final didAuthenticate = await localAuth.authenticate( - localizedReason: "Please authenticate to show account seed"); + localizedReason: S.of(context).pleaseAuthenticateToShowAccountSeed); if (didAuthenticate) { Navigator.of(context).pushNamed('/backup', arguments: true); } @@ -415,7 +416,7 @@ class _AccountPageState extends State context: context, barrierDismissible: true, builder: (context) => AlertDialog( - title: Text('No Authentication Method'), + title: Text(S.of(context).noAuthenticationMethod), content: Text(e.message))); } } @@ -425,20 +426,20 @@ class _AccountPageState extends State context: this.context, barrierDismissible: false, builder: (context) => AlertDialog( - title: Text('Rescan'), - content: Text('Rescan wallet from the first block?'), + title: Text(S.of(context).rescan), + content: Text(S.of(context).rescanWalletFromTheFirstBlock), actions: [ TextButton( - child: Text('Cancel'), + child: Text(S.of(context).cancel), onPressed: () { Navigator.of(this.context).pop(); }, ), TextButton( - child: Text('OK'), + child: Text(S.of(context).ok), onPressed: () { Navigator.of(this.context).pop(); - final snackBar = SnackBar(content: Text("Rescan Requested...")); + final snackBar = SnackBar(content: Text(S.of(context).rescanRequested)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar); syncStatus.setSyncHeight(0); WarpApi.rewindToHeight(0); @@ -454,12 +455,11 @@ class _AccountPageState extends State context: context, barrierDismissible: false, builder: (context) => AlertDialog( - title: Text('Cold Storage'), + title: Text(S.of(context).coldStorage), content: Text( - 'Do you want to DELETE the secret key and convert this account to a watch-only account? ' - 'You will not be able to spend from this device anymore. This operation is NOT reversible.'), + S.of(context).doYouWantToDeleteTheSecretKeyAndConvert), actions: - confirmButtons(context, _convertToWatchOnly, okLabel: 'DELETE') + confirmButtons(context, _convertToWatchOnly, okLabel: S.of(context).delete) )); } @@ -524,16 +524,16 @@ class _NoteState extends State with AutomaticKeepAliveClientMixin { columns: [ DataColumn( label: settings.showConfirmations - ? Text('Confs') - : Text('Height'), + ? Text(S.of(context).confs) + : Text(S.of(context).height), onSort: (_, __) { setState(() { settings.toggleShowConfirmations(); }); }), - DataColumn(label: Text('Date/Time')), + DataColumn(label: Text(S.of(context).datetime)), DataColumn( - label: Text('Amount'), + label: Text(S.of(context).amount), numeric: true, onSort: (_, __) { setState(() { @@ -541,7 +541,7 @@ class _NoteState extends State with AutomaticKeepAliveClientMixin { }); }), ], - header: Text('Select notes to EXCLUDE from payments', + header: Text(S.of(context).selectNotesToExcludeFromPayments, style: Theme.of(context).textTheme.bodyText1), columnSpacing: 16, showCheckboxColumn: false, @@ -652,17 +652,17 @@ class HistoryState extends State columns: [ DataColumn( label: settings.showConfirmations - ? Text('Confs') - : Text('Height'), + ? Text(S.of(context).confs) + : Text(S.of(context).height), onSort: (_, __) { setState(() { settings.toggleShowConfirmations(); }); }), - DataColumn(label: Text('Date/Time')), - DataColumn(label: Text('TXID')), + DataColumn(label: Text(S.of(context).datetime)), + DataColumn(label: Text(S.of(context).txid)), DataColumn( - label: Text('Amount'), + label: Text(S.of(context).amount), numeric: true, onSort: (_, __) { setState(() { @@ -739,18 +739,18 @@ class BudgetState extends State Expanded( child: Card( child: Column(children: [ - Text('Largest Spendings by Address', + Text(S.of(context).largestSpendingsByAddress, style: Theme.of(context).textTheme.headline6), Expanded( child: SpendingChart(accountManager.spendings, _showAddress, _toggleAddress)), - Text('Tap Chart to Toggle between Address and Amount', + Text(S.of(context).tapChartToToggleBetweenAddressAndAmount, style: Theme.of(context).textTheme.caption) ]))), Expanded( child: Card( child: Column(children: [ - Text('Account Balance History', + Text(S.of(context).accountBalanceHistory, style: Theme.of(context).textTheme.headline6), Expanded( child: @@ -777,7 +777,7 @@ class SpendingChart extends StatelessWidget { @override Widget build(BuildContext context) { - final seriesList = _createSeries(data, showAddress); + final seriesList = _createSeries(data, showAddress, context); final color = charts.ColorUtil.fromDartColor( Theme.of(context).textTheme.headline5.color); if (seriesList[0].data.isEmpty) @@ -785,7 +785,7 @@ class SpendingChart extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('No Spending in the Last 30 Days', + Text(S.of(context).noSpendingInTheLast30Days, style: Theme.of(context).textTheme.headline5) ]); return new charts.PieChart(seriesList, @@ -805,11 +805,11 @@ class SpendingChart extends StatelessWidget { } static List> _createSeries( - List data, bool showAddress) { + List data, bool showAddress, BuildContext context) { final palette = settings.palette.makeShades(data.length + 5); return [ new charts.Series( - id: 'Largest Spending Last Month', + id: S.of(context).largestSpendingLastMonth, domainFn: (Spending sales, _) => sales.address, measureFn: (Spending sales, _) => sales.amount, colorFn: (_, index) => palette[index], @@ -836,7 +836,7 @@ class AccountBalanceTimeChartState extends State { final axisColor = charts.ColorUtil.fromDartColor( Theme.of(context).textTheme.headline5.color); final seriesList = _createSeries(widget.data, - charts.ColorUtil.fromDartColor(Theme.of(context).primaryColor)); + charts.ColorUtil.fromDartColor(Theme.of(context).primaryColor), context); return new charts.TimeSeriesChart( seriesList, animate: false, @@ -860,10 +860,10 @@ class AccountBalanceTimeChartState extends State { } static List> _createSeries( - List data, charts.Color lineColor) { + List data, charts.Color lineColor, BuildContext context) { return [ new charts.Series( - id: 'Balance', + id: S.of(context).balance, colorFn: (_, __) => lineColor, domainFn: (AccountBalance ab, _) => ab.time, measureFn: (AccountBalance ab, _) => ab.balance, @@ -887,7 +887,7 @@ class PnLState extends State with AutomaticKeepAliveClientMixin { return Column(children: [ FormBuilderRadioGroup( orientation: OptionsOrientation.horizontal, - name: 'Pnl', + name: S.of(context).pnl, initialValue: accountManager.pnlSeriesIndex, onChanged: (v) { setState(() { @@ -895,12 +895,12 @@ class PnLState extends State with AutomaticKeepAliveClientMixin { }); }, options: [ - FormBuilderFieldOption(child: Text('Real'), value: 0), - FormBuilderFieldOption(child: Text('M/M'), value: 1), - FormBuilderFieldOption(child: Text('Total'), value: 2), - FormBuilderFieldOption(child: Text('Price'), value: 3), - FormBuilderFieldOption(child: Text('Qty'), value: 4), - FormBuilderFieldOption(child: Text('Table'), value: 5), + FormBuilderFieldOption(child: Text(S.of(context).real), value: 0), + FormBuilderFieldOption(child: Text(S.of(context).mm), value: 1), + FormBuilderFieldOption(child: Text(S.of(context).total), value: 2), + FormBuilderFieldOption(child: Text(S.of(context).price), value: 3), + FormBuilderFieldOption(child: Text(S.of(context).qty), value: 4), + FormBuilderFieldOption(child: Text(S.of(context).table), value: 5), ]), Observer(builder: (context) { final _ = accountManager.pnls; @@ -924,7 +924,7 @@ class PnLChart extends StatelessWidget { final axisColor = charts.ColorUtil.fromDartColor( Theme.of(context).textTheme.headline5.color); final seriesList = _createSeries(data, seriesIndex, - charts.ColorUtil.fromDartColor(Theme.of(context).primaryColor)); + charts.ColorUtil.fromDartColor(Theme.of(context).primaryColor), context); return new charts.TimeSeriesChart( seriesList, animate: false, @@ -961,10 +961,10 @@ class PnLChart extends StatelessWidget { } static List> _createSeries( - List data, int index, charts.Color lineColor) { + List data, int index, charts.Color lineColor, BuildContext context) { return [ new charts.Series( - id: 'P/L', + id: S.of(context).pl, colorFn: (_, __) => lineColor, domainFn: (PnL pnl, _) => pnl.timestamp, measureFn: (PnL pnl, _) => _seriesData(pnl, index), @@ -980,12 +980,12 @@ class PnLTable extends StatelessWidget { return SingleChildScrollView( child: PaginatedDataTable( columns: [ - DataColumn(label: Text('Date/Time')), - DataColumn(label: Text('Qty'), numeric: true), - DataColumn(label: Text('Price'), numeric: true), - DataColumn(label: Text('Realized'), numeric: true), - DataColumn(label: Text('M/M'), numeric: true), - DataColumn(label: Text('Total'), numeric: true), + DataColumn(label: Text(S.of(context).datetime)), + DataColumn(label: Text(S.of(context).qty), numeric: true), + DataColumn(label: Text(S.of(context).price), numeric: true), + DataColumn(label: Text(S.of(context).realized), numeric: true), + DataColumn(label: Text(S.of(context).mm), numeric: true), + DataColumn(label: Text(S.of(context).total), numeric: true), ], columnSpacing: 16, showCheckboxColumn: false, @@ -1047,7 +1047,7 @@ class ContactsState extends State return Padding( padding: EdgeInsets.all(12), child: Column(children: [ - Text('To make a contact, send them a memo with "Contact: Name"', + Text(S.of(context).toMakeAContactSendThemAMemoWithContact, style: Theme.of(context).textTheme.caption), Expanded( child: GroupedListView( diff --git a/lib/account_manager.dart b/lib/account_manager.dart index 1c99cf5..08e1792 100644 --- a/lib/account_manager.dart +++ b/lib/account_manager.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:warp/main.dart'; import 'package:warp/store.dart'; +import 'generated/l10n.dart'; import 'about.dart'; @@ -23,11 +24,11 @@ class AccountManagerState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text('Accounts'), actions: [ + appBar: AppBar(title: Text(S.of(context).accounts), actions: [ PopupMenuButton( itemBuilder: (context) => [ - PopupMenuItem(child: Text('Settings'), value: "Settings"), - PopupMenuItem(child: Text("About"), value: "About"), + PopupMenuItem(child: Text(S.of(context).settings), value: "Settings"), + PopupMenuItem(child: Text(S.of(context).about), value: "About"), ], onSelected: _onMenu) ]), @@ -35,7 +36,7 @@ class AccountManagerState extends State { builder: (context) => Stack(children: [ accountManager.accounts.isEmpty ? Center( - child: Text("No account", + child: Text(S.of(context).noAccount, style: Theme.of(context).textTheme.headline5)) : ListView( children: accountManager.accounts @@ -74,12 +75,12 @@ class AccountManagerState extends State { context: context, barrierDismissible: false, builder: (context) => AlertDialog( - title: Text('Seed'), + title: Text(S.of(context).seed), content: Text( - 'Are you SURE you want to DELETE this account? You MUST have a BACKUP to recover it. This operation is NOT reversible.'), + S.of(context).confirmDeleteAccount), actions: confirmButtons(context, () { Navigator.of(context).pop(true); - }, okLabel: 'DELETE', cancelValue: false)), + }, okLabel: S.of(context).delete, cancelValue: false)), ); return confirm; } @@ -103,7 +104,7 @@ class AccountManagerState extends State { await showDialog( context: context, builder: (context) => AlertDialog( - title: Text('Change Account Name'), + title: Text(S.of(context).changeAccountName), content: TextField(controller: _accountNameController), actions: confirmButtons(context, _changeAccountName))); } diff --git a/lib/backup.dart b/lib/backup.dart index 1c0bd18..281745d 100644 --- a/lib/backup.dart +++ b/lib/backup.dart @@ -3,6 +3,7 @@ import 'package:qr_flutter/qr_flutter.dart'; import 'main.dart'; import 'store.dart'; +import 'generated/l10n.dart'; class BackupPage extends StatefulWidget { @override @@ -25,7 +26,7 @@ class BackupState extends State { @override Widget build(BuildContext context) { - return Scaffold(appBar: AppBar(title: Text('Backup')), body: + return Scaffold(appBar: AppBar(title: Text(S.of(context).backup)), body: FutureBuilder( future: _init(), builder: _build, @@ -40,7 +41,7 @@ class BackupState extends State { children: [ TextField( decoration: InputDecoration( - labelText: 'Backup Data - Required for Restore', prefixIcon: IconButton(icon: Icon(Icons.save), + labelText: S.of(context).backupDataRequiredForRestore, prefixIcon: IconButton(icon: Icon(Icons.save), onPressed: () => _showQR(backup.value()))), controller: _backupController, minLines: 3, @@ -49,7 +50,7 @@ class BackupState extends State { ), if (type == 0) TextField( - decoration: InputDecoration(labelText: 'Secret Key', prefixIcon: IconButton(icon: Icon(Icons.vpn_key), + decoration: InputDecoration(labelText: S.of(context).secretKey, prefixIcon: IconButton(icon: Icon(Icons.vpn_key), onPressed: () => _showQR(backup.sk))), controller: _skController, minLines: 3, @@ -58,7 +59,7 @@ class BackupState extends State { style: Theme.of(context).textTheme.caption ), if (type != 2) TextField( - decoration: InputDecoration(labelText: 'Viewing Key', prefixIcon: IconButton(icon: Icon(Icons.visibility), + decoration: InputDecoration(labelText: S.of(context).viewingKey, prefixIcon: IconButton(icon: Icon(Icons.visibility), onPressed: () => _showQR(backup.ivk))), controller: _ivkController, minLines: 3, @@ -67,7 +68,7 @@ class BackupState extends State { style: Theme.of(context).textTheme.caption ), Padding(padding: EdgeInsets.symmetric(vertical: 4)), - Text('Tap an icon to show the QR code'), + Text(S.of(context).tapAnIconToShowTheQrCode), ] ), ); diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart new file mode 100644 index 0000000..a56a5fc --- /dev/null +++ b/lib/generated/intl/messages_all.dart @@ -0,0 +1,67 @@ +// 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_en.dart' as messages_en; +import 'messages_fr.dart' as messages_fr; + +typedef Future LibraryLoader(); +Map _deferredLibraries = { + 'en': () => new Future.value(null), + 'fr': () => new Future.value(null), +}; + +MessageLookupByLibrary _findExact(String localeName) { + switch (localeName) { + case 'en': + return messages_en.messages; + case 'fr': + return messages_fr.messages; + default: + return null; + } +} + +/// User programs should call this before using [localeName] for messages. +Future 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/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart new file mode 100644 index 0000000..a30360c --- /dev/null +++ b/lib/generated/intl/messages_en.dart @@ -0,0 +1,146 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a en 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 args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'en'; + + static m0(currency) => "Amount in ${currency}"; + + static m1(ticker) => "Send ${ticker}"; + + static m2(ticker) => "Send ${ticker} to..."; + + static m3(amount, ticker, count) => "Sending a total of ${amount} ${ticker} to ${count} recipients"; + + static m4(aZEC, ticker, address) => "Sending ${aZEC} ${ticker} to ${address}"; + + static m5(currency) => "Use ${currency}"; + + final messages = _notInlinedMessages(_notInlinedMessages); + static _notInlinedMessages(_) => { + "about" : MessageLookupByLibrary.simpleMessage("About"), + "account" : MessageLookupByLibrary.simpleMessage("Account"), + "accountBalanceHistory" : MessageLookupByLibrary.simpleMessage("Account Balance History"), + "accountName" : MessageLookupByLibrary.simpleMessage("Account Name"), + "accountNameIsRequired" : MessageLookupByLibrary.simpleMessage("Account name is required"), + "accounts" : MessageLookupByLibrary.simpleMessage("Accounts"), + "add" : MessageLookupByLibrary.simpleMessage("ADD"), + "address" : MessageLookupByLibrary.simpleMessage("Address"), + "addressCopiedToClipboard" : MessageLookupByLibrary.simpleMessage("Address copied to clipboard"), + "addressIsEmpty" : MessageLookupByLibrary.simpleMessage("Address is empty"), + "advancedOptions" : MessageLookupByLibrary.simpleMessage("Advanced Options"), + "amount" : MessageLookupByLibrary.simpleMessage("Amount"), + "amountInSettingscurrency" : m0, + "amountMustBeANumber" : MessageLookupByLibrary.simpleMessage("Amount must be a number"), + "amountMustBePositive" : MessageLookupByLibrary.simpleMessage("Amount must be positive"), + "approve" : MessageLookupByLibrary.simpleMessage("APPROVE"), + "backup" : MessageLookupByLibrary.simpleMessage("Backup"), + "backupDataRequiredForRestore" : MessageLookupByLibrary.simpleMessage("Backup Data - Required for Restore"), + "balance" : MessageLookupByLibrary.simpleMessage("Balance"), + "blue" : MessageLookupByLibrary.simpleMessage("Blue"), + "broadcast" : MessageLookupByLibrary.simpleMessage("Broadcast"), + "budget" : MessageLookupByLibrary.simpleMessage("Budget"), + "cancel" : MessageLookupByLibrary.simpleMessage("Cancel"), + "changeAccountName" : MessageLookupByLibrary.simpleMessage("Change Account Name"), + "coffee" : MessageLookupByLibrary.simpleMessage("Coffee"), + "coldStorage" : MessageLookupByLibrary.simpleMessage("Cold Storage"), + "confirmDeleteAccount" : MessageLookupByLibrary.simpleMessage("Are you SURE you want to DELETE this account? You MUST have a BACKUP to recover it. This operation is NOT reversible."), + "confs" : MessageLookupByLibrary.simpleMessage("Confs"), + "contacts" : MessageLookupByLibrary.simpleMessage("Contacts"), + "currency" : MessageLookupByLibrary.simpleMessage("Currency"), + "custom" : MessageLookupByLibrary.simpleMessage("Custom"), + "dark" : MessageLookupByLibrary.simpleMessage("Dark"), + "datetime" : MessageLookupByLibrary.simpleMessage("Date/Time"), + "delete" : MessageLookupByLibrary.simpleMessage("DELETE"), + "doYouWantToDeleteTheSecretKeyAndConvert" : MessageLookupByLibrary.simpleMessage("Do you want to DELETE the secret key and convert this account to a watch-only account? You will not be able to spend from this device anymore. This operation is NOT reversible."), + "doYouWantToTransferYourEntireTransparentBalanceTo" : MessageLookupByLibrary.simpleMessage("Do you want to transfer your entire transparent balance to your shielded address?"), + "enterSeed" : MessageLookupByLibrary.simpleMessage("Enter Seed, Secret Key or Viewing Key. Leave blank for a new account"), + "height" : MessageLookupByLibrary.simpleMessage("Height"), + "history" : MessageLookupByLibrary.simpleMessage("History"), + "includeFeeInAmount" : MessageLookupByLibrary.simpleMessage("Include Fee in Amount"), + "invalidAddress" : MessageLookupByLibrary.simpleMessage("Invalid Address"), + "key" : MessageLookupByLibrary.simpleMessage("Key"), + "largestSpendingLastMonth" : MessageLookupByLibrary.simpleMessage("Largest Spending Last Month"), + "largestSpendingsByAddress" : MessageLookupByLibrary.simpleMessage("Largest Spendings by Address"), + "light" : MessageLookupByLibrary.simpleMessage("Light"), + "max" : MessageLookupByLibrary.simpleMessage("MAX"), + "maxAmountPerNote" : MessageLookupByLibrary.simpleMessage("Max Amount per Note"), + "memo" : MessageLookupByLibrary.simpleMessage("Memo"), + "mm" : MessageLookupByLibrary.simpleMessage("M/M"), + "multiPay" : MessageLookupByLibrary.simpleMessage("Multi Pay"), + "multipay" : MessageLookupByLibrary.simpleMessage("MultiPay"), + "na" : MessageLookupByLibrary.simpleMessage("N/A"), + "newSnapAddress" : MessageLookupByLibrary.simpleMessage("New Snap Address"), + "noAccount" : MessageLookupByLibrary.simpleMessage("No account"), + "noAuthenticationMethod" : MessageLookupByLibrary.simpleMessage("No Authentication Method"), + "noSpendingInTheLast30Days" : MessageLookupByLibrary.simpleMessage("No Spending in the Last 30 Days"), + "notEnoughBalance" : MessageLookupByLibrary.simpleMessage("Not enough balance"), + "notes" : MessageLookupByLibrary.simpleMessage("Notes"), + "numberOfConfirmationsNeededBeforeSpending" : MessageLookupByLibrary.simpleMessage("Number of Confirmations Needed before Spending"), + "ok" : MessageLookupByLibrary.simpleMessage("OK"), + "openInExplorer" : MessageLookupByLibrary.simpleMessage("Open in Explorer"), + "pink" : MessageLookupByLibrary.simpleMessage("Pink"), + "pl" : MessageLookupByLibrary.simpleMessage("P/L"), + "pleaseAuthenticateToShowAccountSeed" : MessageLookupByLibrary.simpleMessage("Please authenticate to show account seed"), + "pleaseConfirm" : MessageLookupByLibrary.simpleMessage("Please Confirm"), + "pnl" : MessageLookupByLibrary.simpleMessage("Pnl"), + "preparingTransaction" : MessageLookupByLibrary.simpleMessage("Preparing transaction..."), + "price" : MessageLookupByLibrary.simpleMessage("Price"), + "qty" : MessageLookupByLibrary.simpleMessage("Qty"), + "real" : MessageLookupByLibrary.simpleMessage("Real"), + "realized" : MessageLookupByLibrary.simpleMessage("Realized"), + "rescan" : MessageLookupByLibrary.simpleMessage("Rescan"), + "rescanRequested" : MessageLookupByLibrary.simpleMessage("Rescan Requested..."), + "rescanWalletFromTheFirstBlock" : MessageLookupByLibrary.simpleMessage("Rescan wallet from the first block?"), + "retrieveTransactionDetails" : MessageLookupByLibrary.simpleMessage("Retrieve Transaction Details"), + "roundToMillis" : MessageLookupByLibrary.simpleMessage("Round to millis"), + "scanStartingMomentarily" : MessageLookupByLibrary.simpleMessage("Scan starting momentarily"), + "secretKey" : MessageLookupByLibrary.simpleMessage("Secret Key"), + "seed" : MessageLookupByLibrary.simpleMessage("Seed"), + "selectNotesToExcludeFromPayments" : MessageLookupByLibrary.simpleMessage("Select notes to EXCLUDE from payments"), + "send" : MessageLookupByLibrary.simpleMessage("Send"), + "sendCointicker" : m1, + "sendCointickerTo" : m2, + "sendingATotalOfAmountCointickerToCountRecipients" : m3, + "sendingAzecCointickerToAddress" : m4, + "server" : MessageLookupByLibrary.simpleMessage("Server"), + "settings" : MessageLookupByLibrary.simpleMessage("Settings"), + "shieldTranspBalance" : MessageLookupByLibrary.simpleMessage("Shield Transp. Balance"), + "shieldTransparentBalance" : MessageLookupByLibrary.simpleMessage("Shield Transparent Balance"), + "shieldingInProgress" : MessageLookupByLibrary.simpleMessage("Shielding in progress..."), + "spendable" : MessageLookupByLibrary.simpleMessage("Spendable:"), + "synching" : MessageLookupByLibrary.simpleMessage("Synching"), + "table" : MessageLookupByLibrary.simpleMessage("Table"), + "tapAnIconToShowTheQrCode" : MessageLookupByLibrary.simpleMessage("Tap an icon to show the QR code"), + "tapChartToToggleBetweenAddressAndAmount" : MessageLookupByLibrary.simpleMessage("Tap Chart to Toggle between Address and Amount"), + "tapQrCodeForShieldedAddress" : MessageLookupByLibrary.simpleMessage("Tap QR Code for Shielded Address"), + "tapQrCodeForTransparentAddress" : MessageLookupByLibrary.simpleMessage("Tap QR Code for Transparent Address"), + "theme" : MessageLookupByLibrary.simpleMessage("Theme"), + "timestamp" : MessageLookupByLibrary.simpleMessage("Timestamp"), + "toMakeAContactSendThemAMemoWithContact" : MessageLookupByLibrary.simpleMessage("To make a contact, send them a memo with Contact:"), + "total" : MessageLookupByLibrary.simpleMessage("Total"), + "tradingPl" : MessageLookupByLibrary.simpleMessage("Trading P&L"), + "transactionDetails" : MessageLookupByLibrary.simpleMessage("Transaction Details"), + "txId" : MessageLookupByLibrary.simpleMessage("TX ID:"), + "txid" : MessageLookupByLibrary.simpleMessage("TXID"), + "unsignedTransactionFile" : MessageLookupByLibrary.simpleMessage("Unsigned Transaction File"), + "useSettingscurrency" : m5, + "version" : MessageLookupByLibrary.simpleMessage("Version"), + "viewingKey" : MessageLookupByLibrary.simpleMessage("Viewing Key") + }; +} diff --git a/lib/generated/intl/messages_fr.dart b/lib/generated/intl/messages_fr.dart new file mode 100644 index 0000000..e9fba07 --- /dev/null +++ b/lib/generated/intl/messages_fr.dart @@ -0,0 +1,146 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a fr 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 args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'fr'; + + static m0(currency) => "********"; + + static m1(ticker) => "********"; + + static m2(ticker) => "********"; + + static m3(amount, ticker, count) => "********"; + + static m4(aZEC, ticker, address) => "********"; + + static m5(currency) => "********"; + + final messages = _notInlinedMessages(_notInlinedMessages); + static _notInlinedMessages(_) => { + "about" : MessageLookupByLibrary.simpleMessage("********"), + "account" : MessageLookupByLibrary.simpleMessage("********"), + "accountBalanceHistory" : MessageLookupByLibrary.simpleMessage("********"), + "accountName" : MessageLookupByLibrary.simpleMessage("********"), + "accountNameIsRequired" : MessageLookupByLibrary.simpleMessage("********"), + "accounts" : MessageLookupByLibrary.simpleMessage("********"), + "add" : MessageLookupByLibrary.simpleMessage("********"), + "address" : MessageLookupByLibrary.simpleMessage("********"), + "addressCopiedToClipboard" : MessageLookupByLibrary.simpleMessage("********"), + "addressIsEmpty" : MessageLookupByLibrary.simpleMessage("********"), + "advancedOptions" : MessageLookupByLibrary.simpleMessage("********"), + "amount" : MessageLookupByLibrary.simpleMessage("********"), + "amountInSettingscurrency" : m0, + "amountMustBeANumber" : MessageLookupByLibrary.simpleMessage("********"), + "amountMustBePositive" : MessageLookupByLibrary.simpleMessage("********"), + "approve" : MessageLookupByLibrary.simpleMessage("********"), + "backup" : MessageLookupByLibrary.simpleMessage("********"), + "backupDataRequiredForRestore" : MessageLookupByLibrary.simpleMessage("********"), + "balance" : MessageLookupByLibrary.simpleMessage("********"), + "blue" : MessageLookupByLibrary.simpleMessage("********"), + "broadcast" : MessageLookupByLibrary.simpleMessage("********"), + "budget" : MessageLookupByLibrary.simpleMessage("********"), + "cancel" : MessageLookupByLibrary.simpleMessage("********"), + "changeAccountName" : MessageLookupByLibrary.simpleMessage("********"), + "coffee" : MessageLookupByLibrary.simpleMessage("********"), + "coldStorage" : MessageLookupByLibrary.simpleMessage("********"), + "confirmDeleteAccount" : MessageLookupByLibrary.simpleMessage("********"), + "confs" : MessageLookupByLibrary.simpleMessage("********"), + "contacts" : MessageLookupByLibrary.simpleMessage("********"), + "currency" : MessageLookupByLibrary.simpleMessage("********"), + "custom" : MessageLookupByLibrary.simpleMessage("********"), + "dark" : MessageLookupByLibrary.simpleMessage("********"), + "datetime" : MessageLookupByLibrary.simpleMessage("********"), + "delete" : MessageLookupByLibrary.simpleMessage("********"), + "doYouWantToDeleteTheSecretKeyAndConvert" : MessageLookupByLibrary.simpleMessage("********"), + "doYouWantToTransferYourEntireTransparentBalanceTo" : MessageLookupByLibrary.simpleMessage("********"), + "enterSeed" : MessageLookupByLibrary.simpleMessage("********"), + "height" : MessageLookupByLibrary.simpleMessage("********"), + "history" : MessageLookupByLibrary.simpleMessage("********"), + "includeFeeInAmount" : MessageLookupByLibrary.simpleMessage("********"), + "invalidAddress" : MessageLookupByLibrary.simpleMessage("********"), + "key" : MessageLookupByLibrary.simpleMessage("********"), + "largestSpendingLastMonth" : MessageLookupByLibrary.simpleMessage("********"), + "largestSpendingsByAddress" : MessageLookupByLibrary.simpleMessage("********"), + "light" : MessageLookupByLibrary.simpleMessage("********"), + "max" : MessageLookupByLibrary.simpleMessage("********"), + "maxAmountPerNote" : MessageLookupByLibrary.simpleMessage("********"), + "memo" : MessageLookupByLibrary.simpleMessage("********"), + "mm" : MessageLookupByLibrary.simpleMessage("********"), + "multiPay" : MessageLookupByLibrary.simpleMessage("********"), + "multipay" : MessageLookupByLibrary.simpleMessage("********"), + "na" : MessageLookupByLibrary.simpleMessage("********"), + "newSnapAddress" : MessageLookupByLibrary.simpleMessage("********"), + "noAccount" : MessageLookupByLibrary.simpleMessage("********"), + "noAuthenticationMethod" : MessageLookupByLibrary.simpleMessage("********"), + "noSpendingInTheLast30Days" : MessageLookupByLibrary.simpleMessage("********"), + "notEnoughBalance" : MessageLookupByLibrary.simpleMessage("********"), + "notes" : MessageLookupByLibrary.simpleMessage("********"), + "numberOfConfirmationsNeededBeforeSpending" : MessageLookupByLibrary.simpleMessage("********"), + "ok" : MessageLookupByLibrary.simpleMessage("********"), + "openInExplorer" : MessageLookupByLibrary.simpleMessage("********"), + "pink" : MessageLookupByLibrary.simpleMessage("********"), + "pl" : MessageLookupByLibrary.simpleMessage("********"), + "pleaseAuthenticateToShowAccountSeed" : MessageLookupByLibrary.simpleMessage("********"), + "pleaseConfirm" : MessageLookupByLibrary.simpleMessage("********"), + "pnl" : MessageLookupByLibrary.simpleMessage("********"), + "preparingTransaction" : MessageLookupByLibrary.simpleMessage("********"), + "price" : MessageLookupByLibrary.simpleMessage("********"), + "qty" : MessageLookupByLibrary.simpleMessage("********"), + "real" : MessageLookupByLibrary.simpleMessage("********"), + "realized" : MessageLookupByLibrary.simpleMessage("********"), + "rescan" : MessageLookupByLibrary.simpleMessage("********"), + "rescanRequested" : MessageLookupByLibrary.simpleMessage("********"), + "rescanWalletFromTheFirstBlock" : MessageLookupByLibrary.simpleMessage("********"), + "retrieveTransactionDetails" : MessageLookupByLibrary.simpleMessage("********"), + "roundToMillis" : MessageLookupByLibrary.simpleMessage("********"), + "scanStartingMomentarily" : MessageLookupByLibrary.simpleMessage("********"), + "secretKey" : MessageLookupByLibrary.simpleMessage("********"), + "seed" : MessageLookupByLibrary.simpleMessage("********"), + "selectNotesToExcludeFromPayments" : MessageLookupByLibrary.simpleMessage("********"), + "send" : MessageLookupByLibrary.simpleMessage("********"), + "sendCointicker" : m1, + "sendCointickerTo" : m2, + "sendingATotalOfAmountCointickerToCountRecipients" : m3, + "sendingAzecCointickerToAddress" : m4, + "server" : MessageLookupByLibrary.simpleMessage("********"), + "settings" : MessageLookupByLibrary.simpleMessage("********"), + "shieldTranspBalance" : MessageLookupByLibrary.simpleMessage("********"), + "shieldTransparentBalance" : MessageLookupByLibrary.simpleMessage("********"), + "shieldingInProgress" : MessageLookupByLibrary.simpleMessage("********"), + "spendable" : MessageLookupByLibrary.simpleMessage("********"), + "synching" : MessageLookupByLibrary.simpleMessage("********"), + "table" : MessageLookupByLibrary.simpleMessage("********"), + "tapAnIconToShowTheQrCode" : MessageLookupByLibrary.simpleMessage("********"), + "tapChartToToggleBetweenAddressAndAmount" : MessageLookupByLibrary.simpleMessage("********"), + "tapQrCodeForShieldedAddress" : MessageLookupByLibrary.simpleMessage("********"), + "tapQrCodeForTransparentAddress" : MessageLookupByLibrary.simpleMessage("********"), + "theme" : MessageLookupByLibrary.simpleMessage("********"), + "timestamp" : MessageLookupByLibrary.simpleMessage("********"), + "toMakeAContactSendThemAMemoWithContact" : MessageLookupByLibrary.simpleMessage("********"), + "total" : MessageLookupByLibrary.simpleMessage("********"), + "tradingPl" : MessageLookupByLibrary.simpleMessage("********"), + "transactionDetails" : MessageLookupByLibrary.simpleMessage("********"), + "txId" : MessageLookupByLibrary.simpleMessage("********"), + "txid" : MessageLookupByLibrary.simpleMessage("********"), + "unsignedTransactionFile" : MessageLookupByLibrary.simpleMessage("********"), + "useSettingscurrency" : m5, + "version" : MessageLookupByLibrary.simpleMessage("********"), + "viewingKey" : MessageLookupByLibrary.simpleMessage("********") + }; +} diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart new file mode 100644 index 0000000..04cb70d --- /dev/null +++ b/lib/generated/l10n.dart @@ -0,0 +1,1156 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'intl/messages_all.dart'; + +// ************************************************************************** +// Generator: Flutter Intl IDE plugin +// Made by Localizely +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars +// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each +// ignore_for_file: avoid_redundant_argument_values + +class S { + S(); + + static S current; + + static const AppLocalizationDelegate delegate = + AppLocalizationDelegate(); + + static Future load(Locale locale) { + final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString(); + final localeName = Intl.canonicalizedLocale(name); + return initializeMessages(localeName).then((_) { + Intl.defaultLocale = localeName; + S.current = S(); + + return S.current; + }); + } + + static S of(BuildContext context) { + return Localizations.of(context, S); + } + + /// `Version` + String get version { + return Intl.message( + 'Version', + name: 'version', + desc: '', + args: [], + ); + } + + /// `About` + String get about { + return Intl.message( + 'About', + name: 'about', + desc: '', + args: [], + ); + } + + /// `OK` + String get ok { + return Intl.message( + 'OK', + name: 'ok', + desc: '', + args: [], + ); + } + + /// `Account` + String get account { + return Intl.message( + 'Account', + name: 'account', + desc: '', + args: [], + ); + } + + /// `Notes` + String get notes { + return Intl.message( + 'Notes', + name: 'notes', + desc: '', + args: [], + ); + } + + /// `History` + String get history { + return Intl.message( + 'History', + name: 'history', + desc: '', + args: [], + ); + } + + /// `Budget` + String get budget { + return Intl.message( + 'Budget', + name: 'budget', + desc: '', + args: [], + ); + } + + /// `Trading P&L` + String get tradingPl { + return Intl.message( + 'Trading P&L', + name: 'tradingPl', + desc: '', + args: [], + ); + } + + /// `Contacts` + String get contacts { + return Intl.message( + 'Contacts', + name: 'contacts', + desc: '', + args: [], + ); + } + + /// `Accounts` + String get accounts { + return Intl.message( + 'Accounts', + name: 'accounts', + desc: '', + args: [], + ); + } + + /// `Backup` + String get backup { + return Intl.message( + 'Backup', + name: 'backup', + desc: '', + args: [], + ); + } + + /// `Rescan` + String get rescan { + return Intl.message( + 'Rescan', + name: 'rescan', + desc: '', + args: [], + ); + } + + /// `Cold Storage` + String get coldStorage { + return Intl.message( + 'Cold Storage', + name: 'coldStorage', + desc: '', + args: [], + ); + } + + /// `MultiPay` + String get multipay { + return Intl.message( + 'MultiPay', + name: 'multipay', + desc: '', + args: [], + ); + } + + /// `Broadcast` + String get broadcast { + return Intl.message( + 'Broadcast', + name: 'broadcast', + desc: '', + args: [], + ); + } + + /// `Settings` + String get settings { + return Intl.message( + 'Settings', + name: 'settings', + desc: '', + args: [], + ); + } + + /// `Synching` + String get synching { + return Intl.message( + 'Synching', + name: 'synching', + desc: '', + args: [], + ); + } + + /// `Tap QR Code for Shielded Address` + String get tapQrCodeForShieldedAddress { + return Intl.message( + 'Tap QR Code for Shielded Address', + name: 'tapQrCodeForShieldedAddress', + desc: '', + args: [], + ); + } + + /// `Tap QR Code for Transparent Address` + String get tapQrCodeForTransparentAddress { + return Intl.message( + 'Tap QR Code for Transparent Address', + name: 'tapQrCodeForTransparentAddress', + desc: '', + args: [], + ); + } + + /// `Address copied to clipboard` + String get addressCopiedToClipboard { + return Intl.message( + 'Address copied to clipboard', + name: 'addressCopiedToClipboard', + desc: '', + args: [], + ); + } + + /// `Shield Transparent Balance` + String get shieldTransparentBalance { + return Intl.message( + 'Shield Transparent Balance', + name: 'shieldTransparentBalance', + desc: '', + args: [], + ); + } + + /// `Do you want to transfer your entire transparent balance to your shielded address?` + String get doYouWantToTransferYourEntireTransparentBalanceTo { + return Intl.message( + 'Do you want to transfer your entire transparent balance to your shielded address?', + name: 'doYouWantToTransferYourEntireTransparentBalanceTo', + desc: '', + args: [], + ); + } + + /// `Shielding in progress...` + String get shieldingInProgress { + return Intl.message( + 'Shielding in progress...', + name: 'shieldingInProgress', + desc: '', + args: [], + ); + } + + /// `TX ID:` + String get txId { + return Intl.message( + 'TX ID:', + name: 'txId', + desc: '', + args: [], + ); + } + + /// `Please authenticate to show account seed` + String get pleaseAuthenticateToShowAccountSeed { + return Intl.message( + 'Please authenticate to show account seed', + name: 'pleaseAuthenticateToShowAccountSeed', + desc: '', + args: [], + ); + } + + /// `No Authentication Method` + String get noAuthenticationMethod { + return Intl.message( + 'No Authentication Method', + name: 'noAuthenticationMethod', + desc: '', + args: [], + ); + } + + /// `Rescan wallet from the first block?` + String get rescanWalletFromTheFirstBlock { + return Intl.message( + 'Rescan wallet from the first block?', + name: 'rescanWalletFromTheFirstBlock', + desc: '', + args: [], + ); + } + + /// `Cancel` + String get cancel { + return Intl.message( + 'Cancel', + name: 'cancel', + desc: '', + args: [], + ); + } + + /// `Rescan Requested...` + String get rescanRequested { + return Intl.message( + 'Rescan Requested...', + name: 'rescanRequested', + desc: '', + args: [], + ); + } + + /// `Do you want to DELETE the secret key and convert this account to a watch-only account? You will not be able to spend from this device anymore. This operation is NOT reversible.` + String get doYouWantToDeleteTheSecretKeyAndConvert { + return Intl.message( + 'Do you want to DELETE the secret key and convert this account to a watch-only account? You will not be able to spend from this device anymore. This operation is NOT reversible.', + name: 'doYouWantToDeleteTheSecretKeyAndConvert', + desc: '', + args: [], + ); + } + + /// `DELETE` + String get delete { + return Intl.message( + 'DELETE', + name: 'delete', + desc: '', + args: [], + ); + } + + /// `Confs` + String get confs { + return Intl.message( + 'Confs', + name: 'confs', + desc: '', + args: [], + ); + } + + /// `Height` + String get height { + return Intl.message( + 'Height', + name: 'height', + desc: '', + args: [], + ); + } + + /// `Date/Time` + String get datetime { + return Intl.message( + 'Date/Time', + name: 'datetime', + desc: '', + args: [], + ); + } + + /// `Amount` + String get amount { + return Intl.message( + 'Amount', + name: 'amount', + desc: '', + args: [], + ); + } + + /// `Select notes to EXCLUDE from payments` + String get selectNotesToExcludeFromPayments { + return Intl.message( + 'Select notes to EXCLUDE from payments', + name: 'selectNotesToExcludeFromPayments', + desc: '', + args: [], + ); + } + + /// `TXID` + String get txid { + return Intl.message( + 'TXID', + name: 'txid', + desc: '', + args: [], + ); + } + + /// `Largest Spendings by Address` + String get largestSpendingsByAddress { + return Intl.message( + 'Largest Spendings by Address', + name: 'largestSpendingsByAddress', + desc: '', + args: [], + ); + } + + /// `Tap Chart to Toggle between Address and Amount` + String get tapChartToToggleBetweenAddressAndAmount { + return Intl.message( + 'Tap Chart to Toggle between Address and Amount', + name: 'tapChartToToggleBetweenAddressAndAmount', + desc: '', + args: [], + ); + } + + /// `Account Balance History` + String get accountBalanceHistory { + return Intl.message( + 'Account Balance History', + name: 'accountBalanceHistory', + desc: '', + args: [], + ); + } + + /// `No Spending in the Last 30 Days` + String get noSpendingInTheLast30Days { + return Intl.message( + 'No Spending in the Last 30 Days', + name: 'noSpendingInTheLast30Days', + desc: '', + args: [], + ); + } + + /// `Largest Spending Last Month` + String get largestSpendingLastMonth { + return Intl.message( + 'Largest Spending Last Month', + name: 'largestSpendingLastMonth', + desc: '', + args: [], + ); + } + + /// `Balance` + String get balance { + return Intl.message( + 'Balance', + name: 'balance', + desc: '', + args: [], + ); + } + + /// `Pnl` + String get pnl { + return Intl.message( + 'Pnl', + name: 'pnl', + desc: '', + args: [], + ); + } + + /// `Real` + String get real { + return Intl.message( + 'Real', + name: 'real', + desc: '', + args: [], + ); + } + + /// `M/M` + String get mm { + return Intl.message( + 'M/M', + name: 'mm', + desc: '', + args: [], + ); + } + + /// `Total` + String get total { + return Intl.message( + 'Total', + name: 'total', + desc: '', + args: [], + ); + } + + /// `Price` + String get price { + return Intl.message( + 'Price', + name: 'price', + desc: '', + args: [], + ); + } + + /// `Qty` + String get qty { + return Intl.message( + 'Qty', + name: 'qty', + desc: '', + args: [], + ); + } + + /// `Table` + String get table { + return Intl.message( + 'Table', + name: 'table', + desc: '', + args: [], + ); + } + + /// `P/L` + String get pl { + return Intl.message( + 'P/L', + name: 'pl', + desc: '', + args: [], + ); + } + + /// `Realized` + String get realized { + return Intl.message( + 'Realized', + name: 'realized', + desc: '', + args: [], + ); + } + + /// `To make a contact, send them a memo with Contact:` + String get toMakeAContactSendThemAMemoWithContact { + return Intl.message( + 'To make a contact, send them a memo with Contact:', + name: 'toMakeAContactSendThemAMemoWithContact', + desc: '', + args: [], + ); + } + + /// `New Snap Address` + String get newSnapAddress { + return Intl.message( + 'New Snap Address', + name: 'newSnapAddress', + desc: '', + args: [], + ); + } + + /// `Shield Transp. Balance` + String get shieldTranspBalance { + return Intl.message( + 'Shield Transp. Balance', + name: 'shieldTranspBalance', + desc: '', + args: [], + ); + } + + /// `Send` + String get send { + return Intl.message( + 'Send', + name: 'send', + desc: '', + args: [], + ); + } + + /// `No account` + String get noAccount { + return Intl.message( + 'No account', + name: 'noAccount', + desc: '', + args: [], + ); + } + + /// `Seed` + String get seed { + return Intl.message( + 'Seed', + name: 'seed', + desc: '', + args: [], + ); + } + + /// `Are you SURE you want to DELETE this account? You MUST have a BACKUP to recover it. This operation is NOT reversible.` + String get confirmDeleteAccount { + return Intl.message( + 'Are you SURE you want to DELETE this account? You MUST have a BACKUP to recover it. This operation is NOT reversible.', + name: 'confirmDeleteAccount', + desc: '', + args: [], + ); + } + + /// `Change Account Name` + String get changeAccountName { + return Intl.message( + 'Change Account Name', + name: 'changeAccountName', + desc: '', + args: [], + ); + } + + /// `Backup Data - Required for Restore` + String get backupDataRequiredForRestore { + return Intl.message( + 'Backup Data - Required for Restore', + name: 'backupDataRequiredForRestore', + desc: '', + args: [], + ); + } + + /// `Secret Key` + String get secretKey { + return Intl.message( + 'Secret Key', + name: 'secretKey', + desc: '', + args: [], + ); + } + + /// `Viewing Key` + String get viewingKey { + return Intl.message( + 'Viewing Key', + name: 'viewingKey', + desc: '', + args: [], + ); + } + + /// `Tap an icon to show the QR code` + String get tapAnIconToShowTheQrCode { + return Intl.message( + 'Tap an icon to show the QR code', + name: 'tapAnIconToShowTheQrCode', + desc: '', + args: [], + ); + } + + /// `Multi Pay` + String get multiPay { + return Intl.message( + 'Multi Pay', + name: 'multiPay', + desc: '', + args: [], + ); + } + + /// `Please Confirm` + String get pleaseConfirm { + return Intl.message( + 'Please Confirm', + name: 'pleaseConfirm', + desc: '', + args: [], + ); + } + + /// `Sending a total of {amount} {ticker} to {count} recipients` + String sendingATotalOfAmountCointickerToCountRecipients(Object amount, Object ticker, Object count) { + return Intl.message( + 'Sending a total of $amount $ticker to $count recipients', + name: 'sendingATotalOfAmountCointickerToCountRecipients', + desc: '', + args: [amount, ticker, count], + ); + } + + /// `Preparing transaction...` + String get preparingTransaction { + return Intl.message( + 'Preparing transaction...', + name: 'preparingTransaction', + desc: '', + args: [], + ); + } + + /// `Send {ticker} to...` + String sendCointickerTo(Object ticker) { + return Intl.message( + 'Send $ticker to...', + name: 'sendCointickerTo', + desc: '', + args: [ticker], + ); + } + + /// `Address is empty` + String get addressIsEmpty { + return Intl.message( + 'Address is empty', + name: 'addressIsEmpty', + desc: '', + args: [], + ); + } + + /// `Invalid Address` + String get invalidAddress { + return Intl.message( + 'Invalid Address', + name: 'invalidAddress', + desc: '', + args: [], + ); + } + + /// `Amount must be a number` + String get amountMustBeANumber { + return Intl.message( + 'Amount must be a number', + name: 'amountMustBeANumber', + desc: '', + args: [], + ); + } + + /// `Amount must be positive` + String get amountMustBePositive { + return Intl.message( + 'Amount must be positive', + name: 'amountMustBePositive', + desc: '', + args: [], + ); + } + + /// `Account Name` + String get accountName { + return Intl.message( + 'Account Name', + name: 'accountName', + desc: '', + args: [], + ); + } + + /// `Account name is required` + String get accountNameIsRequired { + return Intl.message( + 'Account name is required', + name: 'accountNameIsRequired', + desc: '', + args: [], + ); + } + + /// `Enter Seed, Secret Key or Viewing Key. Leave blank for a new account` + String get enterSeed { + return Intl.message( + 'Enter Seed, Secret Key or Viewing Key. Leave blank for a new account', + name: 'enterSeed', + desc: '', + args: [], + ); + } + + /// `Scan starting momentarily` + String get scanStartingMomentarily { + return Intl.message( + 'Scan starting momentarily', + name: 'scanStartingMomentarily', + desc: '', + args: [], + ); + } + + /// `Key` + String get key { + return Intl.message( + 'Key', + name: 'key', + desc: '', + args: [], + ); + } + + /// `Send {ticker}` + String sendCointicker(Object ticker) { + return Intl.message( + 'Send $ticker', + name: 'sendCointicker', + desc: '', + args: [ticker], + ); + } + + /// `MAX` + String get max { + return Intl.message( + 'MAX', + name: 'max', + desc: '', + args: [], + ); + } + + /// `Advanced Options` + String get advancedOptions { + return Intl.message( + 'Advanced Options', + name: 'advancedOptions', + desc: '', + args: [], + ); + } + + /// `Memo` + String get memo { + return Intl.message( + 'Memo', + name: 'memo', + desc: '', + args: [], + ); + } + + /// `Round to millis` + String get roundToMillis { + return Intl.message( + 'Round to millis', + name: 'roundToMillis', + desc: '', + args: [], + ); + } + + /// `Use {currency}` + String useSettingscurrency(Object currency) { + return Intl.message( + 'Use $currency', + name: 'useSettingscurrency', + desc: '', + args: [currency], + ); + } + + /// `Include Fee in Amount` + String get includeFeeInAmount { + return Intl.message( + 'Include Fee in Amount', + name: 'includeFeeInAmount', + desc: '', + args: [], + ); + } + + /// `Max Amount per Note` + String get maxAmountPerNote { + return Intl.message( + 'Max Amount per Note', + name: 'maxAmountPerNote', + desc: '', + args: [], + ); + } + + /// `Spendable:` + String get spendable { + return Intl.message( + 'Spendable:', + name: 'spendable', + desc: '', + args: [], + ); + } + + /// `Not enough balance` + String get notEnoughBalance { + return Intl.message( + 'Not enough balance', + name: 'notEnoughBalance', + desc: '', + args: [], + ); + } + + /// `APPROVE` + String get approve { + return Intl.message( + 'APPROVE', + name: 'approve', + desc: '', + args: [], + ); + } + + /// `Sending {aZEC} {ticker} to {address}` + String sendingAzecCointickerToAddress(Object aZEC, Object ticker, Object address) { + return Intl.message( + 'Sending $aZEC $ticker to $address', + name: 'sendingAzecCointickerToAddress', + desc: '', + args: [aZEC, ticker, address], + ); + } + + /// `Unsigned Transaction File` + String get unsignedTransactionFile { + return Intl.message( + 'Unsigned Transaction File', + name: 'unsignedTransactionFile', + desc: '', + args: [], + ); + } + + /// `Amount in {currency}` + String amountInSettingscurrency(Object currency) { + return Intl.message( + 'Amount in $currency', + name: 'amountInSettingscurrency', + desc: '', + args: [currency], + ); + } + + /// `Custom` + String get custom { + return Intl.message( + 'Custom', + name: 'custom', + desc: '', + args: [], + ); + } + + /// `Server` + String get server { + return Intl.message( + 'Server', + name: 'server', + desc: '', + args: [], + ); + } + + /// `Blue` + String get blue { + return Intl.message( + 'Blue', + name: 'blue', + desc: '', + args: [], + ); + } + + /// `Pink` + String get pink { + return Intl.message( + 'Pink', + name: 'pink', + desc: '', + args: [], + ); + } + + /// `Coffee` + String get coffee { + return Intl.message( + 'Coffee', + name: 'coffee', + desc: '', + args: [], + ); + } + + /// `Light` + String get light { + return Intl.message( + 'Light', + name: 'light', + desc: '', + args: [], + ); + } + + /// `Dark` + String get dark { + return Intl.message( + 'Dark', + name: 'dark', + desc: '', + args: [], + ); + } + + /// `Currency` + String get currency { + return Intl.message( + 'Currency', + name: 'currency', + desc: '', + args: [], + ); + } + + /// `Number of Confirmations Needed before Spending` + String get numberOfConfirmationsNeededBeforeSpending { + return Intl.message( + 'Number of Confirmations Needed before Spending', + name: 'numberOfConfirmationsNeededBeforeSpending', + desc: '', + args: [], + ); + } + + /// `Retrieve Transaction Details` + String get retrieveTransactionDetails { + return Intl.message( + 'Retrieve Transaction Details', + name: 'retrieveTransactionDetails', + desc: '', + args: [], + ); + } + + /// `Theme` + String get theme { + return Intl.message( + 'Theme', + name: 'theme', + desc: '', + args: [], + ); + } + + /// `Transaction Details` + String get transactionDetails { + return Intl.message( + 'Transaction Details', + name: 'transactionDetails', + desc: '', + args: [], + ); + } + + /// `Timestamp` + String get timestamp { + return Intl.message( + 'Timestamp', + name: 'timestamp', + desc: '', + args: [], + ); + } + + /// `Address` + String get address { + return Intl.message( + 'Address', + name: 'address', + desc: '', + args: [], + ); + } + + /// `Open in Explorer` + String get openInExplorer { + return Intl.message( + 'Open in Explorer', + name: 'openInExplorer', + desc: '', + args: [], + ); + } + + /// `N/A` + String get na { + return Intl.message( + 'N/A', + name: 'na', + desc: '', + args: [], + ); + } + + /// `ADD` + String get add { + return Intl.message( + 'ADD', + name: 'add', + desc: '', + args: [], + ); + } +} + +class AppLocalizationDelegate extends LocalizationsDelegate { + const AppLocalizationDelegate(); + + List get supportedLocales { + return const [ + Locale.fromSubtags(languageCode: 'en'), + Locale.fromSubtags(languageCode: 'fr'), + ]; + } + + @override + bool isSupported(Locale locale) => _isSupported(locale); + @override + Future load(Locale locale) => S.load(locale); + @override + bool shouldReload(AppLocalizationDelegate old) => false; + + bool _isSupported(Locale locale) { + if (locale != null) { + for (var supportedLocale in supportedLocales) { + if (supportedLocale.languageCode == locale.languageCode) { + return true; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/lib/l10n/.gitignore b/lib/l10n/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/lib/l10n/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/lib/l10n/index.js b/lib/l10n/index.js new file mode 100644 index 0000000..b23e31e --- /dev/null +++ b/lib/l10n/index.js @@ -0,0 +1,8 @@ +const _ = require('lodash') +const fs = require('fs') + +const t = fs.readFileSync('./intl_en.arb') +const tr = JSON.parse(t) + +const tr2 = _.mapValues(tr, v => '********') +console.log(JSON.stringify(tr2)) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb new file mode 100644 index 0000000..109ec15 --- /dev/null +++ b/lib/l10n/intl_en.arb @@ -0,0 +1,111 @@ +{ + "version": "Version", + "about": "About", + "ok": "OK", + "account": "Account", + "notes": "Notes", + "history": "History", + "budget": "Budget", + "tradingPl": "Trading P&L", + "contacts": "Contacts", + "accounts": "Accounts", + "backup": "Backup", + "rescan": "Rescan", + "coldStorage": "Cold Storage", + "multipay": "MultiPay", + "broadcast": "Broadcast", + "settings": "Settings", + "synching": "Synching", + "tapQrCodeForShieldedAddress": "Tap QR Code for Shielded Address", + "tapQrCodeForTransparentAddress": "Tap QR Code for Transparent Address", + "addressCopiedToClipboard": "Address copied to clipboard", + "shieldTransparentBalance": "Shield Transparent Balance", + "doYouWantToTransferYourEntireTransparentBalanceTo": "Do you want to transfer your entire transparent balance to your shielded address?", + "shieldingInProgress": "Shielding in progress...", + "txId": "TX ID:", + "pleaseAuthenticateToShowAccountSeed": "Please authenticate to show account seed", + "noAuthenticationMethod": "No Authentication Method", + "rescanWalletFromTheFirstBlock": "Rescan wallet from the first block?", + "cancel": "Cancel", + "rescanRequested": "Rescan Requested...", + "doYouWantToDeleteTheSecretKeyAndConvert": "Do you want to DELETE the secret key and convert this account to a watch-only account? You will not be able to spend from this device anymore. This operation is NOT reversible.", + "delete": "DELETE", + "confs": "Confs", + "height": "Height", + "datetime": "Date/Time", + "amount": "Amount", + "selectNotesToExcludeFromPayments": "Select notes to EXCLUDE from payments", + "txid": "TXID", + "largestSpendingsByAddress": "Largest Spendings by Address", + "tapChartToToggleBetweenAddressAndAmount": "Tap Chart to Toggle between Address and Amount", + "accountBalanceHistory": "Account Balance History", + "noSpendingInTheLast30Days": "No Spending in the Last 30 Days", + "largestSpendingLastMonth": "Largest Spending Last Month", + "balance": "Balance", + "pnl": "Pnl", + "real": "Real", + "mm": "M/M", + "total": "Total", + "price": "Price", + "qty": "Qty", + "table": "Table", + "pl": "P/L", + "realized": "Realized", + "toMakeAContactSendThemAMemoWithContact": "To make a contact, send them a memo with Contact:", + "newSnapAddress": "New Snap Address", + "shieldTranspBalance": "Shield Transp. Balance", + "send": "Send", + "noAccount": "No account", + "seed": "Seed", + "confirmDeleteAccount": "Are you SURE you want to DELETE this account? You MUST have a BACKUP to recover it. This operation is NOT reversible.", + "changeAccountName": "Change Account Name", + "backupDataRequiredForRestore": "Backup Data - Required for Restore", + "secretKey": "Secret Key", + "viewingKey": "Viewing Key", + "tapAnIconToShowTheQrCode": "Tap an icon to show the QR code", + "multiPay": "Multi Pay", + "pleaseConfirm": "Please Confirm", + "sendingATotalOfAmountCointickerToCountRecipients": "Sending a total of {amount} {ticker} to {count} recipients", + "preparingTransaction": "Preparing transaction...", + "sendCointickerTo": "Send {ticker} to...", + "addressIsEmpty": "Address is empty", + "invalidAddress": "Invalid Address", + "amountMustBeANumber": "Amount must be a number", + "amountMustBePositive": "Amount must be positive", + "accountName": "Account Name", + "accountNameIsRequired": "Account name is required", + "enterSeed": "Enter Seed, Secret Key or Viewing Key. Leave blank for a new account", + "scanStartingMomentarily": "Scan starting momentarily", + "key": "Key", + "sendCointicker": "Send {ticker}", + "max": "MAX", + "advancedOptions": "Advanced Options", + "memo": "Memo", + "roundToMillis": "Round to millis", + "useSettingscurrency": "Use {currency}", + "includeFeeInAmount": "Include Fee in Amount", + "maxAmountPerNote": "Max Amount per Note", + "spendable": "Spendable:", + "notEnoughBalance": "Not enough balance", + "approve": "APPROVE", + "sendingAzecCointickerToAddress": "Sending {aZEC} {ticker} to {address}", + "unsignedTransactionFile": "Unsigned Transaction File", + "amountInSettingscurrency": "Amount in {currency}", + "custom": "Custom", + "server": "Server", + "blue": "Blue", + "pink": "Pink", + "coffee": "Coffee", + "light": "Light", + "dark": "Dark", + "currency": "Currency", + "numberOfConfirmationsNeededBeforeSpending": "Number of Confirmations Needed before Spending", + "retrieveTransactionDetails": "Retrieve Transaction Details", + "theme": "Theme", + "transactionDetails": "Transaction Details", + "timestamp": "Timestamp", + "address": "Address", + "openInExplorer": "Open in Explorer", + "na": "N/A", + "add": "ADD" +} diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb new file mode 100644 index 0000000..35cf8d4 --- /dev/null +++ b/lib/l10n/intl_fr.arb @@ -0,0 +1 @@ +{"version":"********","about":"********","ok":"********","account":"********","notes":"********","history":"********","budget":"********","tradingPl":"********","contacts":"********","accounts":"********","backup":"********","rescan":"********","coldStorage":"********","multipay":"********","broadcast":"********","settings":"********","synching":"********","tapQrCodeForShieldedAddress":"********","tapQrCodeForTransparentAddress":"********","addressCopiedToClipboard":"********","shieldTransparentBalance":"********","doYouWantToTransferYourEntireTransparentBalanceTo":"********","shieldingInProgress":"********","txId":"********","pleaseAuthenticateToShowAccountSeed":"********","noAuthenticationMethod":"********","rescanWalletFromTheFirstBlock":"********","cancel":"********","rescanRequested":"********","doYouWantToDeleteTheSecretKeyAndConvert":"********","delete":"********","confs":"********","height":"********","datetime":"********","amount":"********","selectNotesToExcludeFromPayments":"********","txid":"********","largestSpendingsByAddress":"********","tapChartToToggleBetweenAddressAndAmount":"********","accountBalanceHistory":"********","noSpendingInTheLast30Days":"********","largestSpendingLastMonth":"********","balance":"********","pnl":"********","real":"********","mm":"********","total":"********","price":"********","qty":"********","table":"********","pl":"********","realized":"********","toMakeAContactSendThemAMemoWithContact":"********","newSnapAddress":"********","shieldTranspBalance":"********","send":"********","noAccount":"********","seed":"********","confirmDeleteAccount":"********","changeAccountName":"********","backupDataRequiredForRestore":"********","secretKey":"********","viewingKey":"********","tapAnIconToShowTheQrCode":"********","multiPay":"********","pleaseConfirm":"********","sendingATotalOfAmountCointickerToCountRecipients":"********","preparingTransaction":"********","sendCointickerTo":"********","addressIsEmpty":"********","invalidAddress":"********","amountMustBeANumber":"********","amountMustBePositive":"********","accountName":"********","accountNameIsRequired":"********","enterSeed":"********","scanStartingMomentarily":"********","key":"********","sendCointicker":"********","max":"********","advancedOptions":"********","memo":"********","roundToMillis":"********","useSettingscurrency":"********","includeFeeInAmount":"********","maxAmountPerNote":"********","spendable":"********","notEnoughBalance":"********","approve":"********","sendingAzecCointickerToAddress":"********","unsignedTransactionFile":"********","amountInSettingscurrency":"********","custom":"********","server":"********","blue":"********","pink":"********","coffee":"********","light":"********","dark":"********","currency":"********","numberOfConfirmationsNeededBeforeSpending":"********","retrieveTransactionDetails":"********","theme":"********","transactionDetails":"********","timestamp":"********","address":"********","openInExplorer":"********","na":"********","add": "********"} diff --git a/lib/l10n/package.json b/lib/l10n/package.json new file mode 100644 index 0000000..6a2768f --- /dev/null +++ b/lib/l10n/package.json @@ -0,0 +1,9 @@ +{ + "name": "l10n", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } +} diff --git a/lib/l10n/placeholder.arb b/lib/l10n/placeholder.arb new file mode 100644 index 0000000..15c3253 --- /dev/null +++ b/lib/l10n/placeholder.arb @@ -0,0 +1 @@ +{"version":"********","about":"********","ok":"********","account":"********","notes":"********","history":"********","budget":"********","tradingPl":"********","contacts":"********","accounts":"********","backup":"********","rescan":"********","coldStorage":"********","multipay":"********","broadcast":"********","settings":"********","synching":"********","tapQrCodeForShieldedAddress":"********","tapQrCodeForTransparentAddress":"********","addressCopiedToClipboard":"********","shieldTransparentBalance":"********","doYouWantToTransferYourEntireTransparentBalanceTo":"********","shieldingInProgress":"********","txId":"********","pleaseAuthenticateToShowAccountSeed":"********","noAuthenticationMethod":"********","rescanWalletFromTheFirstBlock":"********","cancel":"********","rescanRequested":"********","doYouWantToDeleteTheSecretKeyAndConvert":"********","delete":"********","confs":"********","height":"********","datetime":"********","amount":"********","selectNotesToExcludeFromPayments":"********","txid":"********","largestSpendingsByAddress":"********","tapChartToToggleBetweenAddressAndAmount":"********","accountBalanceHistory":"********","noSpendingInTheLast30Days":"********","largestSpendingLastMonth":"********","balance":"********","pnl":"********","real":"********","mm":"********","total":"********","price":"********","qty":"********","table":"********","pl":"********","realized":"********","toMakeAContactSendThemAMemoWithContact":"********","newSnapAddress":"********","shieldTranspBalance":"********","send":"********","noAccount":"********","seed":"********","confirmDeleteAccount":"********","changeAccountName":"********","backupDataRequiredForRestore":"********","secretKey":"********","viewingKey":"********","tapAnIconToShowTheQrCode":"********","multiPay":"********","pleaseConfirm":"********","sendingATotalOfAmountCointickerToCountRecipients":"********","preparingTransaction":"********","sendCointickerTo":"********","addressIsEmpty":"********","invalidAddress":"********","amountMustBeANumber":"********","amountMustBePositive":"********","accountName":"********","accountNameIsRequired":"********","enterSeed":"********","scanStartingMomentarily":"********","key":"********","sendCointicker":"********","max":"********","advancedOptions":"********","memo":"********","roundToMillis":"********","useSettingscurrency":"********","includeFeeInAmount":"********","maxAmountPerNote":"********","spendable":"********","notEnoughBalance":"********","approve":"********","sendingAzecCointickerToAddress":"********","unsignedTransactionFile":"********","amountInSettingscurrency":"********","custom":"********","server":"********","blue":"********","pink":"********","coffee":"********","light":"********","dark":"********","currency":"********","numberOfConfirmationsNeededBeforeSpending":"********","retrieveTransactionDetails":"********","theme":"********","transactionDetails":"********","timestamp":"********","address":"********","openInExplorer":"********","na":"********"} diff --git a/lib/l10n/yarn.lock b/lib/l10n/yarn.lock new file mode 100644 index 0000000..bb4db65 --- /dev/null +++ b/lib/l10n/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== diff --git a/lib/main.dart b/lib/main.dart index 58ea09e..3a3180b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,8 @@ import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:warp_api/warp_api.dart'; import 'package:splashscreen/splashscreen.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'generated/l10n.dart'; import 'account.dart'; import 'account_manager.dart'; @@ -51,6 +53,13 @@ void main() { theme: settings.themeData, home: home, scaffoldMessengerKey: rootScaffoldMessengerKey, + localizationsDelegates: [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, onGenerateRoute: (RouteSettings settings) { var routes = { '/account': (context) => AccountPage(), @@ -115,7 +124,7 @@ List confirmButtons(BuildContext context, VoidCallback onPressed return [ ElevatedButton.icon( icon: Icon(Icons.cancel), - label: Text('Cancel'), + label: Text(S.of(context).cancel), onPressed: () { cancelValue != null ? navigator.pop(cancelValue) : navigator.pop(); }, @@ -127,7 +136,7 @@ List confirmButtons(BuildContext context, VoidCallback onPressed ), ElevatedButton.icon( icon: okIcon ?? Icon(Icons.done), - label: Text(okLabel ?? 'OK'), + label: Text(okLabel ?? S.of(context).ok), onPressed: onPressed, ) ]; diff --git a/lib/multisend.dart b/lib/multisend.dart index 8cb163b..4c4b61d 100644 --- a/lib/multisend.dart +++ b/lib/multisend.dart @@ -10,6 +10,7 @@ import 'package:warp/store.dart'; import 'package:warp_api/warp_api.dart'; import 'main.dart'; +import 'generated/l10n.dart'; class MultiPayPage extends StatefulWidget { @override @@ -17,12 +18,10 @@ class MultiPayPage extends StatefulWidget { } class MultiPayState extends State { - final _formKey = GlobalKey(); - @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text('Multi Pay')), + appBar: AppBar(title: Text(S.of(context).multiPay)), body: Observer(builder: (context) { final rows = multipayData.recipients.asMap().entries.map((e) { final index = e.key; @@ -75,22 +74,22 @@ class MultiPayState extends State { context: context, barrierDismissible: false, builder: (BuildContext context) => AlertDialog( - title: Text('Please Confirm'), + title: Text(S.of(context).pleaseConfirm), content: SingleChildScrollView( child: Text( - "Sending a total of $amount ${coin.ticker} to $count recipients")), + S.of(context).sendingATotalOfAmountCointickerToCountRecipients(amount, coin.ticker, count))), actions: confirmButtons(context, () => Navigator.of(context).pop(true), cancelValue: false) )); if (approved) { - final snackBar1 = SnackBar(content: Text("Preparing transaction...")); + final snackBar1 = SnackBar(content: Text(S.of(context).preparingTransaction)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar1); final recipientsJson = jsonEncode(multipayData.recipients); final tx = await WarpApi.sendMultiPayment(accountManager.active.id, recipientsJson, settings.anchorOffset, (p) {}); - final snackBar2 = SnackBar(content: Text("TX ID: $tx")); + final snackBar2 = SnackBar(content: Text(S.of(context).txId + tx)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar2); multipayData.clear(); @@ -122,7 +121,7 @@ class PayRecipientState extends State { Row(children: [ Expanded( child: TextFormField( - decoration: InputDecoration(labelText: 'Send ${coin.ticker} to...'), + decoration: InputDecoration(labelText: S.of(context).sendCointickerTo(coin.ticker)), minLines: 4, maxLines: null, keyboardType: TextInputType.multiline, @@ -134,12 +133,12 @@ class PayRecipientState extends State { icon: new Icon(MdiIcons.qrcodeScan), onPressed: _onScan) ]), TextFormField( - decoration: InputDecoration(labelText: 'Amount'), + decoration: InputDecoration(labelText: S.of(context).amount), keyboardType: TextInputType.number, controller: _currencyController, validator: _checkAmount), ButtonBar(children: - confirmButtons(context, _onAdd, okLabel: 'ADD', okIcon: Icon(MdiIcons.plus))) + confirmButtons(context, _onAdd, okLabel: S.of(context).add, okIcon: Icon(MdiIcons.plus))) ]), )); } @@ -169,15 +168,15 @@ class PayRecipientState extends State { } String _checkAddress(String v) { - if (v.isEmpty) return 'Address is empty'; - if (!WarpApi.validAddress(v)) return 'Invalid Address'; + if (v.isEmpty) return S.of(context).addressIsEmpty; + if (!WarpApi.validAddress(v)) return S.of(context).invalidAddress; return null; } String _checkAmount(String vs) { final v = double.tryParse(vs); - if (v == null) return 'Amount must be a number'; - if (v <= 0.0) return 'Amount must be positive'; + if (v == null) return S.of(context).amountMustBeANumber; + if (v <= 0.0) return S.of(context).amountMustBePositive; return null; } } diff --git a/lib/restore.dart b/lib/restore.dart index 0f158ba..ecf4bd1 100644 --- a/lib/restore.dart +++ b/lib/restore.dart @@ -5,6 +5,7 @@ import 'package:warp_api/warp_api.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'main.dart'; +import 'generated/l10n.dart'; class RestorePage extends StatefulWidget { @override @@ -29,26 +30,22 @@ class _RestorePageState extends State { padding: EdgeInsets.all(8), child: Column(children: [ TextFormField( - decoration: InputDecoration(labelText: 'Account Name'), + decoration: InputDecoration(labelText: S.of(context).accountName), controller: _nameController, validator: (String name) { if (name == null || name.isEmpty) - return "Account name is required"; + return S.of(context).accountNameIsRequired; return null; }, ), Row( - // padding: EdgeInsets.all(8), - // decoration: BoxDecoration( - // border: Border.all(width: 2.0), - // ), children: [ Expanded( child: TextFormField( decoration: InputDecoration( - labelText: 'Key', + labelText: S.of(context).key, hintText: - 'Enter Seed, Secret Key or Viewing Key. Leave blank for a new account'), + S.of(context).enterSeed), minLines: 4, maxLines: 4, controller: _keyController, @@ -59,7 +56,7 @@ class _RestorePageState extends State { ], ), ButtonBar(children: - confirmButtons(context, _validKey ? _onOK : null, okLabel: 'ADD', okIcon: Icon(Icons.add))) + confirmButtons(context, _validKey ? _onOK : null, okLabel: S.of(context).add, okIcon: Icon(Icons.add))) ])))); } @@ -73,7 +70,7 @@ class _RestorePageState extends State { if (_keyController.text == "") WarpApi.skipToLastHeight(); // single new account -> quick sync else { - final snackBar = SnackBar(content: Text("Scan starting momentarily")); + final snackBar = SnackBar(content: Text(S.of(context).scanStartingMomentarily)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar); WarpApi.rewindToHeight(0); } @@ -83,10 +80,6 @@ class _RestorePageState extends State { } } - _onCancel() { - Navigator.of(context).pop(); - } - _checkKey(key) { setState(() { _validKey = key == "" || WarpApi.validKey(key); diff --git a/lib/send.dart b/lib/send.dart index 369aaa9..7974f04 100644 --- a/lib/send.dart +++ b/lib/send.dart @@ -15,6 +15,7 @@ import 'dart:math' as math; import 'main.dart'; import 'store.dart'; +import 'generated/l10n.dart'; class SendPage extends StatefulWidget { final Contact contact; @@ -71,7 +72,7 @@ class SendState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text('Send ${coin.ticker}')), + appBar: AppBar(title: Text(S.of(context).sendCointicker(coin.ticker))), body: Form( key: _formKey, child: SingleChildScrollView( @@ -81,7 +82,7 @@ class SendState extends State { Expanded( child: TextFormField( decoration: - InputDecoration(labelText: 'Send ${coin.ticker} to...'), + InputDecoration(labelText: S.of(context).sendCointickerTo(coin.ticker)), minLines: 4, maxLines: null, keyboardType: TextInputType.multiline, @@ -103,7 +104,7 @@ class SendState extends State { validator: _checkAmount, onChanged: (_) { _updateOtherAmount(); }, onSaved: _onAmount)), - TextButton(child: Text('MAX'), onPressed: _onMax), + TextButton(child: Text(S.of(context).max), onPressed: _onMax), ]), Row(children: [ Expanded( @@ -124,32 +125,32 @@ class SendState extends State { children: [ ExpansionPanel( headerBuilder: (_, __) => - ListTile(title: Text('Advanced Options')), + ListTile(title: Text(S.of(context).advancedOptions)), body: Column(children: [ ListTile( title: TextFormField( - decoration: InputDecoration(labelText: 'Memo'), + decoration: InputDecoration(labelText: S.of(context).memo), minLines: 4, maxLines: null, keyboardType: TextInputType.multiline, controller: _memoController, )), CheckboxListTile( - title: Text('Round to millis'), + title: Text(S.of(context).roundToMillis), value: _mZEC, onChanged: _onChangedmZEC), Observer(builder: (context) => CheckboxListTile( - title: Text('Use ${settings.currency}'), + title: Text(S.of(context).useSettingscurrency(settings.currency)), value: _useFX, onChanged: _onChangedUseFX)), CheckboxListTile( - title: Text('Include Fee in Amount'), + title: Text(S.of(context).includeFeeInAmount), value: _includeFee, onChanged: _onChangedIncludeFee), ListTile( title: TextFormField( decoration: InputDecoration( - labelText: 'Max Amount per Note'), + labelText: S.of(context).maxAmountPerNote), keyboardType: TextInputType.number, controller: _maxAmountPerNoteController, validator: _checkMaxAmountPerNote, @@ -159,31 +160,31 @@ class SendState extends State { isExpanded: _isExpanded) ]), Padding(padding: EdgeInsets.all(8)), - Text("Spendable: ${_balance / ZECUNIT} ${coin.ticker}"), + Text(S.of(context).spendable + '${_balance / ZECUNIT} ${coin.ticker}'), ButtonBar( - children: confirmButtons(context, _onSend, okLabel: 'SEND', okIcon: Icon(MdiIcons.send))) + children: confirmButtons(context, _onSend, okLabel: S.of(context).send, okIcon: Icon(MdiIcons.send))) ])))); } String _checkAddress(String v) { - if (v.isEmpty) return 'Address is empty'; - if (!WarpApi.validAddress(v)) return 'Invalid Address'; + if (v.isEmpty) return S.of(context).addressIsEmpty; + if (!WarpApi.validAddress(v)) return S.of(context).invalidAddress; return null; } String _checkAmount(String vs) { final vss = vs.replaceAll(',', ''); final v = double.tryParse(vss); - if (v == null) return 'Amount must be a number'; - if (v <= 0.0) return 'Amount must be positive'; - if (amountInZAT(Decimal.parse(vss)) > _balance) return 'Not enough balance'; + if (v == null) return S.of(context).amountMustBeANumber; + if (v <= 0.0) return S.of(context).amountMustBePositive; + if (amountInZAT(Decimal.parse(vss)) > _balance) return S.of(context).notEnoughBalance; return null; } String _checkMaxAmountPerNote(String vs) { final v = double.tryParse(vs); - if (v == null) return 'Amount must be a number'; - if (v < 0.0) return 'Amount must be positive'; + if (v == null) return S.of(context).amountMustBeANumber; + if (v < 0.0) return S.of(context).amountMustBePositive; return null; } @@ -263,14 +264,14 @@ class SendState extends State { context: context, barrierDismissible: false, builder: (BuildContext context) => AlertDialog( - title: Text('Please Confirm'), + title: Text(S.of(context).pleaseConfirm), content: SingleChildScrollView( - child: Text("Sending $aZEC ${coin.ticker} to $_address")), - actions: confirmButtons(context, () => Navigator.of(context).pop(true), okLabel: 'APPROVE', cancelValue: false) + child: Text(S.of(context).sendingAzecCointickerToAddress(aZEC, coin.ticker, _address))), + actions: confirmButtons(context, () => Navigator.of(context).pop(true), okLabel: S.of(context).approve, cancelValue: false) )); if (approved) { Navigator.of(context).pop(); - final snackBar1 = SnackBar(content: Text("Preparing transaction...")); + final snackBar1 = SnackBar(content: Text(S.of(context).preparingTransaction)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar1); if (_includeFee) _amount -= DEFAULT_FEE; @@ -289,7 +290,7 @@ class SendState extends State { settings.anchorOffset, progressPort.sendPort)); - final snackBar2 = SnackBar(content: Text("TX ID: $tx")); + final snackBar2 = SnackBar(content: Text(S.of(context).txId + tx)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar2); } else { Directory tempDir = await getTemporaryDirectory(); @@ -298,7 +299,7 @@ class SendState extends State { final msg = WarpApi.prepareTx(accountManager.active.id, _address, _amount, memo, maxAmountPerNote, settings.anchorOffset, filename); - Share.shareFiles([filename], subject: "Unsigned Transaction File"); + Share.shareFiles([filename], subject: S.of(context).unsignedTransactionFile); final snackBar2 = SnackBar(content: Text(msg)); rootScaffoldMessengerKey.currentState.showSnackBar(snackBar2); @@ -313,9 +314,9 @@ class SendState extends State { thousandSeparator: ',', precision: mZEC ? 3 : 8); - String thisAmountLabel() => _useFX ? "Amount in ${settings.currency}" : "Amount in ${coin.ticker}"; + String thisAmountLabel() => S.of(context).amountInSettingscurrency(_useFX ? settings.currency : coin.ticker); - String otherAmountLabel() => _useFX ? "Amount in ${coin.ticker}" : "Amount in ${settings.currency}"; + String otherAmountLabel() => S.of(context).amountInSettingscurrency(_useFX ? coin.ticker : settings.currency); int amountInZAT(Decimal v) => _useFX ? (v / _fx() * ZECUNIT_DECIMAL).toInt() diff --git a/lib/settings.dart b/lib/settings.dart index 7b8bbe9..43396ba 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -3,8 +3,8 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_masked_text/flutter_masked_text.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'account.dart'; import 'main.dart'; +import 'generated/l10n.dart'; class SettingsPage extends StatefulWidget { @override @@ -29,14 +29,14 @@ class SettingsState extends State { value: 'custom', child: FormBuilderTextField( decoration: - InputDecoration(labelText: 'Custom'), + InputDecoration(labelText: S.of(context).custom), initialValue: settings.ldUrl, onSaved: _onURL, )), ); return Scaffold( - appBar: AppBar(title: Text('Settings')), + appBar: AppBar(title: Text(S.of(context).settings)), body: Padding( padding: EdgeInsets.all(16), child: FormBuilder( @@ -46,25 +46,25 @@ class SettingsState extends State { FormBuilderRadioGroup( orientation: OptionsOrientation.vertical, name: 'servers', - decoration: InputDecoration(labelText: 'Server'), + decoration: InputDecoration(labelText: S.of(context).server), initialValue: settings.ldUrlChoice, onSaved: _onChoice, options: options), FormBuilderRadioGroup( orientation: OptionsOrientation.horizontal, name: 'themes', - decoration: InputDecoration(labelText: 'Theme'), + decoration: InputDecoration(labelText: S.of(context).theme), initialValue: settings.theme, onChanged: _onTheme, options: [ FormBuilderFieldOption( child: Text('Zcash'), value: 'zcash'), FormBuilderFieldOption( - child: Text('Blue'), value: 'blue'), + child: Text(S.of(context).blue), value: 'blue'), FormBuilderFieldOption( - child: Text('Pink'), value: 'pink'), + child: Text(S.of(context).pink), value: 'pink'), FormBuilderFieldOption( - child: Text('Coffee'), value: 'coffee'), + child: Text(S.of(context).coffee), value: 'coffee'), ]), FormBuilderRadioGroup( orientation: OptionsOrientation.horizontal, @@ -73,13 +73,13 @@ class SettingsState extends State { onChanged: _onThemeBrightness, options: [ FormBuilderFieldOption( - child: Text('Light'), value: 'light'), + child: Text(S.of(context).light), value: 'light'), FormBuilderFieldOption( - child: Text('Dark'), value: 'dark'), + child: Text(S.of(context).dark), value: 'dark'), ]), Row(children: [ SizedBox(width: 100, child: DropdownButtonFormField( - decoration: InputDecoration(labelText: 'Currency'), + decoration: InputDecoration(labelText: S.of(context).currency), value: settings.currency, items: settings.currencies.map((c) => DropdownMenuItem(child: Text(c), value: c)).toList(), @@ -89,14 +89,14 @@ class SettingsState extends State { FormBuilderTextField( decoration: InputDecoration( labelText: - 'Number of Confirmations Needed before Spending'), + S.of(context).numberOfConfirmationsNeededBeforeSpending), name: 'anchor', keyboardType: TextInputType.number, controller: _anchorController, onSaved: _onAnchorOffset), FormBuilderCheckbox( name: 'get_tx', - title: Text('Retrieve Transaction Details'), + title: Text(S.of(context).retrieveTransactionDetails), initialValue: settings.getTx, onSaved: _onGetTx), ButtonBar(children: diff --git a/lib/transaction.dart b/lib/transaction.dart index ed0c3de..8c9a113 100644 --- a/lib/transaction.dart +++ b/lib/transaction.dart @@ -3,6 +3,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'main.dart'; import 'store.dart'; +import 'generated/l10n.dart'; class TransactionPage extends StatefulWidget { final Tx tx; @@ -18,20 +19,20 @@ class TransactionState extends State { Widget build(BuildContext context) { final textTheme = Theme.of(context).textTheme; return Scaffold( - appBar: AppBar(title: Text('Transaction Details')), + appBar: AppBar(title: Text(S.of(context).transactionDetails)), body: ListView(padding: EdgeInsets.all(16), children: [ ListTile( - title: Text('TXID'), subtitle: SelectableText('${widget.tx.fullTxId}')), + title: Text(S.of(context).txid), subtitle: SelectableText('${widget.tx.fullTxId}')), ListTile( - title: Text('Height'), subtitle: SelectableText('${widget.tx.height}')), + title: Text(S.of(context).height), subtitle: SelectableText('${widget.tx.height}')), ListTile( - title: Text('Timestamp'), + title: Text(S.of(context).timestamp), subtitle: Text('${widget.tx.timestamp}')), - ListTile(title: Text('Amount'), subtitle: SelectableText('${widget.tx.value.toStringAsFixed(8)}')), + ListTile(title: Text(S.of(context).amount), subtitle: SelectableText('${widget.tx.value.toStringAsFixed(8)}')), ListTile( - title: Text('Address'), subtitle: SelectableText('${widget.tx.address ?? "N/A"}')), - ListTile(title: Text('Memo'), subtitle: SelectableText('${widget.tx.memo ?? "N/A"}')), - ElevatedButton(onPressed: _onOpen, child: Text('Open in Explorer')) + title: Text(S.of(context).address), subtitle: SelectableText('${widget.tx.address ?? S.of(context).na}')), + ListTile(title: Text(S.of(context).memo), subtitle: SelectableText('${widget.tx.memo ?? S.of(context).na}')), + ElevatedButton(onPressed: _onOpen, child: Text(S.of(context).openInExplorer)) ])); } diff --git a/pubspec.lock b/pubspec.lock index e16a5e8..272e5b3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -287,7 +287,7 @@ packages: source: hosted version: "0.0.1" flutter_localizations: - dependency: transitive + dependency: "direct main" description: flutter source: sdk version: "0.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f8ed378..36931b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,8 @@ dependencies: path_provider: any file_picker: any mustache_template: any + flutter_localizations: + sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. @@ -117,3 +119,5 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages +flutter_intl: + enabled: true