mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 18:22:39 +03:00
Merge PR #1291.
This commit is contained in:
commit
b63fb5ba9e
1
.github/workflows/linux.yml
vendored
1
.github/workflows/linux.yml
vendored
@ -30,6 +30,7 @@ jobs:
|
||||
apt-get install -qq git python$PYVER_MINOR-dev python$PYVER_MINOR-venv
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
ln -s `which python$PYVER_MINOR` /usr/local/bin/python
|
||||
ln -s `which python$PYVER_MINOR` /usr/local/bin/python3
|
||||
PYVER_TEMP=`/usr/local/bin/python --version`
|
||||
export PYVERINST=${PYVER_TEMP#* }
|
||||
echo "PYVERINST=$PYVERINST" >> $GITHUB_ENV
|
||||
|
@ -16,6 +16,12 @@ repos:
|
||||
language: script
|
||||
entry: arb_reformatter.py
|
||||
require_serial: true
|
||||
- id: update-android-strings
|
||||
name: update-android-strings
|
||||
files: \.arb$
|
||||
language: script
|
||||
entry: update_android_strings.py
|
||||
require_serial: true
|
||||
|
||||
# Python
|
||||
- repo: local
|
||||
|
@ -33,6 +33,9 @@ class AppPreferences(context: Context) {
|
||||
|
||||
const val PREF_CLIP_KBD_LAYOUT = "flutter.prefClipKbdLayout"
|
||||
const val DEFAULT_CLIP_KBD_LAYOUT = "US"
|
||||
|
||||
const val PREF_ENABLE_COMMUNITY_TRANSLATIONS =
|
||||
"flutter.APP_STATE_ENABLE_COMMUNITY_TRANSLATIONS"
|
||||
}
|
||||
|
||||
private val logger = LoggerFactory.getLogger(AppPreferences::class.java)
|
||||
@ -66,6 +69,9 @@ class AppPreferences(context: Context) {
|
||||
val openAppOnUsb: Boolean
|
||||
get() = prefs.getBoolean(PREF_USB_OPEN_APP, false)
|
||||
|
||||
val communityTranslationsEnabled: Boolean
|
||||
get() = prefs.getBoolean(PREF_ENABLE_COMMUNITY_TRANSLATIONS, false)
|
||||
|
||||
fun registerListener(listener: OnSharedPreferenceChangeListener) {
|
||||
logger.debug("registering change listener")
|
||||
prefs.registerOnSharedPreferenceChangeListener(listener)
|
||||
|
@ -24,13 +24,12 @@ import android.nfc.Tag
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
|
||||
import com.yubico.authenticator.ndef.KeyboardLayout
|
||||
import com.yubico.yubikit.core.util.NdefUtils
|
||||
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
typealias ResourceId = Int
|
||||
|
||||
@ -66,8 +65,8 @@ class NdefActivity : Activity() {
|
||||
compatUtil.until(Build.VERSION_CODES.TIRAMISU) {
|
||||
showToast(
|
||||
when (otpSlotContent.type) {
|
||||
OtpType.Otp -> R.string.otp_success_set_otp_to_clipboard
|
||||
OtpType.Password -> R.string.otp_success_set_password_to_clipboard
|
||||
OtpType.Otp -> R.string.p_ndef_set_otp
|
||||
OtpType.Password -> R.string.p_ndef_set_password
|
||||
}, Toast.LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
@ -77,16 +76,19 @@ class NdefActivity : Activity() {
|
||||
illegalArgumentException.message ?: "Failure when handling YubiKey OTP",
|
||||
illegalArgumentException
|
||||
)
|
||||
showToast(R.string.otp_parse_failure, Toast.LENGTH_LONG)
|
||||
showToast(R.string.p_ndef_parse_failure, Toast.LENGTH_LONG)
|
||||
} catch (_: UnsupportedOperationException) {
|
||||
showToast(R.string.otp_set_clip_failure, Toast.LENGTH_LONG)
|
||||
showToast(R.string.p_ndef_set_clip_failure, Toast.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
|
||||
if (appPreferences.openAppOnNfcTap) {
|
||||
val mainAppIntent = Intent(this, MainActivity::class.java).apply {
|
||||
// Pass the NFC Tag to the main Activity.
|
||||
putExtra(NfcAdapter.EXTRA_TAG, intent.parcelableExtra<Tag>(NfcAdapter.EXTRA_TAG))
|
||||
putExtra(
|
||||
NfcAdapter.EXTRA_TAG,
|
||||
intent.parcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
|
||||
)
|
||||
}
|
||||
startActivity(mainAppIntent)
|
||||
}
|
||||
@ -96,7 +98,15 @@ class NdefActivity : Activity() {
|
||||
}
|
||||
|
||||
private fun showToast(value: ResourceId, length: Int) {
|
||||
Toast.makeText(this, value, length).show()
|
||||
val context = if (appPreferences.communityTranslationsEnabled)
|
||||
this
|
||||
else {
|
||||
// always use 'us' locale
|
||||
val configuration = resources.configuration
|
||||
configuration.setLocale(Locale.US)
|
||||
createConfigurationContext(configuration)
|
||||
}
|
||||
Toast.makeText(context, value, length).show()
|
||||
}
|
||||
|
||||
private fun parseOtpFromIntent(): OtpSlotValue {
|
||||
|
2
android/app/src/main/res/values-de/strings.xml
Normal file
2
android/app/src/main/res/values-de/strings.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources />
|
2
android/app/src/main/res/values-fr/strings.xml
Normal file
2
android/app/src/main/res/values-fr/strings.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources />
|
2
android/app/src/main/res/values-ja/strings.xml
Normal file
2
android/app/src/main/res/values-ja/strings.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources />
|
7
android/app/src/main/res/values-pl/strings.xml
Normal file
7
android/app/src/main/res/values-pl/strings.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<string name="p_ndef_set_otp">OTP zostało skopiowane do schowka.</string>
|
||||
<string name="p_ndef_set_password">Hasło statyczne zostało skopiowane do schowka.</string>
|
||||
<string name="p_ndef_parse_failure">Błąd czytania OTP z YubiKey.</string>
|
||||
<string name="p_ndef_set_clip_failure">Błąd kopiowania OTP do schowka.</string>
|
||||
</resources>
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<string name="app_label">Yubico Authenticator</string>
|
||||
<string name="otp_success_set_otp_to_clipboard">Successfully copied OTP code from YubiKey to clipboard.</string>
|
||||
<string name="otp_success_set_password_to_clipboard">Successfully copied password from YubiKey to clipboard.</string>
|
||||
<string name="otp_parse_failure">Failed to parse OTP code from YubiKey.</string>
|
||||
<string name="otp_set_clip_failure">Failed to access clipboard when trying to copy OTP code from YubiKey.</string>
|
||||
<string name="app_label" translatable="false">Yubico Authenticator</string>
|
||||
<string name="p_ndef_set_otp">Successfully copied OTP code from YubiKey to clipboard.</string>
|
||||
<string name="p_ndef_set_password">Successfully copied password from YubiKey to clipboard.</string>
|
||||
<string name="p_ndef_parse_failure">Failed to parse OTP code from YubiKey.</string>
|
||||
<string name="p_ndef_set_clip_failure">Failed to access clipboard when trying to copy OTP code from YubiKey.</string>
|
||||
</resources>
|
@ -626,5 +626,11 @@
|
||||
"s_nfc_dialog_oath_failure": null,
|
||||
"s_nfc_dialog_oath_add_multiple_accounts": null,
|
||||
|
||||
"@_ndef": {},
|
||||
"p_ndef_set_otp": null,
|
||||
"p_ndef_set_password": null,
|
||||
"p_ndef_parse_failure": null,
|
||||
"p_ndef_set_clip_failure": null,
|
||||
|
||||
"@_eof": {}
|
||||
}
|
||||
|
@ -626,5 +626,11 @@
|
||||
"s_nfc_dialog_oath_failure": "OATH operation failed",
|
||||
"s_nfc_dialog_oath_add_multiple_accounts": "Action: add multiple accounts",
|
||||
|
||||
"@_ndef": {},
|
||||
"p_ndef_set_otp": "Successfully copied OTP code from YubiKey to clipboard.",
|
||||
"p_ndef_set_password": "Successfully copied password from YubiKey to clipboard.",
|
||||
"p_ndef_parse_failure": "Failed to parse OTP code from YubiKey.",
|
||||
"p_ndef_set_clip_failure": "Failed to access clipboard when trying to copy OTP code from YubiKey.",
|
||||
|
||||
"@_eof": {}
|
||||
}
|
||||
|
@ -626,5 +626,11 @@
|
||||
"s_nfc_dialog_oath_failure": "Échec de l'opération OATH",
|
||||
"s_nfc_dialog_oath_add_multiple_accounts": "Action: ajouter plusieurs comptes",
|
||||
|
||||
"@_ndef": {},
|
||||
"p_ndef_set_otp": null,
|
||||
"p_ndef_set_password": null,
|
||||
"p_ndef_parse_failure": null,
|
||||
"p_ndef_set_clip_failure": null,
|
||||
|
||||
"@_eof": {}
|
||||
}
|
||||
|
@ -626,5 +626,11 @@
|
||||
"s_nfc_dialog_oath_failure": "OATH操作は失敗しました",
|
||||
"s_nfc_dialog_oath_add_multiple_accounts": "操作:複数アカウントの追加",
|
||||
|
||||
"@_ndef": {},
|
||||
"p_ndef_set_otp": null,
|
||||
"p_ndef_set_password": null,
|
||||
"p_ndef_parse_failure": null,
|
||||
"p_ndef_set_clip_failure": null,
|
||||
|
||||
"@_eof": {}
|
||||
}
|
||||
|
@ -626,5 +626,11 @@
|
||||
"s_nfc_dialog_oath_failure": "Operacja OATH nie powiodła się",
|
||||
"s_nfc_dialog_oath_add_multiple_accounts": "Działanie: dodawanie wielu kont",
|
||||
|
||||
"@_ndef": {},
|
||||
"p_ndef_set_otp": "OTP zostało skopiowane do schowka.",
|
||||
"p_ndef_set_password": "Hasło statyczne zostało skopiowane do schowka.",
|
||||
"p_ndef_parse_failure": "Błąd czytania OTP z YubiKey.",
|
||||
"p_ndef_set_clip_failure": "Błąd kopiowania OTP do schowka.",
|
||||
|
||||
"@_eof": {}
|
||||
}
|
||||
|
102
update_android_strings.py
Executable file
102
update_android_strings.py
Executable file
@ -0,0 +1,102 @@
|
||||
#!/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.
|
||||
|
||||
"""Rebuild Android String resources from ARB files."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
from os import path as p
|
||||
|
||||
|
||||
def read_arb_file(file_path):
|
||||
"""Load translations from flutter ARB file."""
|
||||
with open(file_path, "r", encoding="utf-8") as file:
|
||||
return json.load(file)
|
||||
|
||||
|
||||
def get_lang_file_dir(lang):
|
||||
"""Return path of Android resource directory for lang."""
|
||||
return (
|
||||
f"android/app/src/main/res/values-{lang}"
|
||||
if lang != "en"
|
||||
else "android/app/src/main/res/values"
|
||||
)
|
||||
|
||||
|
||||
def get_lang_file(lang):
|
||||
"""Return path of Android string resource file for lang."""
|
||||
return p.join(get_lang_file_dir(lang), "strings.xml")
|
||||
|
||||
|
||||
def process_android_res(lang, arb, keys_to_translate):
|
||||
"""Generate or update Android string resource for lang.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lang : str
|
||||
language code
|
||||
arb : dict
|
||||
content of flutter ARB file
|
||||
keys_to_translate : list
|
||||
string resources which will be generated or updated
|
||||
"""
|
||||
res_dir = get_lang_file_dir(lang)
|
||||
if not p.exists(res_dir):
|
||||
os.makedirs(res_dir)
|
||||
|
||||
res_path = get_lang_file(lang)
|
||||
|
||||
res = (
|
||||
ET.parse(res_path).getroot() if p.exists(res_path) else ET.Element("resources")
|
||||
)
|
||||
for key in 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}']")
|
||||
if existing is not None:
|
||||
existing.text = arb[key]
|
||||
else:
|
||||
ET.SubElement(res, "string", name=f"{key}").text = arb[key]
|
||||
tree = ET.ElementTree(res)
|
||||
ET.indent(tree, " ")
|
||||
tree.write(res_path, encoding="utf-8", xml_declaration=True)
|
||||
return True
|
||||
|
||||
|
||||
def get_english_strings():
|
||||
"""Extract translatable strings from English Android string resource."""
|
||||
strings_en = "android/app/src/main/res/values/strings.xml"
|
||||
resources_en = ET.parse(strings_en).getroot()
|
||||
|
||||
return [
|
||||
key.attrib.get("name")
|
||||
for key in resources_en
|
||||
if key.attrib.get("translatable") in [None, True]
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arb_files = "lib/l10n"
|
||||
english_strings = get_english_strings()
|
||||
|
||||
for arb_file in os.listdir(arb_files):
|
||||
if arb_file.startswith("app_") and arb_file.endswith(".arb"):
|
||||
lang = arb_file.split("_")[1].split(".")[0]
|
||||
arb_path = p.join(arb_files, arb_file)
|
||||
arb = read_arb_file(arb_path)
|
||||
if process_android_res(lang, arb, english_strings):
|
||||
print(f"Processed: {get_lang_file(lang)}")
|
Loading…
Reference in New Issue
Block a user