diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 86043f446..eb58fbec5 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -6,7 +6,6 @@ }, "onBoardingBody": "Secure your 2FA codes", "onBoardingGetStarted": "Get Started", - "setupFirstAccount": "Setup your first account", "importScanQrCode": "Scan a QR Code", "importEnterSetupKey": "Enter a setup key", @@ -18,7 +17,100 @@ "timeBasedKeyType": "Time based (TOTP)", "counterBasedKeyType": "Counter based (HOTP)", "saveAction": "Save", - + "nextTotpTitle": "next", + "deleteCodeTitle": "Delete code?", + "deleteCodeMessage": "Are you sure you want to delete this code? This action is irreversible.", + "viewLogsAction": "View logs", + "sendLogsDescription": "This will send across logs to help us debug your issue. While we take precautions to ensure that sensitive information is not logged, we encourage you to view these logs before sharing them.", + "preparingLogsTitle": "Preparing logs...", + "emailLogsTitle": "Email logs", + "emailLogsMessage": "Please send the logs to {email}", + "@emailLogsMessage": { + "placeholders": { + "email": { + "type": "String" + } + } + }, + "copyEmailAction": "Copy email", + "exportLogsAction": "Export logs", + "reportABug": "Report a bug", + "crashAndErrorReporting": "Crash & error reporting", + "reportBug": "Report bug", + "emailUsMessage": "Please email us at {email}", + "@emailUsMessage": { + "placeholders": { + "email": { + "type": "String" + } + } + }, + "contactSupport": "Contact support", + "verifyPassword": "Verify password", + "pleaseWaitTitle": "Please wait...", + "generatingEncryptionKeysTitle": "Generating encryption keys...", + "recreatePassword": "Recreate password", + "recreatePasswordMessage": "The current device is not powerful enough to verify your password, so we need to regenerate it once in a way that works with all devices. \n\nPlease login using your recovery key and regenerate your password (you can use the same one again if you wish).", + "useRecoveryKeyAction": "Use recovery key", + "incorrectPassword": "Incorrect password", + "welcomeBackTitle": "Welcome back!", + "madeWithLoveAtPrefix": "made with ❤️ at ", + "changeEmail": "change email", + "ok": "OK", + "cancel": "Cancel", + "yes": "Yes", + "no": "No", + "email": "Email", + "support": "Support", + "settings": "Settings", + "copied": "Copied", + "tryAgainMessage": "Please try again", "existingUser": "Existing User", - "newUser" : "New to ente" -} \ No newline at end of file + "newUser" : "New to ente", + "delete": "Delete", + "enterYourPasswordHint": "Enter your password", + "forgotPassword": "Forgot password", + "oops": "Oops", + "somethingWentWrongMessage": "Something went wrong, please try again", + "leaveFamily": "Leave family", + "leaveFamilyMessage": "Are you sure that you want to leave the family plan?", + "inFamilyPlanMessage": "You are on a family plan!", + "swipeHint": "Swipe left to edit or remove codes", + "scan": "Scan", + "scanACode": "Scan a code", + "verify": "Verify", + "enterCodeHint": "Enter the 6-digit code from\nyour authenticator app", + "lostDeviceTitle": "Lost device?", + "twoFactorAuthTitle": "Two-factor authentication", + "recoverAccount": "Recover account", + "enterRecoveryKeyHint": "Enter your recovery key", + "recover": "Recover", + "contactSupportViaEmailMessage":"Please drop an email to {email} from your registered email address", + "@contactSupportViaEmailMessage": { + "placeholders": { + "email": { + "type": "String" + } + } + }, + "noRecoveryKeyTitle": "No recovery key?", + "enterEmailHint": "Enter your email address", + "invalidEmailTitle": "Invalid email address", + "invalidEmailMessage": "Please enter a valid email address.", + "deleteAccount": "Delete account", + "deleteAccountQuery": "We'll be sorry to see you go. Are you facing some issue?", + "yesSendFeedbackAction": "Yes, send feedback", + "noDeleteAccountAction": "No, delete account", + "initiateAccountDeleteTitle": "Please authenticate to initiate account deletion", + "confirmAccountDeleteTitle": "Are you sure you want to delete your ente account?", + "confirmAccountDeleteMessage": "Your uploaded data, across all apps (Photos and Authenticator both), will be scheduled for deletion, and your account will be permanently deleted.", + "sendEmail": "Send email", + "createNewAccount": "Create new account", + "passwordStrengthWeak": "Weak", + "passwordStrengthStrong": "Strong", + "passwordStrengthModerate": "Moderate", + "confirmPassword": "Confirm password", + "close": "Close", + "oopsSomethingWentWrong": "Oops, Something went wrong." + +} diff --git a/lib/services/user_service.dart b/lib/services/user_service.dart index df1359c2a..e3e9ee174 100644 --- a/lib/services/user_service.dart +++ b/lib/services/user_service.dart @@ -7,6 +7,7 @@ import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/events/user_details_changed_event.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/delete_account.dart'; import 'package:ente_auth/models/key_attributes.dart'; import 'package:ente_auth/models/key_gen_result.dart'; @@ -47,7 +48,8 @@ class UserService { bool isChangeEmail = false, bool isCreateAccountScreen = false, }) async { - final dialog = createProgressDialog(context, "Please wait..."); + final l10n = context.l10n; + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { final response = await _dio.post( @@ -191,7 +193,8 @@ class UserService { Future getDeleteChallenge( BuildContext context, ) async { - final dialog = createProgressDialog(context, "Please wait..."); + final l10n = context.l10n; + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { final response = await _dio.get( @@ -259,7 +262,8 @@ class UserService { } Future verifyEmail(BuildContext context, String ott) async { - final dialog = createProgressDialog(context, "Please wait..."); + final l10n = context.l10n; + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { final response = await _dio.post( @@ -329,7 +333,8 @@ class UserService { String email, String ott, ) async { - final dialog = createProgressDialog(context, "Please wait..."); + final l10n = context.l10n; + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { final response = await _dio.post( @@ -498,7 +503,8 @@ class UserService { } Future recoverTwoFactor(BuildContext context, String sessionID) async { - final dialog = createProgressDialog(context, "Please wait..."); + final l10n = context.l10n; + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { final response = await _dio.get( @@ -618,7 +624,8 @@ class UserService { String encryptedSecret, String secretDecryptionNonce, ) async { - final dialog = createProgressDialog(context, "Please wait..."); + final l10n = context.l10n; + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); String secret; try { diff --git a/lib/ui/account/change_email_dialog.dart b/lib/ui/account/change_email_dialog.dart index 1f55d5f26..ce505ff7a 100644 --- a/lib/ui/account/change_email_dialog.dart +++ b/lib/ui/account/change_email_dialog.dart @@ -1,5 +1,6 @@ // @dart=2.9 +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/email_util.dart'; @@ -17,20 +18,21 @@ class _ChangeEmailDialogState extends State { @override Widget build(BuildContext context) { + final l10n = context.l10n; return AlertDialog( - title: const Text("Enter your email address"), + title: Text(l10n.enterEmailHint), content: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( - decoration: const InputDecoration( - hintText: 'Email', - hintStyle: TextStyle( + decoration: InputDecoration( + hintText: l10n.email, + hintStyle: const TextStyle( color: Colors.white30, ), - contentPadding: EdgeInsets.all(12), + contentPadding: const EdgeInsets.all(12), ), onChanged: (value) { setState(() { @@ -47,9 +49,9 @@ class _ChangeEmailDialogState extends State { ), actions: [ TextButton( - child: const Text( - "Cancel", - style: TextStyle( + child: Text( + l10n.cancel, + style: const TextStyle( color: Colors.redAccent, ), ), @@ -58,9 +60,9 @@ class _ChangeEmailDialogState extends State { }, ), TextButton( - child: const Text( - "Verify", - style: TextStyle( + child: Text( + l10n.verify, + style: const TextStyle( color: Colors.purple, ), ), @@ -68,8 +70,8 @@ class _ChangeEmailDialogState extends State { if (!isValidEmail(_email)) { showErrorDialog( context, - "Invalid email address", - "Please enter a valid email address.", + l10n.invalidEmailTitle, + l10n.invalidEmailMessage, ); return; } diff --git a/lib/ui/account/delete_account_page.dart b/lib/ui/account/delete_account_page.dart index 8eedb3fcd..0cadad100 100644 --- a/lib/ui/account/delete_account_page.dart +++ b/lib/ui/account/delete_account_page.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:ente_auth/core/configuration.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/delete_account.dart'; import 'package:ente_auth/services/local_authentication_service.dart'; import 'package:ente_auth/services/user_service.dart'; @@ -20,10 +21,11 @@ class DeleteAccountPage extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = context.l10n; return Scaffold( appBar: AppBar( elevation: 0, - title: const Text("Delete account"), + title: Text(l10n.deleteAccount), leading: IconButton( icon: const Icon(Icons.arrow_back), color: Theme.of(context).iconTheme.color, @@ -47,7 +49,7 @@ class DeleteAccountPage extends StatelessWidget { ), Center( child: Text( - "We'll be sorry to see you go. Are you facing some issue?", + l10n.deleteAccountQuery, style: Theme.of(context).textTheme.subtitle1, ), ), @@ -74,7 +76,7 @@ class DeleteAccountPage extends StatelessWidget { height: 24, ), GradientButton( - text: "Yes, send feedback", + text: l10n.yesSendFeedbackAction, iconData: Icons.check, onTap: () async { await sendEmail( @@ -104,9 +106,9 @@ class DeleteAccountPage extends StatelessWidget { ), backgroundColor: Colors.white, ), - label: const Text( - "No, delete account", - style: TextStyle( + label: Text( + l10n.noDeleteAccountAction, + style: const TextStyle( color: Colors.redAccent, // same for both themes ), textAlign: TextAlign.center, @@ -143,21 +145,20 @@ class DeleteAccountPage extends StatelessWidget { BuildContext context, DeleteChallengeResponse response, ) async { + final l10n = context.l10n; final hasAuthenticated = await LocalAuthenticationService.instance.requestLocalAuthentication( context, - "Please authenticate to initiate account deletion", + l10n.initiateAccountDeleteTitle, ); if (hasAuthenticated) { final choice = await showChoiceDialog( context, - 'Are you sure you want to delete your ente account?', - 'Your uploaded data, across all apps ' - '(Photos and Authenticator both), will be scheduled for deletion,' - 'and your account will be permanently deleted.', - firstAction: 'Cancel', - secondAction: 'Delete', + l10n.confirmAccountDeleteTitle, + l10n.confirmAccountDeleteMessage, + firstAction: l10n.cancel, + secondAction: l10n.delete, firstActionColor: Theme.of(context).colorScheme.onSurface, secondActionColor: Colors.red, ); @@ -175,10 +176,11 @@ class DeleteAccountPage extends StatelessWidget { } Future _requestEmailForDeletion(BuildContext context) async { + final l10n = context.l10n; final AlertDialog alert = AlertDialog( - title: const Text( - "Delete account", - style: TextStyle( + title: Text( + l10n.deleteAccount, + style: const TextStyle( color: Colors.red, ), ), @@ -208,9 +210,9 @@ class DeleteAccountPage extends StatelessWidget { ), actions: [ TextButton( - child: const Text( - "Send email", - style: TextStyle( + child: Text( + l10n.sendEmail, + style: const TextStyle( color: Colors.red, ), ), @@ -225,7 +227,7 @@ class DeleteAccountPage extends StatelessWidget { ), TextButton( child: Text( - "Ok", + l10n.ok, style: TextStyle( color: Theme.of(context).colorScheme.onSurface, ), diff --git a/lib/ui/account/email_entry_page.dart b/lib/ui/account/email_entry_page.dart index d30feead3..a5f9225ff 100644 --- a/lib/ui/account/email_entry_page.dart +++ b/lib/ui/account/email_entry_page.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:email_validator/email_validator.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/billing_plan.dart'; import 'package:ente_auth/services/billing_service.dart'; import 'package:ente_auth/services/user_service.dart'; @@ -122,13 +123,14 @@ class _EmailEntryPageState extends State { } Widget _getBody() { - var passwordStrengthText = 'Weak'; + final l10n = context.l10n; + var passwordStrengthText = l10n.passwordStrengthWeak; var passwordStrengthColor = Colors.redAccent; if (_passwordStrength > kStrongPasswordStrengthThreshold) { - passwordStrengthText = 'Strong'; + passwordStrengthText = l10n.passwordStrengthStrong; passwordStrengthColor = Colors.greenAccent; } else if (_passwordStrength > kMildPasswordStrengthThreshold) { - passwordStrengthText = 'Moderate'; + passwordStrengthText = l10n.passwordStrengthModerate; passwordStrengthColor = Colors.orangeAccent; } return Column( @@ -141,7 +143,7 @@ class _EmailEntryPageState extends State { padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20), child: Text( - 'Create new account', + l10n.createNewAccount, style: Theme.of(context).textTheme.headline4, ), ), @@ -153,7 +155,7 @@ class _EmailEntryPageState extends State { decoration: InputDecoration( fillColor: _emailIsValid ? _validFieldValueColor : null, filled: true, - hintText: 'Email', + hintText: l10n.email, contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, @@ -268,7 +270,7 @@ class _EmailEntryPageState extends State { decoration: InputDecoration( fillColor: _passwordsMatch ? _validFieldValueColor : null, filled: true, - hintText: "Confirm password", + hintText: l10n.confirmPassword, contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 14, @@ -510,13 +512,14 @@ class PricingWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = context.l10n; return FutureBuilder( future: BillingService.instance.getBillingPlans(), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { return _buildPlans(context, snapshot.data); } else if (snapshot.hasError) { - return const Text("Oops, Something went wrong."); + return Text(l10n.oopsSomethingWentWrong); } return const EnteLoadingWidget(); }, @@ -524,6 +527,7 @@ class PricingWidget extends StatelessWidget { } Container _buildPlans(BuildContext context, BillingPlans plans) { + final l10n = context.l10n; final planWidgets = []; for (final plan in plans.plans) { final productID = Platform.isAndroid ? plan.androidID : plan.iosID; @@ -564,16 +568,16 @@ class PricingWidget extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, - children: const [ - Icon( + children: [ + const Icon( Icons.close, size: 12, color: Colors.white38, ), - Padding(padding: EdgeInsets.all(1)), + const Padding(padding: EdgeInsets.all(1)), Text( - "Close", - style: TextStyle( + l10n.close, + style: const TextStyle( color: Colors.white38, ), ), diff --git a/lib/ui/account/password_entry_page.dart b/lib/ui/account/password_entry_page.dart index 928cfc94e..8e93e9247 100644 --- a/lib/ui/account/password_entry_page.dart +++ b/lib/ui/account/password_entry_page.dart @@ -1,6 +1,7 @@ // @dart=2.9 import 'package:ente_auth/core/configuration.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/account/recovery_key_page.dart'; import 'package:ente_auth/ui/common/dynamic_fab.dart'; @@ -400,15 +401,16 @@ class _PasswordEntryPageState extends State { } Future _showRecoveryCodeDialog(String password) async { + final l10n = context.l10n; final dialog = - createProgressDialog(context, "Generating encryption keys..."); + createProgressDialog(context, l10n.generatingEncryptionKeysTitle); await dialog.show(); try { final result = await Configuration.instance.generateKey(password); Configuration.instance.setVolatilePassword(null); await dialog.hide(); onDone() async { - final dialog = createProgressDialog(context, "Please wait..."); + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { await UserService.instance.setAttributes(result); diff --git a/lib/ui/account/password_reentry_page.dart b/lib/ui/account/password_reentry_page.dart index d4f35b5e3..98a5b5af5 100644 --- a/lib/ui/account/password_reentry_page.dart +++ b/lib/ui/account/password_reentry_page.dart @@ -2,6 +2,7 @@ import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/errors.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/key_attributes.dart'; import 'package:ente_auth/ui/account/recovery_page.dart'; import 'package:ente_auth/ui/common/dialogs.dart'; @@ -40,6 +41,7 @@ class _PasswordReentryPageState extends State { @override Widget build(BuildContext context) { + final l10n = context.l10n; final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; FloatingActionButtonLocation fabLocation() { @@ -66,10 +68,10 @@ class _PasswordReentryPageState extends State { floatingActionButton: DynamicFAB( isKeypadOpen: isKeypadOpen, isFormValid: _passwordController.text.isNotEmpty, - buttonText: 'Verify password', + buttonText: l10n.verifyPassword, onPressedFunction: () async { FocusScope.of(context).unfocus(); - final dialog = createProgressDialog(context, "Please wait..."); + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { await Configuration.instance.decryptAndSaveSecrets( @@ -81,14 +83,11 @@ class _PasswordReentryPageState extends State { await dialog.hide(); final dialogUserChoice = await showChoiceDialog( context, - "Recreate password", - "The current device is not powerful enough to verify your " - "password, so we need to regenerate it once in a way that " - "works with all devices. \n\nPlease login using your " - "recovery key and regenerate your password (you can use the same one again if you wish).", - firstAction: "Cancel", + l10n.recreatePassword, + l10n.recreatePasswordMessage, + firstAction: l10n.cancel, firstActionColor: Theme.of(context).colorScheme.primary, - secondAction: "Use recovery key", + secondAction: l10n.useRecoveryKeyAction, secondActionColor: Theme.of(context).colorScheme.primary, ); if (dialogUserChoice == DialogUserChoice.secondChoice) { @@ -107,17 +106,17 @@ class _PasswordReentryPageState extends State { final dialogUserChoice = await showChoiceDialog( context, - "Incorrect password", - "Please try again", - firstAction: "Contact Support", + l10n.incorrectPassword, + l10n.tryAgainMessage, + firstAction: l10n.contactSupport, firstActionColor: Theme.of(context).colorScheme.primary, - secondAction: "Ok", + secondAction: l10n.ok, secondActionColor: Theme.of(context).colorScheme.primary, ); if (dialogUserChoice == DialogUserChoice.firstChoice) { await sendLogs( context, - "Contact support", + l10n.contactSupport, "support@ente.io", postShare: () {}, ); @@ -141,6 +140,7 @@ class _PasswordReentryPageState extends State { } Widget _getBody() { + final l10n = context.l10n; return Column( children: [ Expanded( @@ -151,7 +151,7 @@ class _PasswordReentryPageState extends State { padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20), child: Text( - 'Welcome back!', + l10n.welcomeBackTitle, style: Theme.of(context).textTheme.headline4, ), ), @@ -174,7 +174,7 @@ class _PasswordReentryPageState extends State { child: TextFormField( autofillHints: const [AutofillHints.password], decoration: InputDecoration( - hintText: "Enter your password", + hintText: l10n.enterYourPasswordHint, filled: true, contentPadding: const EdgeInsets.all(20), border: UnderlineInputBorder( @@ -236,7 +236,7 @@ class _PasswordReentryPageState extends State { }, child: Center( child: Text( - "Forgot password", + l10n.forgotPassword, style: Theme.of(context).textTheme.subtitle1.copyWith( fontSize: 14, @@ -248,8 +248,10 @@ class _PasswordReentryPageState extends State { GestureDetector( behavior: HitTestBehavior.opaque, onTap: () async { - final dialog = - createProgressDialog(context, "Please wait..."); + final dialog = createProgressDialog( + context, + l10n.pleaseWaitTitle, + ); await dialog.show(); await Configuration.instance.logout(); await dialog.hide(); @@ -258,7 +260,7 @@ class _PasswordReentryPageState extends State { }, child: Center( child: Text( - "Change email", + l10n.changeEmail, style: Theme.of(context).textTheme.subtitle1.copyWith( fontSize: 14, diff --git a/lib/ui/account/sessions_page.dart b/lib/ui/account/sessions_page.dart index 16070c9a8..ff11d21f4 100644 --- a/lib/ui/account/sessions_page.dart +++ b/lib/ui/account/sessions_page.dart @@ -2,6 +2,7 @@ import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/sessions.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/common/loading_widget.dart'; @@ -111,7 +112,8 @@ class _SessionsPageState extends State { } Future _terminateSession(Session session) async { - final dialog = createProgressDialog(context, "Please wait..."); + final l10n = context.l10n; + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { await UserService.instance.terminateSession(session.token); @@ -122,8 +124,8 @@ class _SessionsPageState extends State { _logger.severe('failed to terminate', e, s); showErrorDialog( context, - 'Oops', - "Something went wrong, please try again", + l10n.oops, + l10n.somethingWentWrongMessage, ); } } diff --git a/lib/ui/code_widget.dart b/lib/ui/code_widget.dart index 3a5ec7314..02dc7993f 100644 --- a/lib/ui/code_widget.dart +++ b/lib/ui/code_widget.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:clipboard/clipboard.dart'; import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart'; import 'package:ente_auth/store/code_store.dart'; @@ -49,6 +50,7 @@ class _CodeWidgetState extends State { @override Widget build(BuildContext context) { + final l10n = context.l10n; return Container( margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8, top: 8), child: Slidable( @@ -174,7 +176,7 @@ class _CodeWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - "next", + l10n.nextTotpTitle, style: Theme.of(context).textTheme.caption, ), Text( @@ -224,20 +226,21 @@ class _CodeWidgetState extends State { } void _onDeletePressed(_) { + final l10n = context.l10n; final AlertDialog alert = AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), title: Text( - "Delete code?", + l10n.deleteCodeTitle, style: Theme.of(context).textTheme.headline6, ), - content: const Text( - "Are you sure you want to delete this code? This action is irreversible.", + content: Text( + l10n.deleteCodeMessage, ), actions: [ TextButton( - child: const Text( - "Delete", - style: TextStyle( + child: Text( + l10n.delete, + style: const TextStyle( color: Colors.red, ), ), diff --git a/lib/ui/home_page.dart b/lib/ui/home_page.dart index 89437fa04..92db580bc 100644 --- a/lib/ui/home_page.dart +++ b/lib/ui/home_page.dart @@ -240,6 +240,7 @@ class _HomePageState extends State { } Widget _getCoachMarkWidget() { + final l10n = context.l10n; return GestureDetector( onTap: () async { await PreferenceService.instance.setHasShownCoachMark(true); @@ -268,7 +269,7 @@ class _HomePageState extends State { height: 24, ), Text( - "Swipe left to edit or remove codes", + l10n.swipeHint, style: Theme.of(context).textTheme.headline6, ), const SizedBox( @@ -282,7 +283,7 @@ class _HomePageState extends State { .setHasShownCoachMark(true); setState(() {}); }, - child: const Text("OK"), + child: Text(l10n.ok), ), ) ], diff --git a/lib/ui/payment/child_subscription_widget.dart b/lib/ui/payment/child_subscription_widget.dart index 65cd5b830..46b7ce0b6 100644 --- a/lib/ui/payment/child_subscription_widget.dart +++ b/lib/ui/payment/child_subscription_widget.dart @@ -1,6 +1,7 @@ // @dart=2.9 import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/user_details.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/common/dialogs.dart'; @@ -17,6 +18,7 @@ class ChildSubscriptionWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = context.l10n; final String familyAdmin = userDetails.familyData.members .firstWhere((element) => element.isAdmin) .email; @@ -27,7 +29,7 @@ class ChildSubscriptionWidget extends StatelessWidget { children: [ Center( child: Text( - "You are on a family plan!", + l10n.inFamilyPlanMessage, style: Theme.of(context).textTheme.bodyText1, ), ), @@ -76,9 +78,9 @@ class ChildSubscriptionWidget extends StatelessWidget { const EdgeInsets.symmetric(vertical: 18, horizontal: 100), backgroundColor: Colors.red[500], ), - child: const Text( - "Leave Family", - style: TextStyle( + child: Text( + l10n.leaveFamily, + style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white, // same for both themes @@ -120,19 +122,20 @@ class ChildSubscriptionWidget extends StatelessWidget { } Future _leaveFamilyPlan(BuildContext context) async { + final l10n = context.l10n; final choice = await showChoiceDialog( context, - 'Leave family', - 'Are you sure that you want to leave the family plan?', - firstAction: 'No', - secondAction: 'Yes', + l10n.leaveFamily, + l10n.leaveFamilyMessage, + firstAction: l10n.no, + secondAction: l10n.yes, firstActionColor: Theme.of(context).colorScheme.alternativeColor, secondActionColor: Theme.of(context).colorScheme.onSurface, ); if (choice != DialogUserChoice.secondChoice) { return; } - final dialog = createProgressDialog(context, "Please wait..."); + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { await UserService.instance.leaveFamilyPlan(); diff --git a/lib/ui/payment/payment_web_page.dart b/lib/ui/payment/payment_web_page.dart index 07ded9f87..16ba995c3 100644 --- a/lib/ui/payment/payment_web_page.dart +++ b/lib/ui/payment/payment_web_page.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/subscription.dart'; import 'package:ente_auth/services/billing_service.dart'; import 'package:ente_auth/services/user_service.dart'; @@ -49,7 +50,8 @@ class _PaymentWebPageState extends State { @override Widget build(BuildContext context) { - _dialog = createProgressDialog(context, "Please wait..."); + final l10n = context.l10n; + _dialog = createProgressDialog(context, l10n.pleaseWaitTitle); if (initPaymentUrl == null) { return const EnteLoadingWidget(); } diff --git a/lib/ui/scanner_page.dart b/lib/ui/scanner_page.dart index 408372e2c..97ff1278a 100644 --- a/lib/ui/scanner_page.dart +++ b/lib/ui/scanner_page.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; import 'package:flutter/material.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; @@ -30,9 +31,10 @@ class ScannerPageState extends State { @override Widget build(BuildContext context) { + final l10n = context.l10n; return Scaffold( appBar: AppBar( - title: const Text("Scan"), + title: Text(l10n.scan), ), body: Column( children: [ @@ -47,7 +49,7 @@ class ScannerPageState extends State { Expanded( flex: 1, child: Center( - child: (totp != null) ? Text(totp!) : const Text('Scan a code'), + child: (totp != null) ? Text(totp!) : Text(l10n.scanACode), ), ) ], diff --git a/lib/ui/settings/data_section_widget.dart b/lib/ui/settings/data_section_widget.dart index 0603672f1..dedeada56 100644 --- a/lib/ui/settings/data_section_widget.dart +++ b/lib/ui/settings/data_section_widget.dart @@ -6,6 +6,7 @@ import 'dart:ui'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/services/authenticator_service.dart'; import 'package:ente_auth/services/local_authentication_service.dart'; @@ -215,11 +216,12 @@ class DataSectionWidget extends StatelessWidget { } Future _pickImportFile(BuildContext context) async { + final l10n = context.l10n; FilePickerResult result = await FilePicker.platform.pickFiles(); if (result == null) { return; } - final dialog = createProgressDialog(context, "Please wait..."); + final dialog = createProgressDialog(context, l10n.pleaseWaitTitle); await dialog.show(); try { File file = File(result.files.single.path); @@ -257,7 +259,7 @@ class DataSectionWidget extends StatelessWidget { actions: [ TextButton( child: Text( - "Okay", + l10n.ok, style: TextStyle( color: Theme.of(context).colorScheme.onSurface, ), diff --git a/lib/ui/settings/debug_section_widget.dart b/lib/ui/settings/debug_section_widget.dart index 0e29d8c61..25c8b9ecb 100644 --- a/lib/ui/settings/debug_section_widget.dart +++ b/lib/ui/settings/debug_section_widget.dart @@ -1,6 +1,7 @@ // @dart=2.9 import 'package:ente_auth/core/configuration.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/ui/settings/common_settings.dart'; import 'package:ente_auth/ui/settings/settings_section_title.dart'; import 'package:ente_auth/ui/settings/settings_text_item.dart'; @@ -12,6 +13,8 @@ class DebugSectionWidget extends StatelessWidget { const DebugSectionWidget({Key key}) : super(key: key); @override Widget build(BuildContext context) { + // This is a debug only section not shown to end users, so these strings are + // not translated. return ExpandablePanel( header: const SettingsSectionTitle("Debug"), collapsed: Container(), @@ -38,6 +41,7 @@ class DebugSectionWidget extends StatelessWidget { } void _showKeyAttributesDialog(BuildContext context) { + final l10n = context.l10n; final keyAttributes = Configuration.instance.getKeyAttributes(); final AlertDialog alert = AlertDialog( title: const Text("key attributes"), @@ -73,7 +77,7 @@ class DebugSectionWidget extends StatelessWidget { ), actions: [ TextButton( - child: const Text("OK"), + child: Text(l10n.ok), onPressed: () { Navigator.of(context, rootNavigator: true).pop('dialog'); }, diff --git a/lib/ui/settings/made_with_love_widget.dart b/lib/ui/settings/made_with_love_widget.dart index 345092d00..ff1715efd 100644 --- a/lib/ui/settings/made_with_love_widget.dart +++ b/lib/ui/settings/made_with_love_widget.dart @@ -1,3 +1,4 @@ +import 'package:ente_auth/l10n/l10n.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -8,13 +9,14 @@ class MadeWithLoveWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = context.l10n; return GestureDetector( onTap: () { launchUrl(Uri.parse("https://ente.io")); }, child: RichText( text: TextSpan( - text: "made with ❤️ at ", + text: l10n.madeWithLoveAtPrefix, style: DefaultTextStyle.of(context).style, children: const [ TextSpan( diff --git a/lib/ui/settings/security_section_widget.dart b/lib/ui/settings/security_section_widget.dart index b9aae3fb1..fdf5ba229 100644 --- a/lib/ui/settings/security_section_widget.dart +++ b/lib/ui/settings/security_section_widget.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/local_authentication_service.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/account/sessions_page.dart'; @@ -45,6 +46,7 @@ class _SecuritySectionWidgetState extends State { } Widget _getSectionOptions(BuildContext context) { + final l10n = context.l10n; final List children = []; children.addAll([ MenuItemWidget( @@ -106,7 +108,7 @@ class _SecuritySectionWidgetState extends State { actions: [ TextButton( child: Text( - "No", + l10n.no, style: TextStyle( color: Theme.of(context).colorScheme.defaultTextColor, @@ -119,7 +121,7 @@ class _SecuritySectionWidgetState extends State { ), TextButton( child: Text( - "Yes", + l10n.yes, style: TextStyle( color: Theme.of(context).colorScheme.defaultTextColor, diff --git a/lib/ui/settings/support_section_widget.dart b/lib/ui/settings/support_section_widget.dart index b2c117ca7..09703089b 100644 --- a/lib/ui/settings/support_section_widget.dart +++ b/lib/ui/settings/support_section_widget.dart @@ -2,6 +2,7 @@ import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/core/logging/super_logging.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/components/captioned_text_widget.dart'; import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart'; @@ -21,20 +22,22 @@ class SupportSectionWidget extends StatefulWidget { class _SupportSectionWidgetState extends State { @override Widget build(BuildContext context) { + final l10n = context.l10n; return ExpandableMenuItemWidget( - title: "Support", + title: l10n.support, selectionOptionsWidget: _getSectionOptions(context), leadingIcon: Icons.help_outline_outlined, ); } Widget _getSectionOptions(BuildContext context) { + final l10n = context.l10n; return Column( children: [ sectionOptionSpacing, MenuItemWidget( - captionedTextWidget: const CaptionedTextWidget( - title: "Email", + captionedTextWidget: CaptionedTextWidget( + title: l10n.email, ), pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, @@ -45,14 +48,14 @@ class _SupportSectionWidgetState extends State { ), sectionOptionSpacing, MenuItemWidget( - captionedTextWidget: const CaptionedTextWidget( - title: "Report a bug", + captionedTextWidget: CaptionedTextWidget( + title: l10n.reportABug, ), pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { - await sendLogs(context, "Report bug", "auth@ente.io"); + await sendLogs(context, l10n.reportBug, "auth@ente.io"); }, onDoubleTap: () async { final zipFilePath = await getZippedLogsFile(context); @@ -61,8 +64,8 @@ class _SupportSectionWidgetState extends State { ), sectionOptionSpacing, MenuItemWidget( - captionedTextWidget: const CaptionedTextWidget( - title: "Crash & error reporting", + captionedTextWidget: CaptionedTextWidget( + title: l10n.crashAndErrorReporting, ), trailingSwitch: ToggleSwitchWidget( value: SuperLogging.shouldReportErrors(), diff --git a/lib/ui/settings/title_bar_widget.dart b/lib/ui/settings/title_bar_widget.dart index 5e0df24ba..e11a5c287 100644 --- a/lib/ui/settings/title_bar_widget.dart +++ b/lib/ui/settings/title_bar_widget.dart @@ -1,3 +1,4 @@ +import 'package:ente_auth/l10n/l10n.dart'; import 'package:flutter/material.dart'; class SettingsTitleBarWidget extends StatelessWidget { @@ -5,6 +6,7 @@ class SettingsTitleBarWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = context.l10n; return Container( padding: const EdgeInsets.symmetric(vertical: 4), child: Padding( @@ -19,7 +21,7 @@ class SettingsTitleBarWidget extends StatelessWidget { }, icon: const Icon(Icons.keyboard_double_arrow_left_outlined), ), - const Text("Settings"), + Text(l10n.settings), ], ), ), diff --git a/lib/ui/two_factor_authentication_page.dart b/lib/ui/two_factor_authentication_page.dart index 4876e37e3..589e25922 100644 --- a/lib/ui/two_factor_authentication_page.dart +++ b/lib/ui/two_factor_authentication_page.dart @@ -1,5 +1,6 @@ // @dart=2.9 +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/lifecycle_event_handler.dart'; import 'package:flutter/material.dart'; @@ -47,10 +48,11 @@ class _TwoFactorAuthenticationPageState @override Widget build(BuildContext context) { + final l10n = context.l10n; return Scaffold( appBar: AppBar( - title: const Text( - "Two-factor authentication", + title: Text( + l10n.twoFactorAuthTitle, ), ), body: _getBody(), @@ -58,6 +60,7 @@ class _TwoFactorAuthenticationPageState } Widget _getBody() { + final l10n = context.l10n; final pinPutDecoration = BoxDecoration( border: Border.all( color: Theme.of(context) @@ -73,9 +76,9 @@ class _TwoFactorAuthenticationPageState mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ - const Text( - "Enter the 6-digit code from\nyour authenticator app", - style: TextStyle( + Text( + l10n.enterCodeHint, + style: const TextStyle( height: 1.4, fontSize: 16, ), @@ -121,7 +124,7 @@ class _TwoFactorAuthenticationPageState _verifyTwoFactorCode(_code); } : null, - child: const Text("Verify"), + child: Text(l10n.verify), ), ), const Padding(padding: EdgeInsets.all(30)), @@ -132,10 +135,10 @@ class _TwoFactorAuthenticationPageState }, child: Container( padding: const EdgeInsets.all(10), - child: const Center( + child: Center( child: Text( - "Lost device?", - style: TextStyle( + l10n.lostDeviceTitle, + style: const TextStyle( decoration: TextDecoration.underline, fontSize: 12, ), diff --git a/lib/ui/two_factor_recovery_page.dart b/lib/ui/two_factor_recovery_page.dart index 1f378e5f0..60ce1f021 100644 --- a/lib/ui/two_factor_recovery_page.dart +++ b/lib/ui/two_factor_recovery_page.dart @@ -2,6 +2,7 @@ import 'dart:ui'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:flutter/material.dart'; @@ -27,11 +28,12 @@ class _TwoFactorRecoveryPageState extends State { @override Widget build(BuildContext context) { + final l10n = context.l10n; return Scaffold( appBar: AppBar( - title: const Text( - "Recover account", - style: TextStyle( + title: Text( + l10n.recoverAccount, + style: const TextStyle( fontSize: 18, ), ), @@ -44,9 +46,9 @@ class _TwoFactorRecoveryPageState extends State { Padding( padding: const EdgeInsets.fromLTRB(60, 0, 60, 0), child: TextFormField( - decoration: const InputDecoration( - hintText: "Enter your recovery key", - contentPadding: EdgeInsets.all(20), + decoration: InputDecoration( + hintText: l10n.enterRecoveryKeyHint, + contentPadding: const EdgeInsets.all(20), ), style: const TextStyle( fontSize: 14, @@ -79,7 +81,7 @@ class _TwoFactorRecoveryPageState extends State { ); } : null, - child: const Text("Recover"), + child: Text(l10n.recover), ), ), GestureDetector( @@ -87,15 +89,15 @@ class _TwoFactorRecoveryPageState extends State { onTap: () { showErrorDialog( context, - "Contact support", - "Please drop an email to support@ente.io from your registered email address", + l10n.contactSupport, + l10n.contactSupportViaEmailMessage("support@ente.io"), ); }, child: Container( padding: const EdgeInsets.all(40), child: Center( child: Text( - "No recovery key?", + l10n.noRecoveryKeyTitle, style: TextStyle( decoration: TextDecoration.underline, fontSize: 12, diff --git a/lib/utils/dialog_util.dart b/lib/utils/dialog_util.dart index 0e4f3003a..89a287545 100644 --- a/lib/utils/dialog_util.dart +++ b/lib/utils/dialog_util.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:confetti/confetti.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/ui/common/loading_widget.dart'; import 'package:ente_auth/ui/common/progress_dialog.dart'; import 'package:flutter/material.dart'; @@ -32,6 +33,7 @@ Future showErrorDialog( String title, String content, ) { + final l10n = context.l10n; final AlertDialog alert = AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), title: title.isEmpty @@ -44,7 +46,7 @@ Future showErrorDialog( actions: [ TextButton( child: Text( - "Ok", + l10n.ok, style: TextStyle( color: Theme.of(context).colorScheme.onSurface, ), diff --git a/lib/utils/email_util.dart b/lib/utils/email_util.dart index cf456ecf7..9da345673 100644 --- a/lib/utils/email_util.dart +++ b/lib/utils/email_util.dart @@ -7,6 +7,7 @@ import 'package:email_validator/email_validator.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/logging/super_logging.dart'; import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/ui/common/dialogs.dart'; import 'package:ente_auth/ui/tools/debug/log_file_viewer.dart'; // import 'package:ente_auth/ui/tools/debug/log_file_viewer.dart'; @@ -37,6 +38,7 @@ Future sendLogs( String subject, String body, }) async { + final l10n = context.l10n; final List actions = [ TextButton( child: Row( @@ -48,7 +50,7 @@ Future sendLogs( ), const Padding(padding: EdgeInsets.all(4)), Text( - "View logs", + l10n.viewLogsAction, style: TextStyle( color: Theme.of(context) .colorScheme @@ -88,11 +90,9 @@ Future sendLogs( final List content = []; content.addAll( [ - const Text( - "This will send across logs to help us debug your issue. " - "While we take precautions to ensure that sensitive information is not " - "logged, we encourage you to view these logs before sharing them.", - style: TextStyle( + Text( + l10n.sendLogsDescription, + style: const TextStyle( height: 1.5, fontSize: 16, ), @@ -148,7 +148,8 @@ Future _sendLogs( } Future getZippedLogsFile(BuildContext context) async { - final dialog = createProgressDialog(context, "Preparing logs..."); + final l10n = context.l10n; + final dialog = createProgressDialog(context, l10n.preparingLogsTitle); await dialog.show(); final logsPath = (await getApplicationSupportDirectory()).path; final logsDirectory = Directory(logsPath + "/logs"); @@ -168,12 +169,13 @@ Future shareLogs( String toEmail, String zipFilePath, ) async { + final l10n = context.l10n; final result = await showChoiceDialog( context, - "Email logs", - "Please send the logs to $toEmail", - firstAction: "Copy email", - secondAction: "Export logs", + l10n.emailLogsTitle, + l10n.emailLogsMessage(toEmail), + firstAction: l10n.copyEmailAction, + secondAction: l10n.exportLogsAction, ); if (result != null && result == DialogUserChoice.firstChoice) { await Clipboard.setData(ClipboardData(text: toEmail)); @@ -237,21 +239,22 @@ Future _clientInfo() async { } void _showNoMailAppsDialog(BuildContext context, String toEmail) { + final l10n = context.l10n; showDialog( context: context, builder: (context) { return AlertDialog( - title: Text('Please email us at $toEmail'), + title: Text(l10n.emailUsMessage(toEmail)), actions: [ TextButton( - child: const Text("Copy email"), + child: Text(l10n.copyEmailAction), onPressed: () async { await Clipboard.setData(ClipboardData(text: toEmail)); - showShortToast(context, 'Copied'); + showShortToast(context, l10n.copied); }, ), TextButton( - child: const Text("OK"), + child: Text(l10n.ok), onPressed: () { Navigator.pop(context); },