Change prefixes and add check_strings.py

This commit is contained in:
Dain Nilsson 2023-03-02 12:45:55 +01:00
parent fd1046eb72
commit b33dca3900
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
46 changed files with 456 additions and 340 deletions

110
check_strings.py Executable file
View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
# Copyright (C) 2023 Yubico.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import json
errors = []
def check_duplicate_keys(pairs):
seen = set()
for d in [k for k, v in pairs if k in seen or seen.add(k)]:
errors.append(f"Duplicate key: {d}")
return dict(pairs)
def check_duplicate_values(strings):
seen = {}
for k, v in strings.items():
if isinstance(v, str):
if v in seen:
errors.append(
f"Duplicate value in key: {k} (originally in {seen[v]}): {v}"
)
else:
seen[v] = k
def check_prefixes(k, v, s_max_words, s_max_len):
errs = []
if k.startswith("s_"):
if len(v) > s_max_len:
errs.append(f"Too long ({len(v)} chars)")
if len(v.split()) > s_max_words:
errs.append(f"Too many words ({len(v.split())})")
elif k.startswith("l_"):
if v.endswith("."):
errs.append("Ends with '.'")
if ". " in v:
errs.append("Spans multiple sentences")
elif k.startswith("p_"):
if v[-1] not in ".!":
errs.append("Doesn't end in punctuation")
elif k.startswith("q_"):
if not v.endswith("?"):
errs.append("Doesn't end in '?'")
return errs
def check_misc(k, v):
errs = []
if "..." in v:
errs.append("'...' should be replaced with '\\u2026'")
return errs
def lint_strings(strings, rules):
for k, v in strings.items():
errs = []
errs.extend(
check_prefixes(
k,
v,
rules.get("s_max_words", 4),
rules.get("s_max_len", 32),
)
)
errs.extend(check_misc(k, v))
if errs:
errors.append(f'Errors in {k}: "{v}"')
errors.extend([f" {e}" for e in errs])
if len(sys.argv) != 2:
print("USAGE: check_strings.py <ARB_FILE>")
sys.exit(1)
target = sys.argv[1]
with open(target) as f:
values = json.load(f, object_pairs_hook=check_duplicate_keys)
strings = {k: v for k, v in values.items() if not k.startswith("@")}
check_duplicate_values(strings)
lint_strings(strings, strings.get("@_lint_rules", {}))
print(len(strings), "strings in file")
if errors:
for e in errors:
print(e)
sys.exit(1)
print("OK")

View File

