diff --git a/AdminUi/apps/admin_ui/lib/core/modals/add_quota_dialog.dart b/AdminUi/apps/admin_ui/lib/core/modals/add_quota_dialog.dart index c8e021a0ac..e41b092e8a 100644 --- a/AdminUi/apps/admin_ui/lib/core/modals/add_quota_dialog.dart +++ b/AdminUi/apps/admin_ui/lib/core/modals/add_quota_dialog.dart @@ -82,7 +82,7 @@ class _AddQuotaDialogState extends State<_AddQuotaDialog> { return PopScope( canPop: !_saving, child: AlertDialog( - title: const Text('Add Quota'), + title: Text(context.l10n.addQuota), content: SizedBox( width: 500, child: Column( @@ -92,19 +92,19 @@ class _AddQuotaDialogState extends State<_AddQuotaDialog> { value: _selectedMetric, items: widget.availableMetrics.map((metric) => DropdownMenuItem(value: metric.key, child: Text(metric.displayName))).toList(), onChanged: _saving ? null : (String? selected) => setState(() => _selectedMetric = selected), - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Metric*', + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: '${context.l10n.metric}*', ), ), Gaps.h24, TextField( controller: _maxAmountController, enabled: !_saving, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Max Amount*', - helperText: 'Only numbers greater or equal to 0 are valid.', + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: '${context.l10n.maxAmount}*', + helperText: context.l10n.addQuotaDialog_maxAmount_message, ), inputFormatters: [FilteringTextInputFormatter.digitsOnly], keyboardType: TextInputType.number, @@ -112,17 +112,17 @@ class _AddQuotaDialogState extends State<_AddQuotaDialog> { Gaps.h24, DropdownButtonFormField( value: _selectedPeriod, - items: const [ - DropdownMenuItem(value: 'Hour', child: Text('Hour')), - DropdownMenuItem(value: 'Day', child: Text('Day')), - DropdownMenuItem(value: 'Week', child: Text('Week')), - DropdownMenuItem(value: 'Month', child: Text('Month')), - DropdownMenuItem(value: 'Year', child: Text('Year')), + items: [ + DropdownMenuItem(value: 'Hour', child: Text(context.l10n.hour)), + DropdownMenuItem(value: 'Day', child: Text(context.l10n.day)), + DropdownMenuItem(value: 'Week', child: Text(context.l10n.week)), + DropdownMenuItem(value: 'Month', child: Text(context.l10n.month)), + DropdownMenuItem(value: 'Year', child: Text(context.l10n.year)), ], onChanged: _saving ? null : (String? selected) => setState(() => _selectedPeriod = selected), - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Period*', + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: '${context.l10n.period}*', ), ), if (_errorMessage != null) @@ -143,7 +143,7 @@ class _AddQuotaDialogState extends State<_AddQuotaDialog> { ), FilledButton( onPressed: _isValid && !_saving ? _addQuota : null, - child: const Text('Add'), + child: Text(context.l10n.add), ), ], ), @@ -153,7 +153,7 @@ class _AddQuotaDialogState extends State<_AddQuotaDialog> { Future _addQuota() async { setState(() => _saving = true); - assert(_selectedMetric != null && _maxAmount != null && _selectedPeriod != null, 'Invalid state'); + assert(_selectedMetric != null && _maxAmount != null && _selectedPeriod != null, 'Invalid State'); final response = await widget.addQuota( metricKey: _selectedMetric!, diff --git a/AdminUi/apps/admin_ui/lib/core/modals/confirmation_dialog.dart b/AdminUi/apps/admin_ui/lib/core/modals/confirmation_dialog.dart index c0ec4518d5..32e88a12dd 100644 --- a/AdminUi/apps/admin_ui/lib/core/modals/confirmation_dialog.dart +++ b/AdminUi/apps/admin_ui/lib/core/modals/confirmation_dialog.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import '../extensions.dart'; + Future showConfirmationDialog({ required BuildContext context, required String title, @@ -27,11 +29,11 @@ class _ConfirmationDialog extends StatelessWidget { actions: [ OutlinedButton( onPressed: () => Navigator.of(context).pop(false), - child: const Text('Cancel'), + child: Text(context.l10n.cancel), ), FilledButton( onPressed: () => Navigator.of(context).pop(true), - child: const Text('Confirm'), + child: Text(context.l10n.confirm), ), ], ); diff --git a/AdminUi/apps/admin_ui/lib/core/modals/settings_dialog.dart b/AdminUi/apps/admin_ui/lib/core/modals/settings_dialog.dart index 863f5fe865..f765bec3c4 100644 --- a/AdminUi/apps/admin_ui/lib/core/modals/settings_dialog.dart +++ b/AdminUi/apps/admin_ui/lib/core/modals/settings_dialog.dart @@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart'; import 'package:watch_it/watch_it.dart'; import '../constants.dart'; +import '../extensions.dart'; import '../models/models.dart'; Future openSettingsDialog(BuildContext context) async { @@ -17,26 +18,26 @@ class _SettingsDialog extends StatelessWidget with WatchItMixin { final themeMode = watchValue((ThemeModeModel x) => x.themeMode); return AlertDialog( - title: const Text('Settings'), + title: Text(context.l10n.settings), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Theme', style: Theme.of(context).textTheme.bodyLarge), + Text(context.l10n.theme, style: Theme.of(context).textTheme.bodyLarge), Gaps.h4, SegmentedButton( showSelectedIcon: false, - segments: const [ - ButtonSegment(value: ThemeMode.light, icon: Icon(Icons.light_mode), label: Text('Light')), - ButtonSegment(value: ThemeMode.system, icon: Icon(Icons.settings), label: Text('System')), - ButtonSegment(value: ThemeMode.dark, icon: Icon(Icons.dark_mode), label: Text('Dark')), + segments: [ + ButtonSegment(value: ThemeMode.light, icon: const Icon(Icons.light_mode), label: Text(context.l10n.light)), + ButtonSegment(value: ThemeMode.system, icon: const Icon(Icons.settings), label: Text(context.l10n.system)), + ButtonSegment(value: ThemeMode.dark, icon: const Icon(Icons.dark_mode), label: Text(context.l10n.dark)), ], selected: {themeMode}, onSelectionChanged: (selected) => GetIt.I().setThemeMode(selected.first), ), ], ), - actions: [FilledButton(onPressed: () => context.pop(), child: const Text('Close'))], + actions: [FilledButton(onPressed: () => context.pop(), child: Text(context.l10n.close))], ); } } diff --git a/AdminUi/apps/admin_ui/lib/core/widgets/identities_data_table/identities_data_table.dart b/AdminUi/apps/admin_ui/lib/core/widgets/identities_data_table/identities_data_table.dart index cb946037cb..c5fe40b851 100644 --- a/AdminUi/apps/admin_ui/lib/core/widgets/identities_data_table/identities_data_table.dart +++ b/AdminUi/apps/admin_ui/lib/core/widgets/identities_data_table/identities_data_table.dart @@ -2,6 +2,7 @@ import 'package:data_table_2/data_table_2.dart'; import 'package:flutter/material.dart'; import '../../constants.dart'; +import '../../extensions.dart'; import 'identities_data_table_source.dart'; export 'identities_data_table_source.dart'; @@ -36,26 +37,26 @@ class _IdentitiesDataTableState extends State { isVerticalScrollBarVisible: true, renderEmptyRowsInTheEnd: false, availableRowsPerPage: const [5, 10, 25, 50, 100], - empty: const Text('No identities found.'), + empty: Text(context.l10n.identitiesDataTable_noIdentitiesFound), errorBuilder: (error) => Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ - const Text('An error occurred loading the data.'), + Text(context.l10n.identitiesDataTable_failedToLoadData), Gaps.h16, - FilledButton(onPressed: widget.dataSource.refreshDatasource, child: const Text('Retry')), + FilledButton(onPressed: widget.dataSource.refreshDatasource, child: Text(context.l10n.retry)), ], ), ), columns: [ - DataColumn2(label: const Text('Address'), size: ColumnSize.L, onSort: _sort), - if (!widget.hideTierColumn) const DataColumn2(label: Text('Tier'), size: ColumnSize.S), - DataColumn2(label: const Text('Created with Client'), onSort: _sort), - DataColumn2(label: const Text('Number of Devices'), onSort: _sort), - DataColumn2(label: const Text('Created at'), size: ColumnSize.S, onSort: _sort), - DataColumn2(label: const Text('Last Login at'), size: ColumnSize.S, onSort: _sort), - DataColumn2(label: const Text('Datawallet version'), onSort: _sort), - DataColumn2(label: const Text('Identity Version'), onSort: _sort), + DataColumn2(label: Text(context.l10n.address), size: ColumnSize.L, onSort: _sort), + if (!widget.hideTierColumn) DataColumn2(label: Text(context.l10n.tier), size: ColumnSize.S), + DataColumn2(label: Text(context.l10n.identitiesDataTable_createdWithClient), onSort: _sort), + DataColumn2(label: Text(context.l10n.numberOfDevices), onSort: _sort), + DataColumn2(label: Text(context.l10n.createdAt), size: ColumnSize.S, onSort: _sort), + DataColumn2(label: Text(context.l10n.lastLoginAt), size: ColumnSize.S, onSort: _sort), + DataColumn2(label: Text(context.l10n.datawalletVersion), onSort: _sort), + DataColumn2(label: Text(context.l10n.identityVersion), onSort: _sort), ], ); } diff --git a/AdminUi/apps/admin_ui/lib/core/widgets/identities_data_table/identities_filter.dart b/AdminUi/apps/admin_ui/lib/core/widgets/identities_data_table/identities_filter.dart index f88740fa07..30e6c0dd94 100644 --- a/AdminUi/apps/admin_ui/lib/core/widgets/identities_data_table/identities_filter.dart +++ b/AdminUi/apps/admin_ui/lib/core/widgets/identities_data_table/identities_filter.dart @@ -5,6 +5,7 @@ import 'package:intl/intl.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import '../../constants.dart'; +import '../../extensions.dart'; import '../filters/filters.dart'; class IdentitiesFilter extends StatefulWidget { @@ -60,7 +61,7 @@ class _IdentitiesFilterState extends State { mainAxisSize: MainAxisSize.min, children: [ InputField( - label: 'Address', + label: context.l10n.address, onEnteredText: (String enteredText) { _filter = _filter.copyWith(address: enteredText.isEmpty ? const Optional.absent() : Optional(enteredText)); widget.onFilterChanged(filter: _filter); @@ -69,8 +70,8 @@ class _IdentitiesFilterState extends State { if (widget.fixedTierId == null) ...[ Gaps.w16, MultiSelectFilter( - label: 'Tiers', - searchLabel: 'Search Tiers', + label: context.l10n.tiers, + searchLabel: context.l10n.searchTiers, controller: _tierController, onOptionSelected: (List> selectedOptions) { final selectedTiers = selectedOptions.map((item) => item.value!).toList(); @@ -81,8 +82,8 @@ class _IdentitiesFilterState extends State { ], Gaps.w16, MultiSelectFilter( - label: 'Clients', - searchLabel: 'Search Clients', + label: context.l10n.clients, + searchLabel: context.l10n.identitiesFilter_searchClients, controller: _clientController, onOptionSelected: (List> selectedOptions) { final selectedClients = selectedOptions.map((item) => item.value!).toList(); @@ -92,7 +93,7 @@ class _IdentitiesFilterState extends State { ), Gaps.w16, NumberFilter( - label: 'Number of Devices', + label: context.l10n.numberOfDevices, onNumberSelected: (FilterOperator operator, String enteredValue) { final numberOfDevices = FilterOperatorValue(operator, enteredValue); _filter = _filter.copyWith(numberOfDevices: numberOfDevices.value.isEmpty ? const Optional.absent() : Optional(numberOfDevices)); @@ -101,7 +102,7 @@ class _IdentitiesFilterState extends State { ), Gaps.w16, DateFilter( - label: 'Created At', + label: context.l10n.createdAt, onFilterSelected: (FilterOperator operator, DateTime? selectedDate) { final createdAt = FilterOperatorValue(operator, selectedDate != null ? DateFormat('yyyy-MM-dd').format(selectedDate) : ''); _filter = _filter.copyWith(createdAt: createdAt.value.isEmpty ? const Optional.absent() : Optional(createdAt)); @@ -110,7 +111,7 @@ class _IdentitiesFilterState extends State { ), Gaps.w16, DateFilter( - label: 'Last Login At', + label: context.l10n.lastLoginAt, onFilterSelected: (FilterOperator operator, DateTime? selectedDate) { final lastLoginAt = FilterOperatorValue(operator, selectedDate != null ? DateFormat('yyyy-MM-dd').format(selectedDate) : ''); _filter = _filter.copyWith(lastLoginAt: lastLoginAt.value.isEmpty ? const Optional.absent() : Optional(lastLoginAt)); @@ -119,7 +120,7 @@ class _IdentitiesFilterState extends State { ), Gaps.w16, NumberFilter( - label: 'Datawallet Version', + label: context.l10n.datawalletVersion, onNumberSelected: (FilterOperator operator, String enteredValue) { final datawalletVersion = FilterOperatorValue(operator, enteredValue); _filter = @@ -129,7 +130,7 @@ class _IdentitiesFilterState extends State { ), Gaps.w16, NumberFilter( - label: 'Identity Version', + label: context.l10n.identityVersion, onNumberSelected: (FilterOperator operator, String enteredValue) { final identityVersion = FilterOperatorValue(operator, enteredValue); _filter = _filter.copyWith(identityVersion: identityVersion.value.isEmpty ? const Optional.absent() : Optional(identityVersion)); diff --git a/AdminUi/apps/admin_ui/lib/core/widgets/quotas_button_group.dart b/AdminUi/apps/admin_ui/lib/core/widgets/quotas_button_group.dart index 9c86397c3e..0d35220db1 100644 --- a/AdminUi/apps/admin_ui/lib/core/widgets/quotas_button_group.dart +++ b/AdminUi/apps/admin_ui/lib/core/widgets/quotas_button_group.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import '../constants.dart'; +import '../extensions.dart'; import '../modals/modals.dart'; class QuotasButtonGroup extends StatefulWidget { @@ -62,9 +63,9 @@ class _QuotasButtonGroupState extends State { Future _removeSelectedQuotas() async { final confirmed = await showConfirmationDialog( context: context, - title: 'Remove Quotas', + title: context.l10n.quotaButtonGroup_removeQuotas_title, message: - 'Are you sure you want to remove the selected quotas from ${widget.identityAddress != null ? 'the identity "${widget.identityAddress}"' : 'the tier "${widget.tierId}"'}?', + '${context.l10n.quotaButtonGroup_deletionMessage} ${widget.identityAddress != null ? '${context.l10n.quotaButtonGroup_theIdentity} "${widget.identityAddress}"' : '${context.l10n.quotaButtonGroup_theTier} "${widget.tierId}"'}?', ); if (!confirmed) return; @@ -73,8 +74,8 @@ class _QuotasButtonGroupState extends State { final result = await _deleteQuota(quota); if (result.hasError && mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('An error occurred while deleting the quota(s). Please try again.'), + SnackBar( + content: Text(context.l10n.quotaButtonGroup_errorDeletingQuota), showCloseIcon: true, ), ); @@ -87,8 +88,8 @@ class _QuotasButtonGroupState extends State { widget.selectedQuotas.clear(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Selected quota(s) have been removed.'), + SnackBar( + content: Text(context.l10n.quotaButtonGroup_selectedQuotaRemoved), showCloseIcon: true, ), ); diff --git a/AdminUi/apps/admin_ui/lib/home/clients_overview/clients_filter.dart b/AdminUi/apps/admin_ui/lib/home/clients_overview/clients_filter.dart index 4181beb0d7..af19ed8874 100644 --- a/AdminUi/apps/admin_ui/lib/home/clients_overview/clients_filter.dart +++ b/AdminUi/apps/admin_ui/lib/home/clients_overview/clients_filter.dart @@ -123,7 +123,7 @@ class _ClientsFilterRowState extends State { mainAxisSize: MainAxisSize.min, children: [ InputField( - label: 'Client ID', + label: context.l10n.clientID, onEnteredText: (String enteredText) { filter = filter.copyWith(clientId: enteredText.isEmpty ? const Optional.absent() : Optional(enteredText)); @@ -132,7 +132,7 @@ class _ClientsFilterRowState extends State { ), Gaps.w16, InputField( - label: 'Display Name', + label: context.l10n.displayName, onEnteredText: (String enteredText) { filter = filter.copyWith(displayName: enteredText.isEmpty ? const Optional.absent() : Optional(enteredText)); @@ -141,8 +141,8 @@ class _ClientsFilterRowState extends State { ), Gaps.w16, MultiSelectFilter( - label: 'Default Tier', - searchLabel: 'Search Tiers', + label: context.l10n.defaultTier, + searchLabel: context.l10n.searchTiers, controller: _tierController, onOptionSelected: (List> selectedOptions) { filter = filter.copyWith( @@ -154,7 +154,7 @@ class _ClientsFilterRowState extends State { ), Gaps.w16, NumberFilter( - label: 'Number of Identitites', + label: context.l10n.numberOfIdentities, onNumberSelected: (FilterOperator operator, String enteredValue) { filter = filter.copyWith( numberOfIdentities: enteredValue.isEmpty ? const Optional.absent() : Optional((operator, int.parse(enteredValue))), @@ -165,7 +165,7 @@ class _ClientsFilterRowState extends State { ), Gaps.w16, DateFilter( - label: 'Created At', + label: context.l10n.createdAt, onFilterSelected: (FilterOperator operator, DateTime? selectedDate) { filter = filter.copyWith( createdAt: selectedDate == null ? const Optional.absent() : Optional((operator, selectedDate)), diff --git a/AdminUi/apps/admin_ui/lib/home/clients_overview/clients_overview.dart b/AdminUi/apps/admin_ui/lib/home/clients_overview/clients_overview.dart index 4679c22052..b345ddcd52 100644 --- a/AdminUi/apps/admin_ui/lib/home/clients_overview/clients_overview.dart +++ b/AdminUi/apps/admin_ui/lib/home/clients_overview/clients_overview.dart @@ -33,7 +33,7 @@ class _ClientsOverviewState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('A list of existing Clients')), + appBar: AppBar(title: Text(context.l10n.clientsOverview_title)), body: Card( child: Padding( padding: const EdgeInsets.all(8), @@ -68,7 +68,7 @@ class _ClientsOverviewState extends State { Expanded( child: DataTable2( isVerticalScrollBarVisible: true, - empty: const Text('No clients found.'), + empty: Text(context.l10n.clientsOverview_noClientsFound), onSelectAll: (selected) { if (selected == null) return; @@ -80,13 +80,13 @@ class _ClientsOverviewState extends State { } }); }, - columns: const [ - DataColumn2(label: Text('Client ID'), size: ColumnSize.L), - DataColumn2(label: Text('Display Name'), size: ColumnSize.L), - DataColumn2(label: Text('Default Tier')), - DataColumn2(label: Text('Number of Identities'), size: ColumnSize.L), - DataColumn2(label: Text('Created At')), - DataColumn2(label: Text(''), size: ColumnSize.L), + columns: [ + DataColumn2(label: Text(context.l10n.clientID), size: ColumnSize.L), + DataColumn2(label: Text(context.l10n.displayName), size: ColumnSize.L), + DataColumn2(label: Text(context.l10n.defaultTier)), + DataColumn2(label: Text(context.l10n.numberOfIdentities), size: ColumnSize.L), + DataColumn2(label: Text(context.l10n.createdAt)), + const DataColumn2(label: Text(''), size: ColumnSize.L), ], rows: _originalClients .where((e) => _filter.matches(e)) @@ -121,7 +121,7 @@ class _ClientsOverviewState extends State { style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Theme.of(context).colorScheme.primary)), onPressed: () => showChangeClientSecretDialog(context: context, clientId: client.clientId), child: Text( - 'Change Client Secret', + context.l10n.changeClientSecret, style: TextStyle(color: Theme.of(context).colorScheme.onPrimary), textAlign: TextAlign.center, ), @@ -153,8 +153,8 @@ class _ClientsOverviewState extends State { Future _removeSelectedClients() async { final confirmed = await showConfirmationDialog( context: context, - title: 'Remove Clients', - message: 'Are you sure you want to remove the selected clients?', + title: context.l10n.clientsOverview_removeSelectedClients_title, + message: context.l10n.clientsOverview_removeSelectedClients_message, ); if (!confirmed) return; @@ -163,8 +163,8 @@ class _ClientsOverviewState extends State { final result = await GetIt.I.get().clients.deleteClient(clientId); if (result.hasError && mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('An error occurred while deleting the client(s). Please try again.'), + SnackBar( + content: Text(context.l10n.clientsOverview_removeSelectedClients_error), showCloseIcon: true, ), ); @@ -178,8 +178,8 @@ class _ClientsOverviewState extends State { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Selected clients have been removed.'), + SnackBar( + content: Text(context.l10n.clientsOverview_removeSelectedClients_success), showCloseIcon: true, ), ); diff --git a/AdminUi/apps/admin_ui/lib/home/clients_overview/modals/change_client_secret_dialog.dart b/AdminUi/apps/admin_ui/lib/home/clients_overview/modals/change_client_secret_dialog.dart index cd5e4a2f9a..989953f549 100644 --- a/AdminUi/apps/admin_ui/lib/home/clients_overview/modals/change_client_secret_dialog.dart +++ b/AdminUi/apps/admin_ui/lib/home/clients_overview/modals/change_client_secret_dialog.dart @@ -55,7 +55,7 @@ class _ChangeClientSecretDialogState extends State<_ChangeClientSecretDialog> { return PopScope( canPop: !_saving, child: AlertDialog( - title: const Text('Change Client Secret', textAlign: TextAlign.center), + title: Text(context.l10n.changeClientSecret, textAlign: TextAlign.center), content: SizedBox( width: 500, child: Column( @@ -68,8 +68,8 @@ class _ChangeClientSecretDialogState extends State<_ChangeClientSecretDialog> { obscureText: _isClientSecretVisible, decoration: InputDecoration( border: const OutlineInputBorder(), - labelText: 'Client Secret', - helperText: 'A Client Secret will be generated if this field is left blank.', + labelText: context.l10n.clientSecret, + helperText: context.l10n.clientSecret_message, suffixIcon: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Row( @@ -81,9 +81,9 @@ class _ChangeClientSecretDialogState extends State<_ChangeClientSecretDialog> { ), Gaps.w4, CopyToClipboardButton( - tooltip: 'Copy to clipboard.', + tooltip: context.l10n.changeClientSecretDialog_copyToClipboard, clipboardText: _newClientSecretController.text, - successMessage: 'Client Secret copied to clipboard.', + successMessage: context.l10n.clientSecret_copiedToClipboard, ), ], ), @@ -94,7 +94,7 @@ class _ChangeClientSecretDialogState extends State<_ChangeClientSecretDialog> { Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Text( - 'Please save the Client Secret since it will be inaccessible after exiting.', + context.l10n.clientSecret_save_message, style: TextStyle(color: Theme.of(context).colorScheme.primary), ), ), diff --git a/AdminUi/apps/admin_ui/lib/home/clients_overview/modals/create_client_dialog.dart b/AdminUi/apps/admin_ui/lib/home/clients_overview/modals/create_client_dialog.dart index 75d5de3feb..956b16a3ca 100644 --- a/AdminUi/apps/admin_ui/lib/home/clients_overview/modals/create_client_dialog.dart +++ b/AdminUi/apps/admin_ui/lib/home/clients_overview/modals/create_client_dialog.dart @@ -63,7 +63,7 @@ class _CreateClientDialogState extends State<_CreateClientDialog> { return PopScope( canPop: !_saving, child: AlertDialog( - title: const Text('Create Client', textAlign: TextAlign.center), + title: Text(context.l10n.createClientDialog_title, textAlign: TextAlign.center), content: SizedBox( width: 500, child: Column( @@ -72,20 +72,20 @@ class _CreateClientDialogState extends State<_CreateClientDialog> { TextField( controller: _clientIdController, readOnly: _saveSucceeded, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Client ID', - helperText: 'A Client ID will be generated if this field is left blank.', + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: context.l10n.clientID, + helperText: context.l10n.createClientDialog_clientID_message, ), ), Gaps.h24, TextField( controller: _displayNameController, readOnly: _saveSucceeded, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Display Name', - helperText: 'Client ID will be used as a Display Name if no value is provided.', + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: context.l10n.displayName, + helperText: context.l10n.createClientDialog_displayName_message, ), ), Gaps.h24, @@ -95,8 +95,8 @@ class _CreateClientDialogState extends State<_CreateClientDialog> { obscureText: _isClientSecretVisible, decoration: InputDecoration( border: const OutlineInputBorder(), - labelText: 'Client Secret', - helperText: 'A Client Secret will be generated if this field is left blank.', + labelText: context.l10n.clientSecret, + helperText: context.l10n.clientSecret_message, suffixIcon: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Row( @@ -108,9 +108,9 @@ class _CreateClientDialogState extends State<_CreateClientDialog> { ), Gaps.w4, CopyToClipboardButton( - tooltip: 'Copy to clipboard.', + tooltip: context.l10n.createClientDialog_clientSecret_copyToClipboard, clipboardText: _clientSecretController.text, - successMessage: 'Client Secret copied to clipboard.', + successMessage: context.l10n.clientSecret_copiedToClipboard, ), ], ), @@ -120,7 +120,7 @@ class _CreateClientDialogState extends State<_CreateClientDialog> { if (_saveSucceeded) ...[ Gaps.h16, Text( - 'Please save the Client Secret since it will be inaccessible after exiting.', + context.l10n.clientSecret_save_message, style: TextStyle(color: Theme.of(context).colorScheme.primary), ), ], @@ -128,11 +128,11 @@ class _CreateClientDialogState extends State<_CreateClientDialog> { TextField( controller: _maxIdentitiesController, readOnly: _saveSucceeded, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Max Identities', - helperText: 'The maximum number of Identities that can be created with this Client.' - '\nNo Identity limit will be assigned if this field is left blank.', + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: context.l10n.maxIdentities, + helperText: '${context.l10n.createClientDialog_maxIdentities_message}' + '\n${context.l10n.createClientDialog_maxIdentities_noLimit_message}', ), inputFormatters: [FilteringTextInputFormatter.digitsOnly], keyboardType: TextInputType.number, @@ -140,9 +140,9 @@ class _CreateClientDialogState extends State<_CreateClientDialog> { Gaps.h24, DropdownButtonFormField( isExpanded: true, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Default Tier*', + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: '${context.l10n.defaultTier}*', ), value: _chosenDefaultTier, onChanged: _saveSucceeded ? null : (tier) => setState(() => _chosenDefaultTier = tier), diff --git a/AdminUi/apps/admin_ui/lib/home/identities_overview/identities_overview.dart b/AdminUi/apps/admin_ui/lib/home/identities_overview/identities_overview.dart index c02da15765..573ba07431 100644 --- a/AdminUi/apps/admin_ui/lib/home/identities_overview/identities_overview.dart +++ b/AdminUi/apps/admin_ui/lib/home/identities_overview/identities_overview.dart @@ -35,7 +35,7 @@ class _IdentitiesOverviewState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('A list of existing Identities')), + appBar: AppBar(title: Text(context.l10n.identityOverview_title)), body: Card( child: Padding( padding: const EdgeInsets.all(8), diff --git a/AdminUi/apps/admin_ui/lib/home/identity_details/identity_details.dart b/AdminUi/apps/admin_ui/lib/home/identity_details/identity_details.dart index 43f3ed5f1f..2c98a7a631 100644 --- a/AdminUi/apps/admin_ui/lib/home/identity_details/identity_details.dart +++ b/AdminUi/apps/admin_ui/lib/home/identity_details/identity_details.dart @@ -127,7 +127,7 @@ class _IdentityDetailsCard extends StatelessWidget { Gaps.w16, CopyToClipboardButton( clipboardText: identityDetails.address, - successMessage: 'Identity address copied to clipboard.', + successMessage: context.l10n.identityDetails_card_identityClipboardMessage, ), ], ), @@ -138,26 +138,26 @@ class _IdentityDetailsCard extends StatelessWidget { runSpacing: 8, children: [ _IdentityDetails( - title: 'Client ID', + title: context.l10n.clientID, value: identityDetails.clientId, ), _IdentityDetails( - title: 'Public Key', + title: context.l10n.identityDetails_card_publicKey, value: identityDetails.publicKey.ellipsize(20), onIconPressed: () => context.setClipboardDataWithSuccessNotification( clipboardText: identityDetails.publicKey, - successMessage: 'Public key copied to clipboard.', + successMessage: context.l10n.identityDetails_card_publicKey_copyToClipboardMessage, ), icon: Icons.copy, - tooltipMessage: 'Copy public key', + tooltipMessage: context.l10n.identityDetails_card_publicKey_tooltipMessage, ), _IdentityDetails( - title: 'Created at', + title: context.l10n.createdAt, value: '${DateFormat.yMd(Localizations.localeOf(context).languageCode).format(identityDetails.createdAt)} ${DateFormat.Hms().format(identityDetails.createdAt)}', ), _IdentityDetails( - title: 'Tier', + title: context.l10n.tier, value: currentTier.name, onIconPressed: currentTier.canBeManuallyAssigned ? () => showChangeTierDialog( @@ -168,7 +168,7 @@ class _IdentityDetailsCard extends StatelessWidget { ) : null, icon: Icons.edit, - tooltipMessage: 'Change tier', + tooltipMessage: context.l10n.changeTier, ), ], ), diff --git a/AdminUi/apps/admin_ui/lib/home/identity_details/identity_quotas_table/identity_quotas_table.dart b/AdminUi/apps/admin_ui/lib/home/identity_details/identity_quotas_table/identity_quotas_table.dart index 1256aa1833..5afdc2b64e 100644 --- a/AdminUi/apps/admin_ui/lib/home/identity_details/identity_quotas_table/identity_quotas_table.dart +++ b/AdminUi/apps/admin_ui/lib/home/identity_details/identity_quotas_table/identity_quotas_table.dart @@ -26,8 +26,8 @@ class IdentityQuotaListState extends State { return Theme( data: Theme.of(context).copyWith(dividerColor: Colors.transparent), child: ExpansionTile( - title: const Text('Quotas'), - subtitle: const Text('View and assign quotas for this identity.'), + title: Text(context.l10n.quotas), + subtitle: Text(context.l10n.identityQuotaTable_title_description), children: [ Card( child: Column( @@ -42,19 +42,24 @@ class IdentityQuotaListState extends State { width: double.infinity, height: 500, child: DataTable2( - columns: const [ - DataColumn2(label: Text('Metric')), - DataColumn2(label: Text('Source'), size: ColumnSize.S), - DataColumn2(label: Text('Usage (Used/Max)'), size: ColumnSize.L), - DataColumn2(label: Text('Period'), size: ColumnSize.S), - DataColumn2(label: Text(''), size: ColumnSize.S), + columns: [ + DataColumn2(label: Text(context.l10n.metric)), + DataColumn2(label: Text(context.l10n.source), size: ColumnSize.S), + DataColumn2( + label: Text( + '${context.l10n.usage} (${context.l10n.used}/${context.l10n.max})', + ), + size: ColumnSize.L, + ), + DataColumn2(label: Text(context.l10n.period), size: ColumnSize.S), + const DataColumn2(label: Text(''), size: ColumnSize.S), ], - empty: const Text('No quotas applied for this identity.'), + empty: Text(context.l10n.identityQuotaTable_noQuotaApplied), rows: groupedQuotas.entries.expand((entry) { final metricName = entry.key; final quotas = entry.value; - final hasIndividualQuota = quotas.any((quota) => quota.source == 'Individual'); + final hasIndividualQuota = quotas.any((quota) => quota.source == context.l10n.identityQuotaTable_individual); return [ DataRow2( @@ -71,7 +76,7 @@ class IdentityQuotaListState extends State { (quota) { final isTierQuota = quota.source == 'Tier'; final shouldDisable = isTierQuota && hasIndividualQuota; - final tooltipMessage = shouldDisable ? 'Tier quotas do not take effect if there is an individual quota.' : null; + final tooltipMessage = shouldDisable ? context.l10n.identityQuotaTable_tierQuotaEffectMessage : null; return DataRow2( selected: _selectedQuotas.contains(quota.id), diff --git a/AdminUi/apps/admin_ui/lib/home/identity_details/modals/change_tier.dart b/AdminUi/apps/admin_ui/lib/home/identity_details/modals/change_tier.dart index 12b3ea9981..35a4afdfec 100644 --- a/AdminUi/apps/admin_ui/lib/home/identity_details/modals/change_tier.dart +++ b/AdminUi/apps/admin_ui/lib/home/identity_details/modals/change_tier.dart @@ -52,7 +52,7 @@ class _ShowChangeTierDialogState extends State<_ShowChangeTierDialog> { return PopScope( canPop: !_saving, child: AlertDialog( - title: const Text('Change Tier', textAlign: TextAlign.center), + title: Text(context.l10n.changeTier, textAlign: TextAlign.center), content: DropdownButtonFormField( value: selectedTier, decoration: const InputDecoration(border: OutlineInputBorder()), @@ -87,9 +87,9 @@ class _ShowChangeTierDialogState extends State<_ShowChangeTierDialog> { if (response.hasError) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Failed to update identity. Please try again.'), - duration: Duration(seconds: 3), + SnackBar( + content: Text(context.l10n.changeTierDialog_error), + duration: const Duration(seconds: 3), ), ); @@ -99,9 +99,9 @@ class _ShowChangeTierDialogState extends State<_ShowChangeTierDialog> { } ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Identity updated successfully.'), - duration: Duration(seconds: 3), + SnackBar( + content: Text(context.l10n.changeTierDialog_success), + duration: const Duration(seconds: 3), ), ); diff --git a/AdminUi/apps/admin_ui/lib/home/tier_detail/tier_detail.dart b/AdminUi/apps/admin_ui/lib/home/tier_detail/tier_detail.dart index 33b47ab0a6..5a4a838b95 100644 --- a/AdminUi/apps/admin_ui/lib/home/tier_detail/tier_detail.dart +++ b/AdminUi/apps/admin_ui/lib/home/tier_detail/tier_detail.dart @@ -63,7 +63,10 @@ class _TierDetailState extends State { Text.rich( TextSpan( children: [ - TextSpan(text: 'ID: ', style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.bold)), + TextSpan( + text: '${context.l10n.tierDetails_tierID}: ', + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.bold), + ), TextSpan(text: tierDetails.id, style: Theme.of(context).textTheme.bodyLarge), ], ), @@ -71,7 +74,10 @@ class _TierDetailState extends State { Text.rich( TextSpan( children: [ - TextSpan(text: 'Name: ', style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.bold)), + TextSpan( + text: '${context.l10n.name}: ', + style: Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.bold), + ), TextSpan(text: tierDetails.name, style: Theme.of(context).textTheme.bodyLarge), ], ), @@ -119,10 +125,10 @@ class _QuotaListState extends State<_QuotaList> { return Theme( data: Theme.of(context).copyWith(dividerColor: Colors.transparent), child: ExpansionTile( - title: const Text('Quotas'), + title: Text(context.l10n.quotas), subtitle: isQueuedForDeletionTier - ? const Text('View quotas for this tier. This tier is managed by the system and therefore read-only.') - : const Text('View and assign quotas for this tier.'), + ? Text(context.l10n.tierDetails_quotaList_titleDescription_readOnly) + : Text(context.l10n.tierDetails_quotaList_titleDescription), children: [ Card( child: Column( @@ -137,12 +143,12 @@ class _QuotaListState extends State<_QuotaList> { width: double.infinity, height: 500, child: DataTable2( - columns: const [ - DataColumn(label: Text('Metric')), - DataColumn(label: Text('Max')), - DataColumn(label: Text('Period')), + columns: [ + DataColumn(label: Text(context.l10n.metric)), + DataColumn(label: Text(context.l10n.max)), + DataColumn(label: Text(context.l10n.period)), ], - empty: const Text('No quotas added to this tier.'), + empty: Text(context.l10n.tierDetails_quotaList_noQuotaForTier), rows: widget.tierDetails.quotas .map( (quota) => DataRow2( @@ -215,8 +221,8 @@ class _IdentitiesListState extends State<_IdentitiesList> { return Theme( data: Theme.of(context).copyWith(dividerColor: Colors.transparent), child: ExpansionTile( - title: const Text('Identities'), - subtitle: const Text('View Identities associated with this Tier.'), + title: Text(context.l10n.tierDetails_identityList_title), + subtitle: Text(context.l10n.tierDetails_identityList_titleDescription), children: [ Card( child: Column( diff --git a/AdminUi/apps/admin_ui/lib/home/tiers_overview/modals/show_create_tier_dialog.dart b/AdminUi/apps/admin_ui/lib/home/tiers_overview/modals/show_create_tier_dialog.dart index 73f92de7de..c4faed35b2 100644 --- a/AdminUi/apps/admin_ui/lib/home/tiers_overview/modals/show_create_tier_dialog.dart +++ b/AdminUi/apps/admin_ui/lib/home/tiers_overview/modals/show_create_tier_dialog.dart @@ -44,7 +44,7 @@ class _CreateTierDialogState extends State<_CreateTierDialog> { return PopScope( canPop: !_isLoading, child: AlertDialog( - title: const Text('Create Tier'), + title: Text(context.l10n.createTierDialog_title), content: _isLoading ? const Padding( padding: EdgeInsets.all(16), @@ -53,7 +53,7 @@ class _CreateTierDialogState extends State<_CreateTierDialog> { : Column( mainAxisSize: MainAxisSize.min, children: [ - const Text('Please fill the form below to create your Tier'), + Text(context.l10n.createTierDialog_formMessage), Gaps.h16, TextField( controller: _tierNameController, @@ -65,7 +65,7 @@ class _CreateTierDialogState extends State<_CreateTierDialog> { onSubmitted: _onSubmitted, decoration: InputDecoration( border: const OutlineInputBorder(), - labelText: 'Name', + labelText: context.l10n.name, error: _errorMessage != null ? Text( _errorMessage!, @@ -84,7 +84,7 @@ class _CreateTierDialogState extends State<_CreateTierDialog> { ), FilledButton( onPressed: _isLoading ? null : () => _onSubmitted(_tierNameController.text), - child: const Text('Create'), + child: Text(context.l10n.create), ), ], ), @@ -93,7 +93,7 @@ class _CreateTierDialogState extends State<_CreateTierDialog> { Future _onSubmitted(String name) async { if (name.isEmpty) { - _setErrorMessage('Name cannot be empty.'); + _setErrorMessage(context.l10n.createTierDialog_nameCannotBeEmpty); _focusNode.requestFocus(); return; } @@ -118,13 +118,13 @@ class _CreateTierDialogState extends State<_CreateTierDialog> { }); void _showSuccessSnackbar() { - const snackBar = SnackBar( + final snackBar = SnackBar( content: Text( - 'Tier was created successfully.', - style: TextStyle(color: Colors.white), + context.l10n.createTierDialog_tierCreatedSuccess, + style: const TextStyle(color: Colors.white), ), backgroundColor: Colors.green, - duration: Duration(seconds: 3), + duration: const Duration(seconds: 3), showCloseIcon: true, ); ScaffoldMessenger.of(context).showSnackBar(snackBar); diff --git a/AdminUi/apps/admin_ui/lib/home/tiers_overview/tiers_overview.dart b/AdminUi/apps/admin_ui/lib/home/tiers_overview/tiers_overview.dart index dc9311c1fd..b320510846 100644 --- a/AdminUi/apps/admin_ui/lib/home/tiers_overview/tiers_overview.dart +++ b/AdminUi/apps/admin_ui/lib/home/tiers_overview/tiers_overview.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; +import '/core/core.dart'; import 'modals/show_create_tier_dialog.dart'; class TiersOverview extends StatefulWidget { @@ -50,11 +51,11 @@ class _TiersOverviewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Tiers', + context.l10n.tiers, style: TextStyle(color: Theme.of(context).colorScheme.onPrimary, fontSize: 30), ), Text( - 'A list of all Tiers', + context.l10n.tiersOverview_title_description, style: TextStyle(color: Theme.of(context).colorScheme.onPrimary, fontSize: 13), ), ], @@ -87,10 +88,10 @@ class _TiersOverviewState extends State { child: DataTable2( isVerticalScrollBarVisible: true, showCheckboxColumn: false, - empty: const Text('No tiers found.'), - columns: const [ - DataColumn2(label: Text('Name'), size: ColumnSize.L), - DataColumn2(label: Text('Number of Identities'), size: ColumnSize.L), + empty: Text(context.l10n.tiersOverview_noTiersFound), + columns: [ + DataColumn2(label: Text(context.l10n.name), size: ColumnSize.L), + DataColumn2(label: Text(context.l10n.numberOfIdentities), size: ColumnSize.L), ], rows: _tiers! .map( diff --git a/AdminUi/apps/admin_ui/lib/l10n/app_en.arb b/AdminUi/apps/admin_ui/lib/l10n/app_en.arb index d170ca5ee4..6965ffa8a5 100644 --- a/AdminUi/apps/admin_ui/lib/l10n/app_en.arb +++ b/AdminUi/apps/admin_ui/lib/l10n/app_en.arb @@ -1,7 +1,101 @@ { - "@@locale": "en", - "cancel": "Cancel", - "close": "Close", - "save": "Save", - "yes": "Yes" + "@@locale": "en", + "add": "Add", + "addQuota": "Add Quota", + "address": "Address", + "cancel": "Cancel", + "create": "Create", + "changeClientSecret": "Change Client Secret", + "changeTier": "Change Tier", + "clientID": "Client ID", + "clientSecret": "Client Secret", + "clientSecret_copiedToClipboard": "Client secret copied to clipboard.", + "clientSecret_message": "A client secret will be generated if this field is left blank.", + "clientSecret_save_message": "Please save the Client Secret since it will be inaccessible after exiting.", + "clients": "Clients", + "close": "Close", + "confirm": "Confirm", + "createdAt": "Created At", + "dark": "Dark", + "day": "Day", + "datawalletVersion": "Datawallet Version", + "defaultTier": "Default Tier", + "devices": "Devices", + "displayName": "Display Name", + "hour": "Hour", + "identityVersion": "Identity Version", + "lastLoginAt": "Last Login at", + "light": "Light", + "max": "Max", + "maxAmount": "Max Amount", + "maxIdentities": "Max Identities", + "metric": "Metric", + "month": "Month", + "name": "Name", + "numberOfDevices": "Number of Devices", + "numberOfIdentities": "Number of Identities", + "period": "Period", + "quotas": "Quotas", + "retry": "Retry", + "save": "Save", + "searchTiers": "Search Tiers", + "settings": "Settings", + "source": "Source", + "system": "System", + "theme": "Theme", + "tier": "Tier", + "tiers": "Tiers", + "usage": "Usage", + "used": "Used", + "week": "Week", + "year": "Year", + "yes": "Yes", + "addQuotaDialog_maxAmount_message": "Only numbers greater or equal to 0 are valid.", + "changeClientSecretDialog_copyToClipboard": "Copy to Clipboard", + "changeTierDialog_error": "Failed to update identity. Please try again.", + "changeTierDialog_success": "Identity updated successfully.", + "clientsOverview_title": "A list of existing clients", + "clientsOverview_noClientsFound": "No clients found.", + "clientsOverview_removeSelectedClients_title": "Remove Clients", + "clientsOverview_removeSelectedClients_message": "Are you sure you want to remove the selected clients?", + "clientsOverview_removeSelectedClients_error": "An error occurred while deleting the client(s). Please try again.", + "clientsOverview_removeSelectedClients_success": "Selected client(s) have been removed.", + "createClientDialog_title": "Create Client", + "createClientDialog_clientID_message": "A client ID will be generated if this field is left blank.", + "createClientDialog_displayName_message": "Client ID will be used as a Display Name if no value is provided.", + "createClientDialog_clientSecret_copyToClipboard": "Client Secret copied to clipboard.", + "createClientDialog_maxIdentities_message": "The maximum number of Identities that can be created with this Client.", + "createClientDialog_maxIdentities_noLimit_message": "No Identity limit will be assigned if this field is left blank.", + "createTierDialog_title": "Create Tier", + "createTierDialog_formMessage": "Please fill the form below to create your Tier", + "createTierDialog_nameCannotBeEmpty": "Name cannot be empty.", + "createTierDialog_tierCreatedSuccess": "Tier was created successfully.", + "identityDetails_card_identityClipboardMessage": "Identity address copied to clipboard.", + "identityDetails_card_publicKey": "Public Key", + "identityDetails_card_publicKey_copyToClipboardMessage": "Public key copied to clipboard.", + "identityDetails_card_publicKey_tooltipMessage": "Copy public key", + "identityOverview_title": "A list of existing identities", + "identityQuotaTable_title_description": "View and assign quotas for this identity.", + "identityQuotaTable_noQuotaApplied": "No quotas applied for this identity.", + "identityQuotaTable_individual": "Individual", + "identityQuotaTable_tierQuotaEffectMessage": "Tier quotas do not take effect if there is an individual quota.", + "identitiesDataTable_noIdentitiesFound": "No identities found.", + "identitiesDataTable_failedToLoadData": "Failed to load data.", + "identitiesDataTable_createdWithClient": "Created with Client", + "identitiesFilter_searchClients": "Search Clients", + "quotaButtonGroup_removeQuotas_title": "Remove Quotas", + "quotaButtonGroup_deletionMessage": "Are you sure you want to remove the selected quotas from?", + "quotaButtonGroup_theIdentity": "The Identity", + "quotaButtonGroup_theTier": "The Tier", + "quotaButtonGroup_errorDeletingQuota": "An error occurred while deleting the quota(s). Please try again.", + "quotaButtonGroup_selectedQuotaRemoved": "Selected quota(s) have been removed.", + "tierDetails_tierID": "ID", + "tierDetails_quotaList_titleDescription": "View and assign quotas for this tier.", + "tierDetails_quotaList_titleDescription_readOnly": "View quotas for this tier. This tier is managed by the system and therefore read-only.", + "tierDetails_quotaList_noQuotaForTier": "No quotas added to this tier.", + "tierDetails_identityList_title": "Identities", + "tierDetails_identityList_titleDescription": "View Identities associated with this Tier.", + "tierDetails_quotaList_titleDescription": "View and assign quotas for this tier.", + "tiersOverview_title_description": "A list of existing tiers", + "tiersOverview_noTiersFound": "No tiers found." }