Add missing translations for Android biometrics and general vault unlock flow (#1085)

This commit is contained in:
Leendert de Borst
2025-08-05 15:15:42 +02:00
committed by Leendert de Borst
parent c926933804
commit 3585e20354
5 changed files with 30 additions and 19 deletions

View File

@@ -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,

View File

@@ -5,10 +5,16 @@
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
<string name="autofill_service_description" translatable="true">AliasVault AutoFill</string>
<string name="aliasvault_icon">AliasVault icon</string>
<!-- AutofillService strings -->
<string name="autofill_failed_to_retrieve">Failed to retrieve, open app</string>
<string name="autofill_no_match_found">No match found, create new?</string>
<string name="autofill_open_app">Open app</string>
<string name="autofill_vault_locked">Vault locked</string>
<!-- Biometric prompts -->
<string name="biometric_store_key_title">Store Encryption Key</string>
<string name="biometric_store_key_subtitle">Authenticate to securely store your encryption key in the Android Keystore. This enables secure access to your vault.</string>
<string name="biometric_unlock_vault_title">Unlock Vault</string>
<string name="biometric_unlock_vault_subtitle">Authenticate to access your vault</string>
</resources>

View File

@@ -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<AuthMethod[]>([]);
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) {

View File

@@ -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<AuthContextType | undefined>(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<string | null>(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

View File

@@ -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.",