@ -42,7 +42,7 @@ class AboutPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!;
return ResponsiveDialog(
title: Text(l10n.w_about),
title: Text(l10n.s_about),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 32),
child: Column(
@ -63,7 +63,7 @@ class AboutPage extends ConsumerWidget {
children: [
TextButton(
child: Text(
l10n.l_terms_of_use,
l10n.s_terms_of_use,
style:
const TextStyle(decoration: TextDecoration.underline),
),
@ -73,7 +73,7 @@ class AboutPage extends ConsumerWidget {
),
TextButton(
child: Text(
l10n.l_privacy_policy,
l10n.s_privacy_policy,
style:
const TextStyle(decoration: TextDecoration.underline),
),
@ -85,7 +85,7 @@ class AboutPage extends ConsumerWidget {
),
TextButton(
child: Text(
l10n.l_open_src_licenses,
l10n.s_open_src_licenses,
style: const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
@ -104,7 +104,7 @@ class AboutPage extends ConsumerWidget {
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
l10n.l_help_and_feedback,
l10n.s_help_and_feedback,
style: Theme.of(context).textTheme.titleMedium,
),
),
@ -113,7 +113,7 @@ class AboutPage extends ConsumerWidget {
children: [
TextButton(
child: Text(
l10n.l_send_feedback,
l10n.s_send_feedback,
style:
const TextStyle(decoration: TextDecoration.underline),
),
@ -123,7 +123,7 @@ class AboutPage extends ConsumerWidget {
),
TextButton(
child: Text(
l10n.l_i_need_help,
l10n.s_i_need_help,
style:
const TextStyle(decoration: TextDecoration.underline),
),
@ -140,7 +140,7 @@ class AboutPage extends ConsumerWidget {
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
l10n.w_troubleshooting,
l10n.s_troubleshooting,
style: Theme.of(context).textTheme.titleMedium,
),
),
@ -151,7 +151,7 @@ class AboutPage extends ConsumerWidget {
const SizedBox(height: 12.0),
ActionChip(
avatar: const Icon(Icons.bug_report_outlined),
label: Text(l10n.l_run_diagnostics),
label: Text(l10n.s_run_diagnostics),
onPressed: () async {
_log.info('Running diagnostics...');
final response = await ref
@ -180,7 +180,7 @@ class AboutPage extends ConsumerWidget {
if (isAndroid) ...[
const SizedBox(height: 12.0),
FilterChip(
label: Text(l10n.l_allow_screenshots),
label: Text(l10n.s_allow_screenshots),
selected: ref.watch(androidAllowScreenshotsProvider),
onSelected: (value) async {
ref
@ -216,7 +216,7 @@ class LoggingPanel extends ConsumerWidget {
value: logLevel,
items: Levels.LEVELS,
selected: logLevel != Level.INFO,
labelBuilder: (value) => Text(l10n.l_log_level(
labelBuilder: (value) => Text(l10n.s_log_level(
value.name[0] + value.name.substring(1).toLowerCase())),
itemBuilder: (value) =>
Text('${value.name[0]}${value.name.substring(1).toLowerCase()}'),
@ -227,7 +227,7 @@ class LoggingPanel extends ConsumerWidget {
),
ActionChip(
avatar: const Icon(Icons.copy),
label: Text(l10n.l_copy_log),
label: Text(l10n.s_copy_log),
onPressed: () async {
_log.info('Copying log to clipboard ($version)...');
final logs = await ref.read(logLevelProvider.notifier).getLogs();

View File

@ -195,7 +195,7 @@ class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
return promptUserInteraction(
context,
icon: const Icon(Icons.touch_app),
title: l10n.l_touch_required,
title: l10n.s_touch_required,
description: l10n.l_touch_button_now,
);
},

View File

@ -75,7 +75,7 @@ class QRScannerPermissionsUI extends StatelessWidget {
Navigator.of(context).pop('');
},
child: Text(
l10n.l_enter_manually,
l10n.s_enter_manually,
style: const TextStyle(color: Colors.white),
)),
],
@ -92,7 +92,7 @@ class QRScannerPermissionsUI extends StatelessWidget {
onPermissionRequest();
},
child: Text(
l10n.l_review_permissions,
l10n.s_review_permissions,
style: const TextStyle(color: Colors.white),
)),
],

View File

@ -73,7 +73,7 @@ class QRScannerUI extends StatelessWidget {
},
key: keys.manualEntryButton,
child: Text(
l10n.l_enter_manually,
l10n.s_enter_manually,
style: const TextStyle(color: Colors.white),
)),
],

View File

@ -114,7 +114,7 @@ class _QrScannerViewState extends State<QrScannerView> {
extendBody: true,
appBar: AppBar(
title: Text(
l10n.l_add_account,
l10n.s_add_account,
style: const TextStyle(color: Colors.white),
),
backgroundColor: Colors.transparent,

View File

@ -80,11 +80,11 @@ extension on ThemeMode {
String getDisplayName(AppLocalizations l10n) {
switch (this) {
case ThemeMode.system:
return l10n.l_system_default;
return l10n.s_system_default;
case ThemeMode.light:
return l10n.l_light_mode;
return l10n.s_light_mode;
case ThemeMode.dark:
return l10n.l_dark_mode;
return l10n.s_dark_mode;
}
}
}
@ -114,7 +114,7 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
final theme = Theme.of(context);
return ResponsiveDialog(
title: Text(l10n.w_settings),
title: Text(l10n.s_settings),
child: Theme(
// Make the headers use the primary color to pop a bit.
// Once M3 is implemented this will probably not be needed.
@ -127,7 +127,7 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTitle(l10n.l_nfc_options),
ListTitle(l10n.s_nfc_options),
ListTile(
title: Text(l10n.l_on_yk_nfc_tap),
subtitle: Text(tapAction.getDescription(l10n)),
@ -166,7 +166,7 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
});
}),
SwitchListTile(
title: Text(l10n.l_silence_nfc_sounds),
title: Text(l10n.s_silence_nfc_sounds),
subtitle: Text(nfcSilenceSounds
? l10n.l_silence_nfc_sounds_on
: l10n.l_silence_nfc_sounds_off),
@ -177,7 +177,7 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
prefs.setBool(prefNfcSilenceSounds, value);
});
}),
ListTitle(l10n.l_usb_options),
ListTitle(l10n.s_usb_options),
SwitchListTile(
title: Text(l10n.l_launch_app_on_usb),
subtitle: Text(usbOpenApp
@ -190,9 +190,9 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
prefs.setBool(prefUsbOpenApp, value);
});
}),
ListTitle(l10n.w_appearance),
ListTitle(l10n.s_appearance),
ListTile(
title: Text(l10n.l_app_theme),
title: Text(l10n.s_app_theme),
subtitle: Text(themeMode.getDisplayName(l10n)),
key: keys.themeModeSetting,
onTap: () async {
@ -239,7 +239,7 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
builder: (BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return SimpleDialog(
title: Text(l10n.l_choose_kbd_layout),
title: Text(l10n.s_choose_kbd_layout),
children: _keyboardLayouts
.map(
(e) => RadioListTile<String>(
@ -264,7 +264,7 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
builder: (BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return SimpleDialog(
title: Text(l10n.l_choose_app_theme),
title: Text(l10n.s_choose_app_theme),
children: supportedThemes
.map((e) => RadioListTile(
title: Text(e.getDisplayName(l10n)),

View File

@ -62,9 +62,9 @@ enum Application {
String getDisplayName(AppLocalizations l10n) {
switch (this) {
case Application.oath:
return l10n.w_authenticator;
return l10n.s_authenticator;
case Application.fido:
return l10n.w_webauthn;
return l10n.s_webauthn;
default:
return name.substring(0, 1).toUpperCase() + name.substring(1);
}

View File

@ -48,7 +48,7 @@ class AppFailurePage extends ConsumerWidget {
case 'ccid':
header = l10n.l_ccid_connection_failed;
if (Platform.isMacOS) {
message = l10n.l_try_reinsert_yk;
message = l10n.p_try_reinsert_yk;
} else if (Platform.isLinux) {
message = l10n.p_pcscd_unavailable;
} else {
@ -63,7 +63,7 @@ class AppFailurePage extends ConsumerWidget {
message = l10n.p_webauthn_elevated_permissions_required;
actions = [
ElevatedButton.icon(
label: Text(l10n.w_unlock),
label: Text(l10n.s_unlock),
icon: const Icon(Icons.lock_open),
onPressed: () async {
final closeMessage = showMessage(
@ -77,7 +77,7 @@ class AppFailurePage extends ConsumerWidget {
(context) async {
showMessage(
context,
l10n.l_permission_denied,
l10n.s_permission_denied,
);
},
);
@ -92,7 +92,7 @@ class AppFailurePage extends ConsumerWidget {
break;
default:
header = l10n.l_open_connection_failed;
message = l10n.l_try_reinsert_yk;
message = l10n.p_try_reinsert_yk;
}
}
}

View File

@ -129,7 +129,7 @@ class AppPage extends StatelessWidget {
},
icon: const Icon(Icons.tune),
iconSize: 24,
tooltip: AppLocalizations.of(context)!.l_configure_yk,
tooltip: AppLocalizations.of(context)!.s_configure_yk,
padding: const EdgeInsets.all(12),
),
),

View File

@ -45,7 +45,7 @@ class DeviceButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return IconButton(
tooltip: AppLocalizations.of(context)!.l_select_yk,
tooltip: AppLocalizations.of(context)!.s_select_yk,
icon: _CircledDeviceAvatar(radius),
onPressed: () async {
await showBlurDialog(

View File

@ -44,7 +44,7 @@ class DeviceErrorScreen extends ConsumerWidget {
message: l10n.p_elevated_permissions_required,
actions: [
ElevatedButton.icon(
label: Text(l10n.w_unlock),
label: Text(l10n.s_unlock),
icon: const Icon(Icons.lock_open),
onPressed: () async {
final closeMessage = showMessage(
@ -55,7 +55,7 @@ class DeviceErrorScreen extends ConsumerWidget {
ref.invalidate(rpcProvider);
} else {
await ref.read(withContextProvider)((context) async =>
showMessage(context, l10n.l_permission_denied));
showMessage(context, l10n.s_permission_denied));
}
} finally {
closeMessage();
@ -81,7 +81,7 @@ class DeviceErrorScreen extends ConsumerWidget {
final String message;
switch (error) {
case 'unknown-device':
message = l10n.l_unknown_device;
message = l10n.s_unknown_device;
break;
default:
message = l10n.l_place_on_nfc_reader;

View File

@ -122,7 +122,7 @@ class _DevicePickerContent extends ConsumerWidget {
title: Center(child: Text(l10n.l_no_yk_present)),
subtitle: Center(
child: Text(
Platform.isAndroid ? l10n.l_insert_or_tap_yk : l10n.w_usb)),
Platform.isAndroid ? l10n.l_insert_or_tap_yk : l10n.s_usb)),
),
],
);
@ -136,7 +136,7 @@ class _DevicePickerContent extends ConsumerWidget {
padding: EdgeInsets.symmetric(horizontal: 4),
child: DeviceAvatar(child: Icon(Icons.usb)),
),
title: Text(l10n.w_usb),
title: Text(l10n.s_usb),
subtitle: Text(l10n.l_no_yk_present),
onTap: () {
ref.read(currentDeviceProvider.notifier).setCurrentDevice(null);
@ -168,7 +168,7 @@ class _DevicePickerContent extends ConsumerWidget {
ref.read(_hiddenDevicesProvider.notifier).showAll();
},
child: ListTile(
title: Text(l10n.l_show_hidden_devices),
title: Text(l10n.s_show_hidden_devices),
dense: true,
contentPadding: EdgeInsets.zero,
),
@ -195,11 +195,11 @@ String _getDeviceInfoString(BuildContext context, DeviceInfo info) {
final l10n = AppLocalizations.of(context)!;
final serial = info.serial;
return [
if (serial != null) l10n.l_sn_serial(serial),
if (serial != null) l10n.s_sn_serial(serial),
if (info.version.isAtLeast(1))
l10n.l_fw_version(info.version)
l10n.s_fw_version(info.version)
else
l10n.l_unknown_type,
l10n.s_unknown_type,
].join(' ');
}
@ -211,9 +211,9 @@ List<String> _getDeviceStrings(
error: (error, _) {
switch (error) {
case 'device-inaccessible':
return [node.name, l10n.l_yk_inaccessible];
return [node.name, l10n.s_yk_inaccessible];
case 'unknown-device':
return [l10n.l_unknown_device];
return [l10n.s_unknown_device];
}
return null;
},
@ -306,9 +306,9 @@ class _DeviceRow extends ConsumerWidget {
subtitle: Text(
node.when(
usbYubiKey: (_, __, ___, info) => info == null
? l10n.l_yk_inaccessible
? l10n.s_yk_inaccessible
: _getDeviceInfoString(context, info),
nfcReader: (_, __) => l10n.l_select_to_scan,
nfcReader: (_, __) => l10n.s_select_to_scan,
),
),
onTap: () {
@ -344,7 +344,7 @@ class _NfcDeviceRow extends ConsumerWidget {
ref.read(_hiddenDevicesProvider.notifier).showAll();
},
child: ListTile(
title: Text(l10n.l_show_hidden_devices),
title: Text(l10n.s_show_hidden_devices),
dense: true,
contentPadding: EdgeInsets.zero,
enabled: hidden.isNotEmpty,
@ -355,7 +355,7 @@ class _NfcDeviceRow extends ConsumerWidget {
ref.read(_hiddenDevicesProvider.notifier).hideDevice(node.path);
},
child: ListTile(
title: Text(l10n.l_hide_device),
title: Text(l10n.s_hide_device),
dense: true,
contentPadding: EdgeInsets.zero,
),

View File

@ -142,7 +142,7 @@ class MainPageDrawer extends ConsumerWidget {
NavigationDrawerDestination(
key: managementAppDrawer,
label: Text(
l10n.l_toggle_applications,
l10n.s_toggle_applications,
),
icon: Icon(Application.management._icon),
selectedIcon: Icon(Application.management._filledIcon),
@ -152,11 +152,11 @@ class MainPageDrawer extends ConsumerWidget {
],
// Non-YubiKey pages
NavigationDrawerDestination(
label: Text(l10n.w_settings),
label: Text(l10n.s_settings),
icon: const Icon(Icons.settings_outlined),
),
NavigationDrawerDestination(
label: Text(l10n.l_help_and_about),
label: Text(l10n.s_help_and_about),
icon: const Icon(Icons.help_outline),
),
],

View File

@ -88,7 +88,7 @@ class MainPage extends ConsumerWidget {
actions: [
if (hasNfcSupport && !isNfcEnabled)
ElevatedButton.icon(
label: Text(l10n.l_enable_nfc),
label: Text(l10n.s_enable_nfc),
icon: nfcIcon,
onPressed: () async {
await openNfcSettings();
@ -96,7 +96,7 @@ class MainPage extends ConsumerWidget {
],
actionButtonBuilder: (context) => IconButton(
icon: const Icon(Icons.person_add_alt_1),
tooltip: l10n.l_add_account,
tooltip: l10n.s_add_account,
onPressed: () async {
CredentialData? otpauth;
final scanner = ref.read(qrScannerProvider);
@ -142,17 +142,17 @@ class MainPage extends ConsumerWidget {
if (data.info.supportedCapabilities.isEmpty &&
data.name == 'Unrecognized device') {
return MessagePage(
header: l10n.l_yk_not_recognized,
header: l10n.s_yk_not_recognized,
);
} else if (app.getAvailability(data) ==
Availability.unsupported) {
return MessagePage(
header: l10n.l_app_not_supported,
header: l10n.s_app_not_supported,
message: l10n.l_app_not_supported_on_yk(app.name),
);
} else if (app.getAvailability(data) != Availability.enabled) {
return MessagePage(
header: l10n.l_app_disabled,
header: l10n.s_app_disabled,
message: l10n.l_app_disabled_desc(app.name),
);
}
@ -164,7 +164,7 @@ class MainPage extends ConsumerWidget {
return FidoScreen(data);
default:
return MessagePage(
header: l10n.l_app_not_supported,
header: l10n.s_app_not_supported,
message: l10n.l_app_not_supported_desc,
);
}

View File

@ -285,7 +285,7 @@ class _HelperWaiterState extends ConsumerState<_HelperWaiter> {
actions: [
ActionChip(
avatar: const Icon(Icons.copy),
label: Text(l10n.l_copy_log),
label: Text(l10n.s_copy_log),
onPressed: () async {
_log.info('Copying log to clipboard ($version)...');
final logs = await ref.read(logLevelProvider.notifier).getLogs();

View File

@ -271,7 +271,7 @@ class DesktopCredentialListNotifier extends OathCredentialListNotifier {
return promptUserInteraction(
context,
icon: const Icon(Icons.touch_app),
title: l10n.l_touch_required,
title: l10n.s_touch_required,
description: l10n.l_touch_button_now,
headless: headless,
);

View File

@ -176,7 +176,7 @@ class _Systray extends TrayListener {
.read(clipboardProvider)
.setText(code.value, isSensitive: true);
final notification = LocalNotification(
title: _l10n.l_code_copied,
title: _l10n.s_code_copied,
body: _l10n.p_target_copied_clipboard(label),
silent: true,
);
@ -190,12 +190,12 @@ class _Systray extends TrayListener {
),
if (_credentials.isEmpty)
MenuItem(
label: _l10n.l_no_pinned_accounts,
label: _l10n.s_no_pinned_accounts,
disabled: true,
),
MenuItem.separator(),
MenuItem(
label: _isHidden ? _l10n.l_show_window : _l10n.l_hide_window,
label: _isHidden ? _l10n.s_show_window : _l10n.s_hide_window,
onClick: (_) {
_ref
.read(desktopWindowStateProvider.notifier)
@ -204,7 +204,7 @@ class _Systray extends TrayListener {
),
MenuItem.separator(),
MenuItem(
label: _l10n.w_quit,
label: _l10n.s_quit,
onClick: (_) {
_ref.read(withContextProvider)(
(context) async {

View File

@ -25,7 +25,7 @@ class ErrorPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.l_application_error),
title: Text(AppLocalizations.of(context)!.s_application_error),
),
body: Center(
child: Column(

View File

@ -146,7 +146,7 @@ class _AddFingerprintDialogState extends ConsumerState<AddFingerprintDialog>
.renameFingerprint(_fingerprint!, _label);
if (!mounted) return;
Navigator.of(context).pop(true);
showMessage(context, l10n.l_fingerprint_added);
showMessage(context, l10n.s_fingerprint_added);
} catch (e) {
final String errorMessage;
// TODO: Make this cleaner than importing desktop specific RpcError.
@ -169,7 +169,7 @@ class _AddFingerprintDialogState extends ConsumerState<AddFingerprintDialog>
final progress = _samples == 0 ? 0.0 : _samples / (_samples + _remaining);
return ResponsiveDialog(
title: Text(l10n.l_add_fingerprint),
title: Text(l10n.s_add_fingerprint),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0),
child: Column(
@ -208,7 +208,7 @@ class _AddFingerprintDialogState extends ConsumerState<AddFingerprintDialog>
decoration: InputDecoration(
enabled: _fingerprint != null,
border: const OutlineInputBorder(),
labelText: l10n.w_name,
labelText: l10n.s_name,
prefixIcon: const Icon(Icons.fingerprint_outlined),
),
onChanged: (value) {
@ -234,7 +234,7 @@ class _AddFingerprintDialogState extends ConsumerState<AddFingerprintDialog>
actions: [
TextButton(
onPressed: _fingerprint != null && _label.isNotEmpty ? _submit : null,
child: Text(l10n.w_save),
child: Text(l10n.s_save),
),
],
);

View File

@ -38,7 +38,7 @@ class DeleteCredentialDialog extends ConsumerWidget {
final label = credential.userName;
return ResponsiveDialog(
title: Text(l10n.l_delete_credential),
title: Text(l10n.s_delete_credential),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0),
child: Column(
@ -63,11 +63,11 @@ class DeleteCredentialDialog extends ConsumerWidget {
await ref.read(withContextProvider)(
(context) async {
Navigator.of(context).pop(true);
showMessage(context, l10n.l_credential_deleted);
showMessage(context, l10n.s_credential_deleted);
},
);
},
child: Text(l10n.w_delete),
child: Text(l10n.s_delete),
),
],
);

View File

@ -36,7 +36,7 @@ class DeleteFingerprintDialog extends ConsumerWidget {
final label = fingerprint.label;
return ResponsiveDialog(
title: Text(l10n.l_delete_fingerprint),
title: Text(l10n.s_delete_fingerprint),
actions: [
TextButton(
onPressed: () async {
@ -45,10 +45,10 @@ class DeleteFingerprintDialog extends ConsumerWidget {
.deleteFingerprint(fingerprint);
await ref.read(withContextProvider)((context) async {
Navigator.of(context).pop(true);
showMessage(context, l10n.l_fingerprint_deleted);
showMessage(context, l10n.s_fingerprint_deleted);
});
},
child: Text(l10n.w_delete),
child: Text(l10n.s_delete),
),
],
child: Padding(

View File

@ -37,7 +37,7 @@ class FidoScreen extends ConsumerWidget {
final l10n = AppLocalizations.of(context)!;
return ref.watch(fidoStateProvider(deviceData.node.path)).when(
loading: () => AppPage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
centered: true,
delayedContent: true,
child: const CircularProgressIndicator(),
@ -48,7 +48,7 @@ class FidoScreen extends ConsumerWidget {
0;
if (Capability.fido2.value & supported == 0) {
return MessagePage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
graphic: manageAccounts,
header: l10n.l_ready_to_use,
message: l10n.l_register_sk_on_websites,
@ -59,14 +59,14 @@ class FidoScreen extends ConsumerWidget {
0;
if (Capability.fido2.value & enabled == 0) {
return MessagePage(
title: Text(l10n.w_webauthn),
header: l10n.l_fido_disabled,
title: Text(l10n.s_webauthn),
header: l10n.s_fido_disabled,
message: l10n.l_webauthn_req_fido2,
);
}
return AppFailurePage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
cause: error,
);
},

View File

@ -32,11 +32,11 @@ Widget fidoBuildActions(
return SimpleDialog(
children: [
if (state.bioEnroll != null) ...[
ListTitle(l10n.w_setup,
ListTitle(l10n.s_setup,
textStyle: Theme.of(context).textTheme.bodyLarge),
ListTile(
leading: const CircleAvatar(child: Icon(Icons.fingerprint_outlined)),
title: Text(l10n.l_add_fingerprint),
title: Text(l10n.s_add_fingerprint),
subtitle: state.unlocked
? Text(l10n.l_fingerprints_used(fingerprints))
: Text(state.hasPin
@ -54,13 +54,13 @@ Widget fidoBuildActions(
: null,
),
],
ListTitle(l10n.w_manage,
ListTitle(l10n.s_manage,
textStyle: Theme.of(context).textTheme.bodyLarge),
ListTile(
leading: const CircleAvatar(child: Icon(Icons.pin_outlined)),
title: Text(state.hasPin ? l10n.l_change_pin : l10n.l_set_pin),
title: Text(state.hasPin ? l10n.s_change_pin : l10n.s_set_pin),
subtitle: Text(state.hasPin
? l10n.l_fido_pin_protection
? l10n.s_fido_pin_protection
: l10n.l_fido_pin_protection_optional),
onTap: () {
Navigator.of(context).pop();
@ -75,7 +75,7 @@ Widget fidoBuildActions(
backgroundColor: theme.error,
child: const Icon(Icons.delete_outline),
),
title: Text(l10n.l_reset_fido),
title: Text(l10n.s_reset_fido),
subtitle: Text(l10n.l_factory_reset_this_app),
onTap: () {
Navigator.of(context).pop();

View File

@ -38,15 +38,15 @@ class FidoLockedPage extends ConsumerWidget {
if (!state.hasPin) {
if (state.bioEnroll != null) {
return MessagePage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
graphic: noFingerprints,
header: l10n.l_no_fingerprints,
header: l10n.s_no_fingerprints,
message: l10n.l_set_pin_fingerprints,
keyActionsBuilder: _buildActions,
);
} else {
return MessagePage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
graphic: manageAccounts,
header: state.credMgmt
? l10n.l_no_discoverable_accounts
@ -59,7 +59,7 @@ class FidoLockedPage extends ConsumerWidget {
if (!state.credMgmt && state.bioEnroll == null) {
return MessagePage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
graphic: manageAccounts,
header: l10n.l_ready_to_use,
message: l10n.l_register_sk_on_websites,
@ -68,7 +68,7 @@ class FidoLockedPage extends ConsumerWidget {
}
return AppPage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
keyActionsBuilder: _buildActions,
child: Column(
children: [
@ -148,7 +148,7 @@ class _PinEntryFormState extends ConsumerState<_PinEntryForm> {
controller: _pinController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.w_pin,
labelText: l10n.s_pin,
helperText: '', // Prevents dialog resizing
errorText: _pinIsWrong ? _getErrorText() : null,
errorMaxLines: 3,
@ -187,7 +187,7 @@ class _PinEntryFormState extends ConsumerState<_PinEntryForm> {
minLeadingWidth: 0,
trailing: ElevatedButton.icon(
icon: const Icon(Icons.lock_open),
label: Text(l10n.w_unlock),
label: Text(l10n.s_unlock),
onPressed:
_pinController.text.isNotEmpty && !_blocked ? _submit : null,
),

View File

@ -57,11 +57,11 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
final minPinLength = widget.state.minPinLength;
return ResponsiveDialog(
title: Text(hasPin ? l10n.l_change_pin : l10n.l_set_pin),
title: Text(hasPin ? l10n.s_change_pin : l10n.s_set_pin),
actions: [
TextButton(
onPressed: isValid ? _submit : null,
child: Text(l10n.w_save),
child: Text(l10n.s_save),
),
],
child: Padding(
@ -77,7 +77,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
obscureText: true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_current_pin,
labelText: l10n.s_current_pin,
errorText: _currentIsWrong ? _currentPinError : null,
errorMaxLines: 3,
prefixIcon: const Icon(Icons.pin_outlined),
@ -98,7 +98,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
obscureText: true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_new_pin,
labelText: l10n.s_new_pin,
enabled: !hasPin || _currentPin.isNotEmpty,
errorText: _newIsWrong ? _newPinError : null,
errorMaxLines: 3,
@ -116,7 +116,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
obscureText: true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_confirm_pin,
labelText: l10n.s_confirm_pin,
prefixIcon: const Icon(Icons.pin_outlined),
enabled:
(!hasPin || _currentPin.isNotEmpty) && _newPin.isNotEmpty,
@ -160,7 +160,7 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
.setPin(_newPin, oldPin: oldPin);
result.when(success: () {
Navigator.of(context).pop(true);
showMessage(context, l10n.l_pin_set);
showMessage(context, l10n.s_pin_set);
}, failed: (retries, authBlocked) {
setState(() {
if (authBlocked) {

View File

@ -54,7 +54,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameFingerprintDialog> {
.renameFingerprint(widget.fingerprint, _label);
if (!mounted) return;
Navigator.of(context).pop(renamed);
showMessage(context, l10n.l_fingerprint_renamed);
showMessage(context, l10n.s_fingerprint_renamed);
} catch (e) {
final String errorMessage;
// TODO: Make this cleaner than importing desktop specific RpcError.
@ -75,11 +75,11 @@ class _RenameAccountDialogState extends ConsumerState<RenameFingerprintDialog> {
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return ResponsiveDialog(
title: Text(l10n.l_rename_fp),
title: Text(l10n.s_rename_fp),
actions: [
TextButton(
onPressed: _label.isNotEmpty ? _submit : null,
child: Text(l10n.w_save),
child: Text(l10n.s_save),
),
],
child: Padding(
@ -96,7 +96,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameFingerprintDialog> {
buildCounter: buildByteCounterFor(_label),
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.w_label,
labelText: l10n.s_label,
prefixIcon: const Icon(Icons.fingerprint_outlined),
),
onChanged: (value) {

View File

@ -63,7 +63,7 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return ResponsiveDialog(
title: Text(l10n.l_factory_reset),
title: Text(l10n.s_factory_reset),
onCancel: () {
_subscription?.cancel();
},
@ -100,7 +100,7 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
});
}
: null,
child: Text(l10n.w_reset),
child: Text(l10n.s_reset),
),
],
child: Padding(

View File

@ -48,7 +48,7 @@ class FidoUnlockedPage extends ConsumerWidget {
}
final creds = data.value;
if (creds.isNotEmpty) {
children.add(ListTitle(l10n.w_credentials));
children.add(ListTitle(l10n.s_credentials));
children.addAll(
creds.map(
(cred) => ListTile(
@ -96,7 +96,7 @@ class FidoUnlockedPage extends ConsumerWidget {
final fingerprints = data.value;
if (fingerprints.isNotEmpty) {
nFingerprints = fingerprints.length;
children.add(ListTitle(l10n.w_fingerprints));
children.add(ListTitle(l10n.s_fingerprints));
children.addAll(fingerprints.map((fp) => ListTile(
leading: CircleAvatar(
foregroundColor: Theme.of(context).colorScheme.onSecondary,
@ -137,7 +137,7 @@ class FidoUnlockedPage extends ConsumerWidget {
if (children.isNotEmpty) {
return AppPage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
keyActionsBuilder: (context) =>
fidoBuildActions(context, node, state, nFingerprints),
child: Column(
@ -147,9 +147,9 @@ class FidoUnlockedPage extends ConsumerWidget {
if (state.bioEnroll != null) {
return MessagePage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
graphic: noFingerprints,
header: l10n.l_no_fingerprints,
header: l10n.s_no_fingerprints,
message: l10n.l_add_one_or_more_fps,
keyActionsBuilder: (context) =>
fidoBuildActions(context, node, state, 0),
@ -157,7 +157,7 @@ class FidoUnlockedPage extends ConsumerWidget {
}
return MessagePage(
title: Text(l10n.w_webauthn),
title: Text(l10n.s_webauthn),
graphic: manageAccounts,
header: l10n.l_no_discoverable_accounts,
message: l10n.l_register_sk_on_websites,
@ -166,7 +166,7 @@ class FidoUnlockedPage extends ConsumerWidget {
}
Widget _buildLoadingPage(BuildContext context) => AppPage(
title: Text(AppLocalizations.of(context)!.w_webauthn),
title: Text(AppLocalizations.of(context)!.s_webauthn),
centered: true,
delayedContent: true,
child: const CircularProgressIndicator(),

View File

@ -4,31 +4,37 @@
"@_readme": {
"notes": [
"All strings start with a Captial letter.",
"Group strings by category, but don't needlessly tie them to a section of the app if they can be re-used between several."
"Group strings by category, but don't needlessly tie them to a section of the app if they can be re-used between several.",
"Run check_strings.py on the .arb file to detect problems, tweak @_lint_rules as needed per language."
],
"prefixes": {
"w_": "A single word",
"l_": "A single line. Should not be more than one sentence, and not end with a period.",
"s_": "A single, or few words. Should be short enough to display on a button, or a header.",
"l_": "A single line, can be wrapped. Should not be more than one sentence, and not end with a period.",
"p_": "One or more full sentences, with proper punctuation.",
"q_": "A question, ending in question mark."
}
},
"@_lint_rules": {
"s_max_words": 4,
"s_max_length": 32
},
"app_name": "Yubico Authenticator",
"w_save": "Save",
"w_cancel": "Cancel",
"w_close": "Close",
"w_delete": "Delete",
"w_quit": "Quit",
"w_unlock": "Unlock",
"w_calculate": "Calculate",
"w_label": "Label",
"w_name": "Name",
"w_usb": "USB",
"w_nfc": "NFC",
"l_show_window": "Show window",
"l_hide_window": "Hide window",
"s_save": "Save",
"s_cancel": "Cancel",
"s_close": "Close",
"s_delete": "Delete",
"s_quit": "Quit",
"s_unlock": "Unlock",
"s_calculate": "Calculate",
"s_label": "Label",
"s_name": "Name",
"s_usb": "USB",
"s_nfc": "NFC",
"s_show_window": "Show window",
"s_hide_window": "Hide window",
"q_rename_target": "Rename {label}?",
"@q_rename_target" : {
"placeholders": {
@ -36,62 +42,62 @@
}
},
"w_about": "About",
"w_appearance": "Appearance",
"w_authenticator": "Authenticator",
"w_manage": "Manage",
"w_setup": "Setup",
"w_settings": "Settings",
"w_webauthn": "WebAuthn",
"l_help_and_about": "Help and about",
"l_help_and_feedback": "Help and feedback",
"l_send_feedback": "Send us feedback",
"l_i_need_help": "I need help",
"w_troubleshooting": "Troubleshooting",
"l_terms_of_use": "Terms of use",
"l_privacy_policy": "Privacy policy",
"l_open_src_licenses": "Open source licenses",
"l_configure_yk": "Configure YubiKey",
"l_please_wait": "Please wait\u2026",
"l_secret_key": "Secret key",
"l_invalid_length": "Invalid length",
"l_require_touch": "Require touch",
"s_about": "About",
"s_appearance": "Appearance",
"s_authenticator": "Authenticator",
"s_manage": "Manage",
"s_setup": "Setup",
"s_settings": "Settings",
"s_webauthn": "WebAuthn",
"s_help_and_about": "Help and about",
"s_help_and_feedback": "Help and feedback",
"s_send_feedback": "Send us feedback",
"s_i_need_help": "I need help",
"s_troubleshooting": "Troubleshooting",
"s_terms_of_use": "Terms of use",
"s_privacy_policy": "Privacy policy",
"s_open_src_licenses": "Open source licenses",
"s_configure_yk": "Configure YubiKey",
"s_please_wait": "Please wait\u2026",
"s_secret_key": "Secret key",
"s_invalid_length": "Invalid length",
"s_require_touch": "Require touch",
"q_have_account_info": "Have account info?",
"l_run_diagnostics": "Run diagnostics",
"l_log_level": "Log level: {level}",
"@l_log_level": {
"s_run_diagnostics": "Run diagnostics",
"s_log_level": "Log level: {level}",
"@s_log_level": {
"placeholders": {
"level": {}
}
},
"l_character_count": "Character count",
"l_learn_more": "Learn\u00a0more",
"s_character_count": "Character count",
"s_learn_more": "Learn\u00a0more",
"@_language": {},
"w_language": "Language",
"s_language": "Language",
"l_enable_community_translations": "Enable community translations",
"p_community_translations_desc": "These translations are provided and maintained by the community. They may contain errors or be incomplete.",
"@_theme": {},
"l_app_theme": "App theme",
"l_choose_app_theme": "Choose app theme",
"l_system_default": "System default",
"l_light_mode": "Light mode",
"l_dark_mode": "Dark mode",
"s_app_theme": "App theme",
"s_choose_app_theme": "Choose app theme",
"s_system_default": "System default",
"s_light_mode": "Light mode",
"s_dark_mode": "Dark mode",
"@_yubikey_selection": {},
"l_select_yk": "Select YubiKey",
"l_select_to_scan": "Select to scan",
"l_hide_device": "Hide device",
"l_show_hidden_devices": "Show hidden devices",
"l_sn_serial": "S/N: {serial}",
"@l_sn_serial" : {
"s_select_yk": "Select YubiKey",
"s_select_to_scan": "Select to scan",
"s_hide_device": "Hide device",
"s_show_hidden_devices": "Show hidden devices",
"s_sn_serial": "S/N: {serial}",
"@s_sn_serial" : {
"placeholders": {
"serial": {}
}
},
"l_fw_version": "F/W: {version}",
"@l_fw_version" : {
"s_fw_version": "F/W: {version}",
"@s_fw_version" : {
"placeholders": {
"version": {}
}
@ -105,18 +111,18 @@
"l_place_on_nfc_reader": "Place your YubiKey on the NFC reader",
"l_replace_yk_on_reader": "Place your YubiKey back on the reader",
"l_remove_yk_from_reader": "Remove your YubiKey from the NFC reader",
"l_try_reinsert_yk": "Try to remove and reinsert your YubiKey.",
"l_touch_required": "Touch required",
"p_try_reinsert_yk": "Try to remove and reinsert your YubiKey.",
"s_touch_required": "Touch required",
"l_touch_button_now": "Touch the button on your YubiKey now",
"l_keep_touching_yk": "Keep touching your YubiKey repeatedly\u2026",
"@_app_configuration": {},
"l_toggle_applications": "Toggle applications",
"s_toggle_applications": "Toggle applications",
"l_min_one_interface": "At least one interface must be enabled",
"l_reconfiguring_yk": "Reconfiguring YubiKey\u2026",
"l_config_updated": "Configuration updated",
"s_reconfiguring_yk": "Reconfiguring YubiKey\u2026",
"s_config_updated": "Configuration updated",
"l_config_updated_reinsert": "Configuration updated, remove and reinsert your YubiKey",
"l_app_not_supported": "Application not supported",
"s_app_not_supported": "Application not supported",
"l_app_not_supported_on_yk": "The used YubiKey does not support '${app}' application",
"@l_app_not_supported_on_yk" : {
"placeholders": {
@ -124,65 +130,65 @@
}
},
"l_app_not_supported_desc": "This application is not supported",
"l_app_disabled": "Application disabled",
"s_app_disabled": "Application disabled",
"l_app_disabled_desc": "Enable the '{app}' application on your YubiKey to access",
"@l_app_disabled_desc" : {
"placeholders": {
"app": {}
}
},
"l_fido_disabled": "FIDO2 disabled",
"s_fido_disabled": "FIDO2 disabled",
"l_webauthn_req_fido2": "WebAuthn requires the FIDO2 application to be enabled on your YubiKey",
"@_connectivity_issues": {},
"l_helper_not_responding": "The Helper process isn't responding",
"l_yk_no_access": "This YubiKey cannot be accessed",
"l_yk_inaccessible": "Device inaccessible",
"s_yk_inaccessible": "Device inaccessible",
"l_open_connection_failed": "Failed to open connection",
"l_ccid_connection_failed": "Failed to open smart card connection",
"p_ccid_service_unavailable": "Make sure your smart card service is functioning.",
"p_pcscd_unavailable": "Make sure pcscd is installed and running.",
"l_no_yk_present": "No YubiKey present",
"l_unknown_type": "Unknown type",
"l_unknown_device": "Unrecognized device",
"l_unsupported_yk": "Unsupported YubiKey",
"l_yk_not_recognized": "Device not recognized",
"s_unknown_type": "Unknown type",
"s_unknown_device": "Unrecognized device",
"s_unsupported_yk": "Unsupported YubiKey",
"s_yk_not_recognized": "Device not recognized",
"@_general_errors": {},
"l_error_occured": "An error has occured",
"l_application_error": "Application error",
"s_application_error": "Application error",
"l_import_error": "Import error",
"l_file_not_found": "File not found",
"l_file_too_big": "File size too big",
"l_filesystem_error": "File system operation error",
"@_pins": {},
"w_pin": "PIN",
"l_set_pin": "Set PIN",
"l_change_pin": "Change PIN",
"l_current_pin": "Current PIN",
"l_new_pin": "New PIN",
"l_confirm_pin": "Confirm PIN",
"s_pin": "PIN",
"s_set_pin": "Set PIN",
"s_change_pin": "Change PIN",
"s_current_pin": "Current PIN",
"s_new_pin": "New PIN",
"s_confirm_pin": "Confirm PIN",
"l_new_pin_len": "New PIN must be at least {length} characters",
"@l_new_pin_len" : {
"placeholders": {
"length": {}
}
},
"l_pin_set": "PIN set",
"s_pin_set": "PIN set",
"l_set_pin_failed": "Failed to set PIN: {message}",
"@l_set_pin_failed" : {
"placeholders": {
"message": {}
}
},
"l_wrong_pin_attempts_remaining": "Wrong PIN. {retries} attempt(s) remaining.",
"l_wrong_pin_attempts_remaining": "Wrong PIN, {retries} attempt(s) remaining",
"@l_wrong_pin_attempts_remaining" : {
"placeholders": {
"retries": {}
}
},
"l_fido_pin_protection": "FIDO PIN protection",
"s_fido_pin_protection": "FIDO PIN protection",
"l_fido_pin_protection_optional": "Optional FIDO PIN protection",
"l_enter_fido2_pin": "Enter the FIDO2 PIN for your YubiKey",
"l_optionally_set_a_pin": "Optionally set a PIN to protect access to your YubiKey\nRegister as a Security Key on websites",
@ -199,20 +205,20 @@
},
"@_passwords": {},
"w_password": "Password",
"l_manage_password": "Manage password",
"l_set_password": "Set password",
"l_password_set": "Password set",
"s_password": "Password",
"s_manage_password": "Manage password",
"s_set_password": "Set password",
"s_password_set": "Password set",
"l_optional_password_protection": "Optional password protection",
"l_new_password": "New password",
"l_current_password": "Current password",
"l_confirm_password": "Confirm password",
"l_wrong_password": "Wrong password",
"l_remove_password": "Remove password",
"l_password_removed": "Password removed",
"l_remember_password": "Remember password",
"l_clear_saved_password": "Clear saved password",
"l_password_forgotten": "Password forgotten",
"s_new_password": "New password",
"s_current_password": "Current password",
"s_confirm_password": "Confirm password",
"s_wrong_password": "Wrong password",
"s_remove_password": "Remove password",
"s_password_removed": "Password removed",
"s_remember_password": "Remember password",
"s_clear_saved_password": "Clear saved password",
"s_password_forgotten": "Password forgotten",
"l_keystore_unavailable": "OS Keystore unavailable",
"l_remember_pw_failed": "Failed to remember password",
"l_unlock_first": "Unlock with password first",
@ -227,10 +233,10 @@
"label": {}
}
},
"w_accounts": "Accounts",
"l_no_accounts": "No accounts",
"l_add_account": "Add account",
"l_account_added": "Account added",
"s_accounts": "Accounts",
"s_no_accounts": "No accounts",
"s_add_account": "Add account",
"s_account_added": "Account added",
"l_account_add_failed": "Failed adding account: {message}",
"@l_account_add_failed" : {
"placeholders": {
@ -240,19 +246,19 @@
"l_account_name_required": "Your account must have a name",
"l_name_already_exists": "This name already exists for the issuer",
"l_invalid_character_issuer": "Invalid character: ':' is not allowed in issuer",
"w_pinned": "Pinned",
"l_pin_account": "Pin account",
"l_unpin_account": "Unpin account",
"l_no_pinned_accounts": "No pinned accounts",
"l_rename_account": "Rename account",
"l_account_renamed": "Account renamed",
"s_pinned": "Pinned",
"s_pin_account": "Pin account",
"s_unpin_account": "Unpin account",
"s_no_pinned_accounts": "No pinned accounts",
"s_rename_account": "Rename account",
"s_account_renamed": "Account renamed",
"p_rename_will_change_account_displayed": "This will change how the account is displayed in the list.",
"l_delete_account": "Delete account",
"l_account_deleted": "Account deleted",
"s_delete_account": "Delete account",
"s_account_deleted": "Account deleted",
"p_warning_delete_account": "Warning! This action will delete the account from your YubiKey.",
"p_warning_disable_credential": "You will no longer be able to generate OTPs for this account. Make sure to first disable this credential from the website to avoid being locked out of your account.",
"l_account_name": "Account name",
"l_search_accounts": "Search accounts",
"s_account_name": "Account name",
"s_search_accounts": "Search accounts",
"l_accounts_used": "{used} of {capacity} accounts used",
"@l_accounts_used" : {
"placeholders": {
@ -260,19 +266,19 @@
"capacity": {}
}
},
"l_num_digits": "{num} digits",
"@l_num_digits" : {
"s_num_digits": "{num} digits",
"@s_num_digits" : {
"placeholders": {
"num": {}
}
},
"l_num_sec": "{num} sec",
"@l_num_sec" : {
"s_num_sec": "{num} sec",
"@s_num_sec" : {
"placeholders": {
"num": {}
}
},
"l_issuer_optional": "Issuer (optional)",
"s_issuer_optional": "Issuer (optional)",
"@_fido_credentials": {},
"l_credential": "Credential: {label}",
@ -281,12 +287,12 @@
"label": {}
}
},
"w_credentials": "Credentials",
"s_credentials": "Credentials",
"l_ready_to_use": "Ready to use",
"l_register_sk_on_websites": "Register as a Security Key on websites",
"l_no_discoverable_accounts": "No discoverable accounts",
"l_delete_credential": "Delete credential",
"l_credential_deleted": "Credential deleted",
"s_delete_credential": "Delete credential",
"s_credential_deleted": "Credential deleted",
"p_warning_delete_credential": "This will delete the credential from your YubiKey.",
"@_fingerprints": {},
@ -296,32 +302,32 @@
"label": {}
}
},
"w_fingerprints": "Fingerprints",
"s_fingerprints": "Fingerprints",
"l_fingerprint_captured": "Fingerprint captured successfully!",
"l_fingerprint_added": "Fingerprint added",
"s_fingerprint_added": "Fingerprint added",
"l_setting_name_failed": "Error setting name: {message}",
"@l_setting_name_failed" : {
"placeholders": {
"message": {}
}
},
"l_add_fingerprint": "Add fingerprint",
"s_add_fingerprint": "Add fingerprint",
"l_fp_step_1_capture": "Step 1/2: Capture fingerprint",
"l_fp_step_2_name": "Step 2/2: Name fingerprint",
"l_delete_fingerprint": "Delete fingerprint",
"l_fingerprint_deleted": "Fingerprint deleted",
"s_delete_fingerprint": "Delete fingerprint",
"s_fingerprint_deleted": "Fingerprint deleted",
"p_warning_delete_fingerprint": "This will delete the fingerprint from your YubiKey.",
"l_no_fingerprints": "No fingerprints",
"s_no_fingerprints": "No fingerprints",
"l_set_pin_fingerprints": "Set a PIN to register fingerprints",
"l_no_fps_added": "No fingerprints have been added",
"l_fingerprint_renamed": "Fingerprint renamed",
"s_rename_fp": "Rename fingerprint",
"s_fingerprint_renamed": "Fingerprint renamed",
"l_rename_fp_failed": "Error renaming: {message}",
"@l_rename_fp_failed" : {
"placeholders": {
"message": {}
}
},
"l_rename_fp": "Rename fingerprint",
"l_add_one_or_more_fps": "Add one or more (up to five) fingerprints",
"l_fingerprints_used": "{used}/5 fingerprints registered",
"@l_fingerprints_used": {
@ -333,16 +339,16 @@
"p_will_change_label_fp": "This will change the label of the fingerprint.",
"@_permissions": {},
"l_enable_nfc": "Enable NFC",
"l_permission_denied": "Permission denied",
"s_enable_nfc": "Enable NFC",
"s_permission_denied": "Permission denied",
"l_elevating_permissions": "Elevating permissions\u2026",
"l_review_permissions": "Review permissions",
"s_review_permissions": "Review permissions",
"p_elevated_permissions_required": "Managing this device requires elevated privileges.",
"p_webauthn_elevated_permissions_required": "WebAuthn management requires elevated privileges.",
"p_need_camera_permission": "Yubico Authenticator needs Camera permissions for scanning QR codes.",
"@_qr_codes": {},
"l_qr_scan": "Scan QR code",
"s_qr_scan": "Scan QR code",
"l_qr_scanned": "Scanned QR code",
"l_invalid_qr": "Invalid QR code",
"l_qr_not_found": "No QR code found",
@ -355,15 +361,15 @@
"l_point_camera_scan": "Point your camera at a QR code to scan it",
"q_want_to_scan": "Would like to scan?",
"q_no_qr": "No QR code?",
"l_enter_manually": "Enter manually",
"s_enter_manually": "Enter manually",
"@_factory_reset": {},
"w_reset": "Reset",
"l_factory_reset": "Factory reset",
"s_reset": "Reset",
"s_factory_reset": "Factory reset",
"l_factory_reset_this_app": "Factory reset this application",
"l_reset_oath": "Reset OATH",
"s_reset_oath": "Reset OATH",
"l_oath_application_reset": "OATH application reset",
"l_reset_fido": "Reset FIDO",
"s_reset_fido": "Reset FIDO",
"l_fido_app_reset": "FIDO application reset",
"l_press_reset_to_begin": "Press reset to begin\u2026",
"l_reset_failed": "Error performing reset: {message}",
@ -379,9 +385,9 @@
"@_copy_to_clipboard": {},
"l_copy_to_clipboard": "Copy to clipboard",
"l_code_copied": "Code copied",
"s_code_copied": "Code copied",
"l_code_copied_clipboard": "Code copied to clipboard",
"l_copy_log": "Copy log",
"s_copy_log": "Copy log",
"l_log_copied": "Log copied to clipboard",
"l_diagnostics_copied": "Diagnostic data copied to clipboard",
"p_target_copied_clipboard": "{label} copied to clipboard.",
@ -392,16 +398,16 @@
},
"@_custom_icons": {},
"l_custom_icons": "Custom icons",
"s_custom_icons": "Custom icons",
"l_set_icons_for_accounts": "Set icons for accounts",
"p_custom_icons_description": "Icon packs can make your accounts more easily distinguishable with familiar logos and colors.",
"l_replace_icon_pack": "Replace icon pack",
"s_replace_icon_pack": "Replace icon pack",
"l_loading_icon_pack": "Loading icon pack\u2026",
"l_load_icon_pack": "Load icon pack",
"l_remove_icon_pack": "Remove icon pack",
"s_load_icon_pack": "Load icon pack",
"s_remove_icon_pack": "Remove icon pack",
"l_icon_pack_removed": "Icon pack removed",
"l_remove_icon_pack_failed": "Error removing icon pack",
"l_choose_icon_pack": "Choose icon pack",
"s_choose_icon_pack": "Choose icon pack",
"l_icon_pack_imported": "Icon pack imported",
"l_import_icon_pack_failed": "Error importing icon pack: {message}",
"@l_import_icon_pack_failed": {
@ -412,24 +418,24 @@
"l_invalid_icon_pack": "Invalid icon pack",
"@_android_settings": {},
"l_nfc_options": "NFC options",
"s_nfc_options": "NFC options",
"l_on_yk_nfc_tap": "On YubiKey NFC tap",
"l_launch_ya": "Launch Yubico Authenticator",
"l_copy_otp_clipboard": "Copy OTP to clipboard",
"l_launch_and_copy_otp": "Launch app and copy OTP",
"l_kbd_layout_for_static": "Keyboard layout (for static password)",
"l_choose_kbd_layout": "Choose keyboard layout",
"s_choose_kbd_layout": "Choose keyboard layout",
"l_bypass_touch_requirement": "Bypass touch requirement",
"l_bypass_touch_requirement_on": "Accounts that require touch are automatically shown over NFC",
"l_bypass_touch_requirement_off": "Accounts that require touch need an additional tap over NFC",
"l_silence_nfc_sounds": "Silence NFC sounds",
"s_silence_nfc_sounds": "Silence NFC sounds",
"l_silence_nfc_sounds_on": "No sounds will be played on NFC tap",
"l_silence_nfc_sounds_off": "Sound will play on NFC tap",
"l_usb_options": "USB options",
"s_usb_options": "USB options",
"l_launch_app_on_usb": "Launch when YubiKey is connected",
"l_launch_app_on_usb_on": "This prevents other apps from using the YubiKey over USB",
"l_launch_app_on_usb_off": "Other apps can use the YubiKey over USB",
"l_allow_screenshots": "Allow screenshots",
"s_allow_screenshots": "Allow screenshots",
"@_eof": {}
}

View File

@ -116,7 +116,7 @@ class _CapabilitiesForm extends StatelessWidget {
if (usbCapabilities != 0) ...[
ListTile(
leading: const Icon(Icons.usb),
title: Text(l10n.w_usb),
title: Text(l10n.s_usb),
contentPadding: const EdgeInsets.only(bottom: 8),
horizontalTitleGap: 0,
),
@ -137,7 +137,7 @@ class _CapabilitiesForm extends StatelessWidget {
),
ListTile(
leading: nfcIcon,
title: Text(l10n.w_nfc),
title: Text(l10n.s_nfc),
contentPadding: const EdgeInsets.only(bottom: 8),
horizontalTitleGap: 0,
),
@ -212,7 +212,7 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
// This will take longer, show a message
close = showMessage(
context,
l10n.l_reconfiguring_yk,
l10n.s_reconfiguring_yk,
duration: const Duration(seconds: 8),
);
}
@ -225,7 +225,7 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
);
if (!mounted) return;
if (!reboot) Navigator.pop(context);
showMessage(context, l10n.l_config_updated);
showMessage(context, l10n.s_config_updated);
} finally {
close?.call();
}
@ -250,7 +250,7 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
showMessage(
context,
widget.deviceData.node.maybeMap(
nfcReader: (_) => l10n.l_config_updated,
nfcReader: (_) => l10n.s_config_updated,
orElse: () => l10n.l_config_updated_reinsert));
Navigator.pop(context);
}
@ -318,12 +318,12 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
);
return ResponsiveDialog(
title: Text(l10n.l_toggle_applications),
title: Text(l10n.s_toggle_applications),
actions: [
TextButton(
onPressed: canSave ? _submitForm : null,
key: management_keys.saveButtonKey,
child: Text(l10n.w_save),
child: Text(l10n.s_save),
),
],
child: child,

View File

@ -34,7 +34,7 @@ class IconPackDialog extends ConsumerWidget {
final l10n = AppLocalizations.of(context)!;
final iconPack = ref.watch(iconPackProvider);
return ResponsiveDialog(
title: Text(l10n.l_custom_icons),
title: Text(l10n.s_custom_icons),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0),
child: Column(
@ -66,9 +66,9 @@ class IconPackDialog extends ConsumerWidget {
Widget? _action(AsyncValue<IconPack?> iconPack, AppLocalizations l10n) =>
iconPack.when(
data: (IconPack? data) => _ImportActionChip(
data != null ? l10n.l_replace_icon_pack : l10n.l_load_icon_pack),
data != null ? l10n.s_replace_icon_pack : l10n.s_load_icon_pack),
error: (Object error, StackTrace stackTrace) =>
_ImportActionChip(l10n.l_load_icon_pack),
_ImportActionChip(l10n.s_load_icon_pack),
loading: () => _ImportActionChip(
l10n.l_loading_icon_pack,
avatar: const CircularProgressIndicator(),
@ -96,7 +96,7 @@ class _DialogDescription extends ConsumerWidget {
TextSpan _createLearnMoreLink(BuildContext context) {
final theme = Theme.of(context);
return TextSpan(
text: AppLocalizations.of(context)!.l_learn_more,
text: AppLocalizations.of(context)!.s_learn_more,
style: theme.textTheme.bodyMedium
?.copyWith(color: theme.colorScheme.primary),
recognizer: TapGestureRecognizer()
@ -134,7 +134,7 @@ class _IconPackDescription extends ConsumerWidget {
Row(
children: [
IconButton(
tooltip: l10n.l_remove_icon_pack,
tooltip: l10n.s_remove_icon_pack,
onPressed: () async {
final removePackStatus =
await ref.read(iconPackProvider.notifier).removePack();
@ -179,7 +179,7 @@ class _ImportActionChip extends ConsumerWidget {
type: FileType.custom,
allowMultiple: false,
lockParentWindow: true,
dialogTitle: l10n.l_choose_icon_pack);
dialogTitle: l10n.s_choose_icon_pack);
if (result != null && result.files.isNotEmpty) {
final importStatus = await ref
.read(iconPackProvider.notifier)

View File

@ -46,7 +46,7 @@ class AccountDialog extends ConsumerWidget {
final copy =
actions.firstWhere(((e) => e.text == l10n.l_copy_to_clipboard));
final delete = actions.firstWhere(((e) => e.text == l10n.l_delete_account));
final delete = actions.firstWhere(((e) => e.text == l10n.s_delete_account));
final colors = {
copy: Pair(theme.primary, theme.onPrimary),
delete: Pair(theme.error, theme.onError),
@ -54,7 +54,7 @@ class AccountDialog extends ConsumerWidget {
// If we can't copy, but can calculate, highlight that button instead
if (copy.intent == null) {
final calculates = actions.where(((e) => e.text == l10n.w_calculate));
final calculates = actions.where(((e) => e.text == l10n.s_calculate));
if (calculates.isNotEmpty) {
colors[calculates.first] = Pair(theme.primary, theme.onPrimary);
}

View File

@ -73,12 +73,12 @@ class AccountHelper {
),
if (manual)
MenuAction(
text: l10n.w_calculate,
text: l10n.s_calculate,
icon: const Icon(Icons.refresh),
intent: ready ? const CalculateIntent() : null,
),
MenuAction(
text: pinned ? l10n.l_unpin_account : l10n.l_pin_account,
text: pinned ? l10n.s_unpin_account : l10n.s_pin_account,
icon: pinned
? pushPinStrokeIcon
: const Icon(Icons.push_pin_outlined),
@ -87,11 +87,11 @@ class AccountHelper {
if (data.info.version.isAtLeast(5, 3))
MenuAction(
icon: const Icon(Icons.edit_outlined),
text: l10n.l_rename_account,
text: l10n.s_rename_account,
intent: const EditIntent(),
),
MenuAction(
text: l10n.l_delete_account,
text: l10n.s_delete_account,
icon: const Icon(Icons.delete_outline),
intent: const DeleteIntent(),
),

View File

@ -34,7 +34,7 @@ class AccountList extends ConsumerWidget {
final favorites = ref.watch(favoritesProvider);
if (credentials.isEmpty) {
return Center(
child: Text(l10n.l_no_accounts),
child: Text(l10n.s_no_accounts),
);
}
@ -47,13 +47,13 @@ class AccountList extends ConsumerWidget {
policy: WidgetOrderTraversalPolicy(),
child: Column(
children: [
if (pinnedCreds.isNotEmpty) ListTitle(l10n.w_pinned),
if (pinnedCreds.isNotEmpty) ListTitle(l10n.s_pinned),
...pinnedCreds.map(
(entry) => AccountView(
entry.credential,
),
),
if (creds.isNotEmpty) ListTitle(l10n.w_accounts),
if (creds.isNotEmpty) ListTitle(l10n.s_accounts),
...creds.map(
(entry) => AccountView(
entry.credential,

View File

@ -188,7 +188,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
}
if (!mounted) return;
Navigator.of(context).pop();
showMessage(context, l10n.l_account_added);
showMessage(context, l10n.s_account_added);
} on CancellationException catch (_) {
// ignored
} catch (e) {
@ -244,7 +244,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
0) !=
0) {
if (oathState == null) {
_promptController?.updateContent(title: l10n.l_please_wait);
_promptController?.updateContent(title: l10n.s_please_wait);
} else if (oathState.locked) {
_promptController?.close();
} else {
@ -256,12 +256,12 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
));
}
} else {
_promptController?.updateContent(title: l10n.l_unsupported_yk);
_promptController?.updateContent(title: l10n.s_unsupported_yk);
}
}, error: (error, _) {
_promptController?.updateContent(title: l10n.l_unsupported_yk);
_promptController?.updateContent(title: l10n.s_unsupported_yk);
}, loading: () {
_promptController?.updateContent(title: l10n.l_please_wait);
_promptController?.updateContent(title: l10n.s_please_wait);
});
}
@ -343,7 +343,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
_promptController = promptUserInteraction(
context,
title: l10n.l_insert_yk,
description: l10n.l_add_account,
description: l10n.s_add_account,
icon: const Icon(Icons.usb),
onCancel: () {
_otpauthUri = null;
@ -358,11 +358,11 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
}
return ResponsiveDialog(
title: Text(l10n.l_add_account),
title: Text(l10n.s_add_account),
actions: [
TextButton(
onPressed: isValid ? submit : null,
child: Text(l10n.w_save, key: keys.saveButton),
child: Text(l10n.s_save, key: keys.saveButton),
),
],
child: FileDropTarget(
@ -402,7 +402,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
buildCounter: buildByteCounterFor(issuerText),
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_issuer_optional,
labelText: l10n.s_issuer_optional,
helperText:
'', // Prevents dialog resizing when disabled
prefixIcon: const Icon(Icons.business_outlined),
@ -431,7 +431,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
decoration: InputDecoration(
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.person_outline),
labelText: l10n.l_account_name,
labelText: l10n.s_account_name,
helperText:
'', // Prevents dialog resizing when disabled
errorText: (byteLength(nameText) > nameMaxLength)
@ -474,9 +474,9 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
),
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.key_outlined),
labelText: l10n.l_secret_key,
labelText: l10n.s_secret_key,
errorText: _validateSecretLength && !secretLengthValid
? l10n.l_invalid_length
? l10n.s_invalid_length
: null),
readOnly: _qrState == _QrScanState.success,
textInputAction: TextInputAction.done,
@ -502,7 +502,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
strokeWidth: 2.0),
label: _qrState == _QrScanState.success
? Text(l10n.l_qr_scanned)
: Text(l10n.l_qr_scan),
: Text(l10n.s_qr_scan),
onPressed: () {
_scanQrCode(qrScanner);
}),
@ -515,7 +515,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
children: [
if (oathState?.version.isAtLeast(4, 2) ?? true)
FilterChip(
label: Text(l10n.l_require_touch),
label: Text(l10n.s_require_touch),
selected: _touch,
onSelected: (value) {
setState(() {
@ -557,7 +557,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
selected: int.tryParse(_periodController.text) !=
defaultPeriod,
itemBuilder: ((value) =>
Text(l10n.l_num_sec(value))),
Text(l10n.s_num_sec(value))),
onChanged: _qrState != _QrScanState.success
? (period) {
setState(() {
@ -571,7 +571,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
value: _digits,
selected: _digits != defaultDigits,
itemBuilder: (value) =>
Text(l10n.l_num_digits(value)),
Text(l10n.s_num_digits(value)),
onChanged: _qrState != _QrScanState.success
? (digits) {
setState(() {

View File

@ -37,7 +37,7 @@ class DeleteAccountDialog extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!;
return ResponsiveDialog(
title: Text(l10n.l_delete_account),
title: Text(l10n.s_delete_account),
actions: [
TextButton(
key: keys.deleteButton,
@ -49,14 +49,14 @@ class DeleteAccountDialog extends ConsumerWidget {
await ref.read(withContextProvider)(
(context) async {
Navigator.of(context).pop(true);
showMessage(context, l10n.l_account_deleted);
showMessage(context, l10n.s_account_deleted);
},
);
} on CancellationException catch (_) {
// ignored
}
},
child: Text(l10n.w_delete),
child: Text(l10n.s_delete),
),
],
child: Padding(

View File

@ -45,9 +45,9 @@ Widget oathBuildActions(
final theme = Theme.of(context).colorScheme;
return SimpleDialog(
children: [
ListTitle(l10n.w_setup, textStyle: Theme.of(context).textTheme.bodyLarge),
ListTitle(l10n.s_setup, textStyle: Theme.of(context).textTheme.bodyLarge),
ListTile(
title: Text(l10n.l_add_account),
title: Text(l10n.s_add_account),
key: keys.addAccountAction,
leading:
const CircleAvatar(child: Icon(Icons.person_add_alt_1_outlined)),
@ -87,11 +87,11 @@ Widget oathBuildActions(
}
: null,
),
ListTitle(l10n.w_manage,
ListTitle(l10n.s_manage,
textStyle: Theme.of(context).textTheme.bodyLarge),
ListTile(
key: keys.customIconsAction,
title: Text(l10n.l_custom_icons),
title: Text(l10n.s_custom_icons),
subtitle: Text(l10n.l_set_icons_for_accounts),
leading: const CircleAvatar(
child: Icon(Icons.image_outlined),
@ -108,7 +108,7 @@ Widget oathBuildActions(
ListTile(
key: keys.setOrManagePasswordAction,
title: Text(
oathState.hasKey ? l10n.l_manage_password : l10n.l_set_password),
oathState.hasKey ? l10n.s_manage_password : l10n.s_set_password),
subtitle: Text(l10n.l_optional_password_protection),
leading: const CircleAvatar(child: Icon(Icons.password_outlined)),
onTap: () {
@ -120,7 +120,7 @@ Widget oathBuildActions(
}),
ListTile(
key: keys.resetAction,
title: Text(l10n.l_reset_oath),
title: Text(l10n.s_reset_oath),
subtitle: Text(l10n.l_factory_reset_this_app),
leading: CircleAvatar(
foregroundColor: theme.onError,

View File

@ -48,7 +48,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
if (result) {
if (!mounted) return;
Navigator.of(context).pop();
showMessage(context, AppLocalizations.of(context)!.l_password_set);
showMessage(context, AppLocalizations.of(context)!.s_password_set);
} else {
setState(() {
_currentIsWrong = true;
@ -64,12 +64,12 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
(!widget.state.hasKey || _currentPassword.isNotEmpty);
return ResponsiveDialog(
title: Text(l10n.l_manage_password),
title: Text(l10n.s_manage_password),
actions: [
TextButton(
onPressed: isValid ? _submit : null,
key: keys.savePasswordButton,
child: Text(l10n.w_save),
child: Text(l10n.s_save),
)
],
child: Padding(
@ -85,9 +85,9 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
key: keys.currentPasswordField,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_current_password,
labelText: l10n.s_current_password,
prefixIcon: const Icon(Icons.password_outlined),
errorText: _currentIsWrong ? l10n.l_wrong_password : null,
errorText: _currentIsWrong ? l10n.s_wrong_password : null,
errorMaxLines: 3),
textInputAction: TextInputAction.next,
onChanged: (value) {
@ -111,7 +111,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
if (result) {
if (!mounted) return;
Navigator.of(context).pop();
showMessage(context, l10n.l_password_removed);
showMessage(context, l10n.s_password_removed);
} else {
setState(() {
_currentIsWrong = true;
@ -119,18 +119,18 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
}
}
: null,
child: Text(l10n.l_remove_password),
child: Text(l10n.s_remove_password),
),
if (widget.state.remembered)
OutlinedButton(
child: Text(l10n.l_clear_saved_password),
child: Text(l10n.s_clear_saved_password),
onPressed: () async {
await ref
.read(oathStateProvider(widget.path).notifier)
.forgetPassword();
if (!mounted) return;
Navigator.of(context).pop();
showMessage(context, l10n.l_password_forgotten);
showMessage(context, l10n.s_password_forgotten);
},
),
],
@ -143,7 +143,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
obscureText: true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_new_password,
labelText: l10n.s_new_password,
prefixIcon: const Icon(Icons.password_outlined),
enabled: !widget.state.hasKey || _currentPassword.isNotEmpty,
),
@ -164,7 +164,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
obscureText: true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_confirm_password,
labelText: l10n.s_confirm_password,
prefixIcon: const Icon(Icons.password_outlined),
enabled:
(!widget.state.hasKey || _currentPassword.isNotEmpty) &&

View File

@ -42,12 +42,12 @@ class OathScreen extends ConsumerWidget {
final l10n = AppLocalizations.of(context)!;
return ref.watch(oathStateProvider(devicePath)).when(
loading: () => MessagePage(
title: Text(l10n.w_authenticator),
title: Text(l10n.s_authenticator),
graphic: const CircularProgressIndicator(),
delayedContent: true,
),
error: (error, _) => AppFailurePage(
title: Text(l10n.w_authenticator),
title: Text(l10n.s_authenticator),
cause: error,
),
data: (oathState) => oathState.locked
@ -66,7 +66,7 @@ class _LockedView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return AppPage(
title: Text(AppLocalizations.of(context)!.w_authenticator),
title: Text(AppLocalizations.of(context)!.s_authenticator),
keyActionsBuilder: (context) =>
oathBuildActions(context, devicePath, oathState, ref),
child: Padding(
@ -116,10 +116,10 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
.select((value) => value?.length));
if (numCreds == 0) {
return MessagePage(
title: Text(l10n.w_authenticator),
title: Text(l10n.s_authenticator),
key: keys.noAccountsView,
graphic: noAccounts,
header: l10n.l_no_accounts,
header: l10n.s_no_accounts,
keyActionsBuilder: (context) => oathBuildActions(
context, widget.devicePath, widget.oathState, ref,
used: 0),
@ -154,7 +154,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
style: textTheme.titleMedium
?.copyWith(fontSize: textTheme.titleSmall?.fontSize),
decoration: InputDecoration(
hintText: l10n.l_search_accounts,
hintText: l10n.s_search_accounts,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(32)),
),

View File

@ -73,7 +73,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
if (!mounted) return;
Navigator.of(context).pop(renamed);
showMessage(context, l10n.l_account_renamed);
showMessage(context, l10n.s_account_renamed);
} on CancellationException catch (_) {
// ignored
} catch (e) {
@ -127,12 +127,12 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
final isValid = isUnique && isValidFormat;
return ResponsiveDialog(
title: Text(l10n.l_rename_account),
title: Text(l10n.s_rename_account),
actions: [
TextButton(
onPressed: didChange && isValid ? _submit : null,
key: keys.saveButton,
child: Text(l10n.w_save),
child: Text(l10n.s_save),
),
],
child: Padding(
@ -151,7 +151,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
key: keys.issuerField,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_issuer_optional,
labelText: l10n.s_issuer_optional,
helperText: '', // Prevents dialog resizing when disabled
prefixIcon: const Icon(Icons.business_outlined),
),
@ -170,7 +170,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
key: keys.nameField,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.l_account_name,
labelText: l10n.s_account_name,
helperText: '', // Prevents dialog resizing when disabled
errorText: !isValidFormat
? l10n.l_account_name_required

View File

@ -32,7 +32,7 @@ class ResetDialog extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!;
return ResponsiveDialog(
title: Text(l10n.l_factory_reset),
title: Text(l10n.s_factory_reset),
actions: [
TextButton(
onPressed: () async {
@ -42,7 +42,7 @@ class ResetDialog extends ConsumerWidget {
showMessage(context, l10n.l_oath_application_reset);
});
},
child: Text(l10n.w_reset),
child: Text(l10n.s_reset),
),
],
child: Padding(

View File

@ -79,8 +79,8 @@ class _UnlockFormState extends ConsumerState<UnlockForm> {
obscureText: _isObscure,
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: l10n.w_password,
errorText: _passwordIsWrong ? l10n.l_wrong_password : null,
labelText: l10n.s_password,
errorText: _passwordIsWrong ? l10n.s_wrong_password : null,
helperText: '', // Prevents resizing when errorText shown
prefixIcon: const Icon(Icons.password_outlined),
suffixIcon: IconButton(
@ -111,7 +111,7 @@ class _UnlockFormState extends ConsumerState<UnlockForm> {
minLeadingWidth: 0,
)
: CheckboxListTile(
title: Text(l10n.l_remember_password),
title: Text(l10n.s_remember_password),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
value: _remember,
@ -127,7 +127,7 @@ class _UnlockFormState extends ConsumerState<UnlockForm> {
alignment: Alignment.centerRight,
child: ElevatedButton.icon(
key: keys.unlockButton,
label: Text(l10n.w_unlock),
label: Text(l10n.s_unlock),
icon: const Icon(Icons.lock_open),
onPressed: _passwordController.text.isNotEmpty ? _submit : null,
),

View File

@ -39,7 +39,7 @@ class SettingsPage extends ConsumerWidget {
final theme = Theme.of(context);
final enableTranslations = ref.watch(communityTranslationsProvider);
return ResponsiveDialog(
title: Text(l10n.w_settings),
title: Text(l10n.s_settings),
child: Theme(
// Make the headers use the primary color to pop a bit.
// Once M3 is implemented this will probably not be needed.
@ -52,9 +52,9 @@ class SettingsPage extends ConsumerWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTitle(l10n.w_appearance),
ListTitle(l10n.s_appearance),
RadioListTile<ThemeMode>(
title: Text(l10n.l_system_default),
title: Text(l10n.s_system_default),
value: ThemeMode.system,
groupValue: themeMode,
onChanged: (mode) {
@ -63,7 +63,7 @@ class SettingsPage extends ConsumerWidget {
},
),
RadioListTile<ThemeMode>(
title: Text(l10n.l_light_mode),
title: Text(l10n.s_light_mode),
value: ThemeMode.light,
groupValue: themeMode,
onChanged: (mode) {
@ -72,7 +72,7 @@ class SettingsPage extends ConsumerWidget {
},
),
RadioListTile<ThemeMode>(
title: Text(l10n.l_dark_mode),
title: Text(l10n.s_dark_mode),
value: ThemeMode.dark,
groupValue: themeMode,
onChanged: (mode) {
@ -84,7 +84,7 @@ class SettingsPage extends ConsumerWidget {
basicLocaleListResolution(window.locales, officialLocales) !=
basicLocaleListResolution(
window.locales, AppLocalizations.supportedLocales)) ...[
ListTitle(l10n.w_language),
ListTitle(l10n.s_language),
SwitchListTile(
title: Text(l10n.l_enable_community_translations),
subtitle: Text(l10n.p_community_translations_desc),

View File

@ -62,8 +62,8 @@ class _ResponsiveDialogState extends State<ResponsiveDialog> {
} else {
// Dialog
final cancelText = widget.onCancel == null && widget.actions.isEmpty
? l10n.w_close
: l10n.w_cancel;
? l10n.s_close
: l10n.s_cancel;
return AlertDialog(
title: widget.title,
titlePadding: const EdgeInsets.only(top: 24, left: 18, right: 18),

View File

@ -38,7 +38,7 @@ InputCounterWidgetBuilder buildByteCounterFor(String currentValue) =>
return Text(
maxLength != null ? '${byteLength(currentValue)}/$maxLength' : '',
style: style,
semanticsLabel: AppLocalizations.of(context)!.l_character_count,
semanticsLabel: AppLocalizations.of(context)!.s_character_count,
);
};