Merge updated strings

This commit is contained in:
Dain Nilsson 2024-04-29 09:30:33 +02:00
commit 2e7dfc54bf
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
15 changed files with 1019 additions and 1020 deletions

View File

@ -1,2 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources />
<resources>
<string name="p_ndef_set_otp">Code OTP copié de la YubiKey dans le presse-papiers.</string>
<string name="p_ndef_set_password">Mot de passe copié de la YubiKey dans le presse-papiers.</string>
<string name="p_ndef_parse_failure">Impossible d\'analyser le code OTP de la YubiKey.</string>
<string name="p_ndef_set_clip_failure">Presse-papiers inaccessible lors de la tentative de copie du code OTP depuis la YubiKey.</string>
</resources>

View File

@ -1,2 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources />
<resources>
<string name="p_ndef_set_otp">OTPコードがYubiKeyからクリップボードに正常にコピーされました。</string>
<string name="p_ndef_set_password">パスワードがYubiKeyからクリップボードに正常にコピーされました。</string>
<string name="p_ndef_parse_failure">YubiKeyからのOTPコードを解析できませんでした。</string>
<string name="p_ndef_set_clip_failure">YubiKeyからのOTPコードのコピー試行時にクリップボードにアクセスできませんでした。</string>
</resources>

View File

@ -19,6 +19,7 @@ import json
import os
import sys
non_words = (":",)
errors = []
@ -46,8 +47,9 @@ def check_prefixes(k, v, s_max_words, s_max_len, p_ending_chars, q_ending_chars)
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())})")
n_words = len([w for w in v.split() if w not in non_words])
if n_words > s_max_words:
errs.append(f"Too many words ({n_words})")
if k.startswith("l_") or k.startswith("s_"):
if v.endswith("."):
errs.append("Ends with '.'")
@ -89,7 +91,7 @@ def lint_strings(strings, rules):
k,
v,
rules.get("s_max_words", 4),
rules.get("s_max_len", 32),
rules.get("s_max_length", 32),
rules.get("p_ending_chars", ".!"),
rules.get("q_ending_chars", "?"),
)

127
crowdin.yaml Normal file
View File

@ -0,0 +1,127 @@
#
# Your Crowdin credentials
#
"project_id_env": "CROWDIN_PROJECT_ID"
"api_token_env": "CROWDIN_PERSONAL_TOKEN"
"base_path": "."
"base_url": "https://api.crowdin.com"
#
# Choose file structure in Crowdin
# e.g. true or false
#
"preserve_hierarchy": false
#
# Files configuration
#
files: [
{
#
# Source files filter
# e.g. "/resources/en/*.json"
#
"source": "/lib/l10n/app_en.arb",
#
# Where translations will be placed
# e.g. "/resources/%two_letters_code%/%original_file_name%"
#
"translation": "/lib/l10n/app_%two_letters_code%.arb",
#
# Files or directories for ignore
# e.g. ["/**/?.txt", "/**/[0-9].txt", "/**/*\?*.txt"]
#
# "ignore": [],
#
# The dest allows you to specify a file name in Crowdin
# e.g. "/messages.json"
#
# "dest": "",
#
# File type
# e.g. "json"
#
# "type": "",
#
# The parameter "update_option" is optional. If it is not set, after the files update the translations for changed strings will be removed. Use to fix typos and for minor changes in the source strings
# e.g. "update_as_unapproved" or "update_without_changes"
#
# "update_option": "",
#
# Start block (for XML only)
#
#
# Defines whether to translate tags attributes.
# e.g. 0 or 1 (Default is 1)
#
# "translate_attributes": 1,
#
# Defines whether to translate texts placed inside the tags.
# e.g. 0 or 1 (Default is 1)
#
# "translate_content": 1,
#
# This is an array of strings, where each item is the XPaths to DOM element that should be imported
# e.g. ["/content/text", "/content/text[@value]"]
#
# "translatable_elements": [],
#
# Defines whether to split long texts into smaller text segments
# e.g. 0 or 1 (Default is 1)
#
# "content_segmentation": 1,
#
# End block (for XML only)
#
#
# Start .properties block
#
#
# Defines whether single quote should be escaped by another single quote or backslash in exported translations
# e.g. 0 or 1 or 2 or 3 (Default is 3)
# 0 - do not escape single quote;
# 1 - escape single quote by another single quote;
# 2 - escape single quote by backslash;
# 3 - escape single quote by another single quote only in strings containing variables ( {0} ).
#
# "escape_quotes": 3,
#
# Defines whether any special characters (=, :, ! and #) should be escaped by backslash in exported translations.
# e.g. 0 or 1 (Default is 0)
# 0 - do not escape special characters
# 1 - escape special characters by a backslash
#
# "escape_special_characters": 0
#
#
# End .properties block
#
#
# Does the first line contain header?
# e.g. true or false
#
# "first_line_contains_header": true,
#
# for spreadsheets
# e.g. "identifier,source_phrase,context,uk,ru,fr"
#
# "scheme": "",
}
]

