From 3585e20354700755250d3fdaaf91aafa27e9decc Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Tue, 5 Aug 2025 15:15:42 +0200 Subject: [PATCH] Add missing translations for Android biometrics and general vault unlock flow (#1085) --- .../AndroidKeystoreProvider.kt | 11 +++++----- .../app/src/main/res/values/strings.xml | 8 +++++++- .../app/(tabs)/settings/vault-unlock.tsx | 6 +++--- apps/mobile-app/context/AuthContext.tsx | 20 ++++++++++--------- apps/mobile-app/i18n/locales/en.json | 4 ++++ 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/keystoreprovider/AndroidKeystoreProvider.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/keystoreprovider/AndroidKeystoreProvider.kt index 27fab186d..7af643473 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/keystoreprovider/AndroidKeystoreProvider.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/keystoreprovider/AndroidKeystoreProvider.kt @@ -11,6 +11,7 @@ import android.util.Log import androidx.biometric.BiometricManager import androidx.biometric.BiometricPrompt import androidx.fragment.app.FragmentActivity +import net.aliasvault.app.R import java.io.File import java.nio.ByteBuffer import java.security.KeyStore @@ -117,10 +118,8 @@ class AndroidKeystoreProvider( // Create BiometricPrompt val promptInfo = BiometricPrompt.PromptInfo.Builder() - .setTitle("Store Encryption Key") - .setSubtitle( - "Authenticate to securely store your encryption key in the Android Keystore. This enables secure access to your vault.", - ) + .setTitle(context.getString(R.string.biometric_store_key_title)) + .setSubtitle(context.getString(R.string.biometric_store_key_subtitle)) .setAllowedAuthenticators( BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL, @@ -233,8 +232,8 @@ class AndroidKeystoreProvider( // Create BiometricPrompt val promptInfo = BiometricPrompt.PromptInfo.Builder() - .setTitle("Unlock Vault") - .setSubtitle("Authenticate to access your vault") + .setTitle(context.getString(R.string.biometric_unlock_vault_title)) + .setSubtitle(context.getString(R.string.biometric_unlock_vault_subtitle)) .setAllowedAuthenticators( BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL, diff --git a/apps/mobile-app/android/app/src/main/res/values/strings.xml b/apps/mobile-app/android/app/src/main/res/values/strings.xml index 4d3963f3c..ec01f2f97 100644 --- a/apps/mobile-app/android/app/src/main/res/values/strings.xml +++ b/apps/mobile-app/android/app/src/main/res/values/strings.xml @@ -5,10 +5,16 @@ false AliasVault AutoFill AliasVault icon - + Failed to retrieve, open app No match found, create new? Open app Vault locked + + + Store Encryption Key + Authenticate to securely store your encryption key in the Android Keystore. This enables secure access to your vault. + Unlock Vault + Authenticate to access your vault \ No newline at end of file diff --git a/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx b/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx index b287c9d5e..fe65f2c3c 100644 --- a/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx +++ b/apps/mobile-app/app/(tabs)/settings/vault-unlock.tsx @@ -21,7 +21,7 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode { const { setAuthMethods, getEnabledAuthMethods, getBiometricDisplayName } = useAuth(); const [hasBiometrics, setHasBiometrics] = useState(false); const [isBiometricsEnabled, setIsBiometricsEnabled] = useState(false); - const [biometricDisplayName, setBiometricDisplayName] = useState(Platform.OS === 'ios' ? 'Face ID / Touch ID' : 'Biometrics'); + const [biometricDisplayName, setBiometricDisplayName] = useState(Platform.OS === 'ios' ? t('settings.vaultUnlockSettings.faceIdTouchId') : t('settings.vaultUnlockSettings.biometrics')); const [_, setEnabledAuthMethods] = useState([]); useEffect(() => { @@ -41,7 +41,7 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode { setHasBiometrics(isBiometricAvailable); // Get appropriate display name - const displayName = Platform.OS === 'ios' ? await getBiometricDisplayName() : 'Biometrics'; + const displayName = Platform.OS === 'ios' ? await getBiometricDisplayName() : t('settings.vaultUnlockSettings.biometrics'); setBiometricDisplayName(displayName); const methods = await getEnabledAuthMethods(); @@ -60,7 +60,7 @@ export default function VaultUnlockSettingsScreen() : React.ReactNode { }; initializeAuth(); - }, [getEnabledAuthMethods, getBiometricDisplayName]); + }, [getEnabledAuthMethods, getBiometricDisplayName, t]); useEffect(() => { if (!initialized) { diff --git a/apps/mobile-app/context/AuthContext.tsx b/apps/mobile-app/context/AuthContext.tsx index 50f97ffd1..77637d5dc 100644 --- a/apps/mobile-app/context/AuthContext.tsx +++ b/apps/mobile-app/context/AuthContext.tsx @@ -3,6 +3,7 @@ import { NavigationContainerRef, ParamListBase } from '@react-navigation/native' import * as LocalAuthentication from 'expo-local-authentication'; import { router, useGlobalSearchParams, usePathname } from 'expo-router'; import React, { createContext, useContext, useState, useEffect, useMemo, useCallback, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; import { AppState, Platform } from 'react-native'; import EncryptionUtility from '@/utils/EncryptionUtility'; @@ -55,6 +56,7 @@ const AuthContext = createContext(undefined); * AuthProvider to provide the authentication state to the app that components can use. */ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const { t } = useTranslation(); const [isLoggedIn, setIsLoggedIn] = useState(false); const [isInitialized, setIsInitialized] = useState(false); const [username, setUsername] = useState(null); @@ -208,12 +210,12 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children // For Android, we use the term "Biometrics" for facial recognition and fingerprint. if (Platform.OS === 'android') { - return 'Biometrics'; + return t('settings.vaultUnlockSettings.biometrics'); } // For iOS, we check if the device has explicit Face ID or Touch ID support. if (!hasBiometrics || !enrolled) { - return 'Face ID / Touch ID'; + return t('settings.vaultUnlockSettings.faceIdTouchId'); } const types = await LocalAuthentication.supportedAuthenticationTypesAsync(); @@ -221,17 +223,17 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children const hasTouchIDSupport = types.includes(LocalAuthentication.AuthenticationType.FINGERPRINT); if (hasFaceIDSupport) { - return 'Face ID'; + return t('settings.vaultUnlockSettings.faceId'); } else if (hasTouchIDSupport) { - return 'Touch ID'; + return t('settings.vaultUnlockSettings.touchId'); } - return 'Face ID / Touch ID'; + return t('settings.vaultUnlockSettings.faceIdTouchId'); } catch (error) { console.error('Failed to get biometric display name:', error); - return 'Face ID / Touch ID'; + return t('settings.vaultUnlockSettings.faceIdTouchId'); } - }, []); + }, [t]); /** * Get the display label for the current auth method @@ -248,8 +250,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children console.error('Failed to check Face ID enrollment:', error); } } - return 'Password'; - }, [getEnabledAuthMethods, getBiometricDisplayName, isBiometricsEnabledOnDevice]); + return t('credentials.password'); + }, [getEnabledAuthMethods, getBiometricDisplayName, isBiometricsEnabledOnDevice, t]); /** * Get the auto-lock timeout from the iOS credentials manager diff --git a/apps/mobile-app/i18n/locales/en.json b/apps/mobile-app/i18n/locales/en.json index aefe1e6d1..45f9327b6 100644 --- a/apps/mobile-app/i18n/locales/en.json +++ b/apps/mobile-app/i18n/locales/en.json @@ -205,6 +205,10 @@ "openSettings": "Open Settings", "vaultUnlockSettings": { "description": "Choose how you want to unlock your vault.", + "biometrics": "Biometrics", + "faceId": "Face ID", + "touchId": "Touch ID", + "faceIdTouchId": "Face ID / Touch ID", "biometricEnabled": "{{biometric}} is now successfully enabled", "biometricNotAvailable": "{{biometric}} Not Available", "biometricDisabledMessage": "{{biometric}} is disabled for AliasVault. In order to use it, please enable it in your device settings first.",