View File

@ -92,9 +92,9 @@ void main() {
await tester.tap(helpDrawerButton);
await tester.longWait();
if (isAndroid) {
expect(find.byKey(feedbackButton).hitTestable(), findsOneWidget);
expect(find.byKey(userGuideButton).hitTestable(), findsOneWidget);
} else {
await tester.tap(find.byKey(feedbackButton).hitTestable());
await tester.tap(find.byKey(userGuideButton).hitTestable());
await tester.longWait();
}
});

View File

@ -118,25 +118,21 @@ class AboutPage extends ConsumerWidget {
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: launchDocumentationUrl,
child: Text(
key: feedbackButton,
l10n.s_send_feedback,
key: userGuideButton,
l10n.s_user_guide,
style:
const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchFeedbackUrl();
},
),
TextButton(
onPressed: launchHelpUrl,
child: Text(
l10n.s_i_need_help,
style:
const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchHelpUrl();
},
),
],
),

View File

@ -16,15 +16,9 @@
import 'package:url_launcher/url_launcher.dart';
import '../core/state.dart';
void launchDocumentationUrl() => _launchUrl('https://yubi.co/ya-documentation');
void launchFeedbackUrl() => _launchUrl(isAndroid
? 'https://yubi.co/ya-feedback-android'
: 'https://yubi.co/ya-feedback-desktop');
void launchHelpUrl() => _launchUrl(isAndroid
? 'https://yubi.co/ya-help-android'
: 'https://yubi.co/ya-help-desktop');
void launchHelpUrl() => _launchUrl('https://yubi.co/ya-support');
void launchTermsUrl() => _launchUrl('https://yubi.co/terms');

View File

@ -36,6 +36,8 @@ final _log = Logger('app.state');
// Officially supported translations
const officialLocales = [
Locale('en', ''),
Locale('fr', ''),
Locale('ja', ''),
];
extension on Section {

View File

@ -62,7 +62,7 @@ Key themeModeOption(ThemeMode mode) => Key('$_prefix.theme_mode.${mode.name}');
const tosButton = Key('$_prefix.tos_button');
const privacyButton = Key('$_prefix.privacy_button');
const licensesButton = Key('$_prefix.licenses_button');
const feedbackButton = Key('$_prefix.feedback_button');
const userGuideButton = Key('$_prefix.user_guide_button');
const helpButton = Key('$_prefix.help_button');
const diagnosticsChip = Key('$_prefix.diagnostics_chip');
const logChip = Key('$_prefix.log_chip');

View File

@ -76,7 +76,7 @@
"l_help_and_about_desc": null,
"s_help_and_feedback": "Hilfe und Feedback",
"s_home": null,
"s_send_feedback": "Senden Sie uns Feedback",
"s_user_guide": null,
"s_i_need_help": "Ich brauche Hilfe",
"s_troubleshooting": "Problembehebung",
"s_terms_of_use": "Nutzungsbedingungen",
@ -189,7 +189,6 @@
"app": {}
}
},
"l_app_not_supported_desc": "Diese Anwendung wird nicht unterstützt",
"s_app_disabled": "Anwendung deaktiviert",
"l_app_disabled_desc": "Aktivieren Sie die Anwendung '{app}' auf Ihrem YubiKey für Zugriff",
"@l_app_disabled_desc": {
@ -248,12 +247,6 @@
"s_unblock_pin": null,
"l_pin_mismatch": null,
"l_puk_mismatch": null,
"l_new_pin_len": "Neue PIN muss mindestens {length} Zeichen lang sein",
"@l_new_pin_len": {
"placeholders": {
"length": {}
}
},
"s_pin_set": "PIN gesetzt",
"s_puk_set": null,
"l_set_pin_failed": "PIN konnte nicht gesetzt werden: {message}",
@ -262,18 +255,6 @@
"message": {}
}
},
"l_set_puk_failed": null,
"@l_set_puk_failed": {
"placeholders": {
"message": {}
}
},
"l_unblock_pin_failed": null,
"@l_unblock_pin_failed": {
"placeholders": {
"message": {}
}
},
"l_attempts_remaining": null,
"@l_attempts_remaining": {
"placeholders": {
@ -294,7 +275,6 @@
},
"s_fido_pin_protection": "FIDO PIN Schutz",
"s_pin_change_required": null,
"l_fido_pin_protection_optional": "Optionaler FIDO PIN Schutz",
"l_enter_fido2_pin": "Geben Sie die FIDO2 PIN für Ihren YubiKey ein",
"l_pin_blocked_reset": "PIN ist blockiert; setzen Sie die FIDO Anwendung auf Werkseinstellung zurück",
"l_pin_blocked": null,
@ -591,7 +571,6 @@
"slot": {}
}
},
"l_generating_private_key": null,
"s_private_key_generated": null,
"p_select_what_to_delete": null,
"p_warning_delete_certificate": null,
@ -739,12 +718,6 @@
"l_append_enter_desc": null,
"@_otp_errors": {},
"p_otp_slot_configuration_error": null,
"@p_otp_slot_configuration_error": {
"placeholders": {
"slot": {}
}
},
"p_otp_swap_error": null,
"l_wrong_access_code": null,
@ -907,10 +880,8 @@
"p_ndef_set_clip_failure": null,
"@_key_customization": {},
"s_customize_key_action": null,
"s_set_label": null,
"s_change_label": null,
"s_theme_color": null,
"s_color": null,
"p_set_will_add_custom_name": null,
"p_rename_will_change_custom_name": null,

View File

@ -76,7 +76,7 @@
"l_help_and_about_desc": "Troubleshoot and support",
"s_help_and_feedback": "Help and feedback",
"s_home": "Home",
"s_send_feedback": "Send us feedback",
"s_user_guide": "User guide",
"s_i_need_help": "I need help",
"s_troubleshooting": "Troubleshooting",
"s_terms_of_use": "Terms of use",
@ -189,7 +189,6 @@
"app": {}
}
},
"l_app_not_supported_desc": "This application is not supported",
"s_app_disabled": "Application disabled",
"l_app_disabled_desc": "Enable the '{app}' application on your YubiKey to access",
"@l_app_disabled_desc": {
@ -248,12 +247,6 @@
"s_unblock_pin": "Unblock PIN",
"l_pin_mismatch": "PINs do not match",
"l_puk_mismatch": "PUKs do not match",
"l_new_pin_len": "New PIN must be at least {length} characters",
"@l_new_pin_len": {
"placeholders": {
"length": {}
}
},
"s_pin_set": "PIN set",
"s_puk_set": "PUK set",
"l_set_pin_failed": "Failed to set PIN: {message}",
@ -262,18 +255,6 @@
"message": {}
}
},
"l_set_puk_failed": "Failed to set PUK: {message}",
"@l_set_puk_failed": {
"placeholders": {
"message": {}
}
},
"l_unblock_pin_failed": "Failed to unblock PIN: {message}",
"@l_unblock_pin_failed": {
"placeholders": {
"message": {}
}
},
"l_attempts_remaining": "{retries} attempt(s) remaining",
"@l_attempts_remaining": {
"placeholders": {
@ -294,7 +275,6 @@
},
"s_fido_pin_protection": "FIDO PIN protection",
"s_pin_change_required": "PIN change required",
"l_fido_pin_protection_optional": "Optional FIDO PIN protection",
"l_enter_fido2_pin": "Enter the FIDO2 PIN for your YubiKey",
"l_pin_blocked_reset": "PIN is blocked; factory reset the FIDO application",
"l_pin_blocked": "PIN is blocked",
@ -591,7 +571,6 @@
"slot": {}
}
},
"l_generating_private_key": "Generating private key\u2026",
"s_private_key_generated": "Private key generated",
"p_select_what_to_delete": "Select what to delete from the slot.",
"p_warning_delete_certificate": "Warning! This action will delete the certificate from your YubiKey.",
@ -739,12 +718,6 @@
"l_append_enter_desc": "Append an Enter keystroke after emitting the OTP",
"@_otp_errors": {},
"p_otp_slot_configuration_error": "Failed to modify {slot}! Make sure the YubiKey does not have restrictive access.",
"@p_otp_slot_configuration_error": {
"placeholders": {
"slot": {}
}
},
"p_otp_swap_error": "Failed to swap slots! Make sure the YubiKey does not have restrictive access.",
"l_wrong_access_code": "Wrong access code",
@ -907,10 +880,8 @@
"p_ndef_set_clip_failure": "Failed to access clipboard when trying to copy OTP code from YubiKey.",
"@_key_customization": {},
"s_customize_key_action": "Set label/color",
"s_set_label": "Set label",
"s_change_label": "Change label",
"s_theme_color": "Theme color",
"s_color": "Color",
"p_set_will_add_custom_name": "This will give your YubiKey a custom name.",
"p_rename_will_change_custom_name": "This will change the label of your YubiKey.",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -76,7 +76,7 @@
"l_help_and_about_desc": null,
"s_help_and_feedback": "Pomoc i opinie",
"s_home": null,
"s_send_feedback": "Prześlij opinię",
"s_user_guide": null,
"s_i_need_help": "Pomoc",
"s_troubleshooting": "Rozwiązywanie problemów",
"s_terms_of_use": "Warunki użytkowania",
@ -189,7 +189,6 @@
"app": {}
}
},
"l_app_not_supported_desc": "Ta funkcja nie jest obsługiwana",
"s_app_disabled": "Wyłączona funkcja",
"l_app_disabled_desc": "Włącz funkcję '{app}' w kluczu YubiKey, aby uzyskać dostęp",
"@l_app_disabled_desc": {
@ -248,12 +247,6 @@
"s_unblock_pin": "Odblokuj PIN",
"l_pin_mismatch": null,
"l_puk_mismatch": null,
"l_new_pin_len": "Nowy PIN musi mieć co najmniej {length} znaków",
"@l_new_pin_len": {
"placeholders": {
"length": {}
}
},
"s_pin_set": "PIN ustawiony",
"s_puk_set": "PUK ustawiony",
"l_set_pin_failed": "Nie udało się ustawić kodu PIN: {message}",
@ -262,18 +255,6 @@
"message": {}
}
},
"l_set_puk_failed": null,
"@l_set_puk_failed": {
"placeholders": {
"message": {}
}
},
"l_unblock_pin_failed": null,
"@l_unblock_pin_failed": {
"placeholders": {
"message": {}
}
},
"l_attempts_remaining": "Pozostało prób: {retries}",
"@l_attempts_remaining": {
"placeholders": {
@ -294,7 +275,6 @@
},
"s_fido_pin_protection": "Ochrona FIDO kodem PIN",
"s_pin_change_required": "Wymagana zmiana PINu",
"l_fido_pin_protection_optional": "Opcjonalna ochrona FIDO kodem PIN",
"l_enter_fido2_pin": "Wprowadź kod PIN FIDO2 klucza YubiKey",
"l_pin_blocked_reset": "PIN jest zablokowany; przywróć ustawienia fabryczne funkcji FIDO",
"l_pin_blocked": null,
@ -591,7 +571,6 @@
"slot": {}
}
},
"l_generating_private_key": "Generowanie prywatnego klucza\u2026",
"s_private_key_generated": "Wygenerowano klucz prywatny",
"p_select_what_to_delete": null,
"p_warning_delete_certificate": "Uwaga! Ta czynność spowoduje usunięcie certyfikatu z klucza YubiKey.",
@ -739,12 +718,6 @@
"l_append_enter_desc": "Dołącz naciśnięcie klawisza Enter po wysłaniu OTP",
"@_otp_errors": {},
"p_otp_slot_configuration_error": "Nie udało się zmodyfikować {slot}! Upewnij się, że klucz YubiKey nie ma ograniczonego dostępu.",
"@p_otp_slot_configuration_error": {
"placeholders": {
"slot": {}
}
},
"p_otp_swap_error": null,
"l_wrong_access_code": null,
@ -907,10 +880,8 @@
"p_ndef_set_clip_failure": "Błąd kopiowania OTP do schowka.",
"@_key_customization": {},
"s_customize_key_action": "Dostosuj klucz",
"s_set_label": null,
"s_change_label": null,
"s_theme_color": "Kolor motywu",
"s_color": null,
"p_set_will_add_custom_name": null,
"p_rename_will_change_custom_name": null,

View File

@ -22,6 +22,18 @@ import xml.etree.ElementTree as ET
from os import path as p
escape_chars = str.maketrans(
{
"@": r"\@",
"?": r"\?",
"\n": r"\n",
"\t": r"\t",
"'": r"\'",
'"': r"\"",
}
)
def read_arb_file(file_path):
"""Load translations from flutter ARB file."""
with open(file_path, "r", encoding="utf-8") as file:
@ -67,10 +79,11 @@ def process_android_res(lang, arb, keys_to_translate):
# only add the string if translation exists in arb
if key in arb.keys() and arb[key] is not None:
existing = res.find(f"./string[@name='{key}']")
escaped_val = arb[key].translate(escape_chars)
if existing is not None:
existing.text = arb[key]
existing.text = escaped_val
else:
ET.SubElement(res, "string", name=f"{key}").text = arb[key]
ET.SubElement(res, "string", name=f"{key}").text = escaped_val
tree = ET.ElementTree(res)
ET.indent(tree, " ")
tree.write(res_path, encoding="utf-8", xml_declaration=True)