From 57643d1b2496994554245368efc92e024a4ee8e1 Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Fri, 11 Sep 2026 15:36:00 +0100 Subject: [PATCH 1/8] fix(auth): localize user-facing auth errors by Firebase error code --- .../com/firebase/ui/auth/AuthException.kt | 334 +++++++++++++++- .../string_provider/AuthUIStringProvider.kt | 41 ++ .../DefaultAuthUIStringProvider.kt | 33 ++ .../auth/ui/components/ErrorRecoveryDialog.kt | 32 +- auth/src/main/res/values/strings.xml | 13 + .../AuthExceptionRecoveryResolutionTest.kt | 358 ++++++++++++++++++ .../com/firebase/ui/auth/AuthExceptionTest.kt | 309 ++++++++++++++- .../EmailAuthProviderFirebaseAuthUITest.kt | 173 ++++++++- .../ErrorRecoveryDialogLogicTest.kt | 152 +++++++- ...honeAuthScreenVerificationLifecycleTest.kt | 5 +- 10 files changed, 1401 insertions(+), 49 deletions(-) create mode 100644 auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt diff --git a/auth/src/main/java/com/firebase/ui/auth/AuthException.kt b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt index 2f61822d60..75940cbc23 100644 --- a/auth/src/main/java/com/firebase/ui/auth/AuthException.kt +++ b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt @@ -19,7 +19,9 @@ import com.firebase.ui.auth.AuthException.Companion.from import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider import com.google.firebase.FirebaseException +import com.google.firebase.FirebaseTooManyRequestsException import com.google.firebase.auth.AuthCredential +import com.google.firebase.auth.FirebaseAuthActionCodeException import com.google.firebase.auth.FirebaseAuthException import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException import com.google.firebase.auth.FirebaseAuthInvalidUserException @@ -256,6 +258,27 @@ abstract class AuthException( cause: Throwable? = null ) : AuthException(message, cause) + /** + * The Firebase project or the app is not set up for the operation that was attempted. + * + * Examples are a sign-in provider left disabled in the Firebase console, an unauthorized + * continue-URL domain, a missing SHA-1 certificate hash, and the reCAPTCHA and tenant + * families. None of these are anything the user can act on. + * + * [message] is generic translated copy, safe to render anywhere — the error dialog, an inline + * error on a screen, or a host's own `onSignInFailure`. The raw Firebase SDK diagnostic is + * untranslated but names the actual misconfiguration, so [from] keeps it on [cause] (the + * original [com.google.firebase.auth.FirebaseAuthException]): it stays in the stack trace and + * is reachable as `exception.cause?.message`. + * + * @property message Generic translated copy, safe to display + * @property cause The original Firebase exception, carrying the raw diagnostic for logs + */ + class MisconfigurationException( + message: String, + cause: Throwable? = null + ) : AuthException(message, cause) + /** * An unknown or unhandled error occurred. * @@ -361,11 +384,14 @@ abstract class AuthException( * * **Mapping:** * - [FirebaseException] → [NetworkException] (for network-related errors) - * - [FirebaseAuthInvalidCredentialsException] → [InvalidCredentialsException] + * - [FirebaseTooManyRequestsException] → [TooManyRequestsException] + * - [FirebaseAuthInvalidCredentialsException] → [InvalidCredentialsException], with the + * message selected by `errorCode` * - [FirebaseAuthInvalidUserException] → [UserNotFoundException] * - [FirebaseAuthWeakPasswordException] → [WeakPasswordException] * - [FirebaseAuthUserCollisionException] → [EmailAlreadyInUseException] - * - [FirebaseAuthException] with ERROR_TOO_MANY_REQUESTS → [TooManyRequestsException] + * - [FirebaseAuthActionCodeException] → [InvalidCredentialsException] + * - [FirebaseAuthException] with a developer-setup `errorCode` → [MisconfigurationException] * - [FirebaseAuthMultiFactorException] → [MfaRequiredException] * - Other exceptions → [UnknownException] * @@ -399,8 +425,16 @@ abstract class AuthException( * [stringProvider] so it honours the host's configured strings and locale. * * This is the preferred overload; see the [Context] one above for the exception mapping - * table and an example. A `null` [stringProvider], or one whose resource for a given error - * is blank, falls back to the Firebase SDK's own message. + * table and an example. + * + * Given a non-null [stringProvider], the returned exception's `message` is always + * library-owned, translated copy, so it is safe for any consumer to render directly. Each + * branch resolves in this order: the blank-able hook scoped to the exception type, then + * the string for the specific Firebase `errorCode`, then the corresponding generic + * recovery message, and only then the Firebase SDK's own untranslated message. The + * exception is [MisconfigurationException], which never uses the SDK message at all — the + * raw diagnostic lives on `cause`. A `null` [stringProvider] has nothing to resolve + * against and falls back to the SDK message everywhere except [MisconfigurationException]. * * @param firebaseException The Firebase exception to convert * @param stringProvider Supplies localized message text; pass `config.stringProvider` @@ -431,6 +465,7 @@ abstract class AuthException( } else { WeakPasswordException( message = stringProvider?.errorWeakPasswordGeneric.nonEmpty() + ?: stringProvider?.weakPasswordRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Password is too weak", cause = firebaseException, @@ -440,18 +475,154 @@ abstract class AuthException( } is FirebaseAuthInvalidCredentialsException -> { - InvalidCredentialsException( - message = stringProvider?.errorInvalidCredentials.nonEmpty() - ?: firebaseException.message - ?: "Invalid credentials provided", - cause = firebaseException - ) + // `errorInvalidCredentials` is the blank-able hook for the whole exception + // type, so it stays ahead of the per-code string in every branch. + val typeLevel = stringProvider?.errorInvalidCredentials.nonEmpty() + when (firebaseException.errorCode) { + // Under email enumeration protection the backend merges "wrong password" + // and "no such account" into this one code, so the copy cannot claim the + // password specifically. + "ERROR_INVALID_CREDENTIAL" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorIncorrectEmailOrPassword.nonEmpty() + ?: firebaseException.message + ?: "That email or password isn't correct", + cause = firebaseException + ) + + "ERROR_WRONG_PASSWORD" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.invalidPassword.nonEmpty() + ?: firebaseException.message + ?: "Incorrect password.", + cause = firebaseException + ) + + "ERROR_INVALID_EMAIL" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.invalidEmailAddress.nonEmpty() + ?: firebaseException.message + ?: "That email address isn't correct", + cause = firebaseException + ) + + "ERROR_MISSING_EMAIL" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.missingEmailAddress.nonEmpty() + ?: firebaseException.message + ?: "Enter your email address to continue", + cause = firebaseException + ) + + "ERROR_MISSING_PASSWORD", + "ERROR_MISSING_VERIFICATION_CODE" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.requiredField.nonEmpty() + ?: firebaseException.message + ?: "You can't leave this empty.", + cause = firebaseException + ) + + "ERROR_INVALID_PHONE_NUMBER" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.invalidPhoneNumber.nonEmpty() + ?: firebaseException.message + ?: "Enter a valid phone number", + cause = firebaseException + ) + + "ERROR_MISSING_PHONE_NUMBER" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.missingPhoneNumber.nonEmpty() + ?: firebaseException.message + ?: "You can't leave this empty.", + cause = firebaseException + ) + + "ERROR_INVALID_VERIFICATION_CODE" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.invalidVerificationCode.nonEmpty() + ?: firebaseException.message + ?: "Wrong code. Try again.", + cause = firebaseException + ) + + "ERROR_SESSION_EXPIRED" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorSessionExpired.nonEmpty() + ?: firebaseException.message + ?: "This code is no longer valid", + cause = firebaseException + ) + + "ERROR_INVALID_VERIFICATION_ID", + "ERROR_MISSING_VERIFICATION_ID" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorInvalidVerificationId.nonEmpty() + ?: firebaseException.message + ?: "That verification session is no longer valid. Request a new code.", + cause = firebaseException + ) + + "ERROR_RETRY_PHONE_AUTH" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorRetryPhoneAuth.nonEmpty() + ?: firebaseException.message + ?: "Phone verification didn't complete. Try again.", + cause = firebaseException + ) + + "ERROR_USER_MISMATCH" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorUserMismatch.nonEmpty() + ?: firebaseException.message + ?: "Those credentials belong to a different account.", + cause = firebaseException + ) + + "ERROR_PHONE_NUMBER_NOT_FOUND", + "ERROR_MULTI_FACTOR_INFO_NOT_FOUND", + "ERROR_MISSING_MULTI_FACTOR_INFO" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorPhoneNumberNotEnrolled.nonEmpty() + ?: firebaseException.message + ?: "That phone number isn't set up for verification on this account.", + cause = firebaseException + ) + + "ERROR_INVALID_MULTI_FACTOR_SESSION", + "ERROR_MISSING_MULTI_FACTOR_SESSION" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorMultiFactorSessionExpired.nonEmpty() + ?: firebaseException.message + ?: "Your sign-in session expired. Sign in again to continue.", + cause = firebaseException + ) + + // Custom tokens are minted by the developer's own backend; the SDK's + // diagnostic names the setup problem and means nothing to the user, so it + // stays on `cause` while `message` carries renderable generic copy. + "ERROR_INVALID_CUSTOM_TOKEN", + "ERROR_CUSTOM_TOKEN_MISMATCH" -> MisconfigurationException( + message = stringProvider?.unknownErrorRecoveryMessage.nonEmpty() + ?: "An unknown error occurred.", + cause = firebaseException + ) + + else -> InvalidCredentialsException( + message = typeLevel + ?: firebaseException.message + ?: "Invalid credentials provided", + cause = firebaseException + ) + } } is FirebaseAuthInvalidUserException -> { when (firebaseException.errorCode) { "ERROR_USER_NOT_FOUND" -> UserNotFoundException( message = stringProvider?.errorUserNotFound.nonEmpty() + ?: stringProvider?.userNotFoundRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "User not found", cause = firebaseException @@ -464,8 +635,18 @@ abstract class AuthException( cause = firebaseException ) + "ERROR_INVALID_USER_TOKEN", + "ERROR_USER_TOKEN_EXPIRED" -> InvalidCredentialsException( + message = stringProvider?.errorUserAccountGeneric.nonEmpty() + ?: stringProvider?.errorMultiFactorSessionExpired.nonEmpty() + ?: firebaseException.message + ?: "Your sign-in session expired. Sign in again to continue.", + cause = firebaseException + ) + else -> UserNotFoundException( message = stringProvider?.errorUserAccountGeneric.nonEmpty() + ?: stringProvider?.userNotFoundRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "User account error", cause = firebaseException @@ -473,10 +654,36 @@ abstract class AuthException( } } + // Must precede the plain FirebaseAuthException arm, which it extends. + is FirebaseAuthActionCodeException -> { + when (firebaseException.errorCode) { + // The type-level hook is the one scoped to the exception this produces — + // `errorInvalidCredentials`. Using `errorUnknownAuth` here would let a + // host customising the unknown-error copy silently lose this string. + "ERROR_EXPIRED_ACTION_CODE", + "ERROR_INVALID_ACTION_CODE" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorActionCodeInvalid.nonEmpty() + ?: firebaseException.message + ?: "That link is no longer valid. Request a new one.", + cause = firebaseException + ) + + else -> UnknownException( + message = stringProvider?.errorUnknownAuth.nonEmpty() + ?: stringProvider?.unknownErrorRecoveryMessage.nonEmpty() + ?: firebaseException.message + ?: "An unknown authentication error occurred", + cause = firebaseException + ) + } + } + is FirebaseAuthUserCollisionException -> { when (firebaseException.errorCode) { "ERROR_EMAIL_ALREADY_IN_USE" -> EmailAlreadyInUseException( message = stringProvider?.errorEmailAlreadyInUse.nonEmpty() + ?: stringProvider?.emailAlreadyInUseRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Email address is already in use", cause = firebaseException, @@ -485,6 +692,7 @@ abstract class AuthException( "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL" -> AccountLinkingRequiredException( message = stringProvider?.errorAccountExistsDifferentCredential.nonEmpty() + ?: stringProvider?.accountLinkingRequiredRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Account already exists with different credentials", cause = firebaseException @@ -492,6 +700,7 @@ abstract class AuthException( "ERROR_CREDENTIAL_ALREADY_IN_USE" -> AccountLinkingRequiredException( message = stringProvider?.errorCredentialAlreadyInUse.nonEmpty() + ?: stringProvider?.accountLinkingRequiredRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Credential is already associated with a different user account", cause = firebaseException @@ -499,6 +708,7 @@ abstract class AuthException( else -> AccountLinkingRequiredException( message = stringProvider?.errorAccountCollisionGeneric.nonEmpty() + ?: stringProvider?.accountLinkingRequiredRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Account collision error", cause = firebaseException @@ -509,6 +719,7 @@ abstract class AuthException( is FirebaseAuthMultiFactorException -> { MfaRequiredException( message = stringProvider?.errorMfaRequiredFallback.nonEmpty() + ?: stringProvider?.mfaRequiredRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Multi-factor authentication required", cause = firebaseException @@ -526,23 +737,96 @@ abstract class AuthException( is FirebaseAuthException -> { when (firebaseException.errorCode) { - "ERROR_TOO_MANY_REQUESTS" -> TooManyRequestsException( - message = stringProvider?.errorTooManyRequests.nonEmpty() + // FirebaseAuthWebException code for backing out of the OAuth custom tab, + // and the Credential Manager / Play services equivalent. + "ERROR_WEB_CONTEXT_CANCELED", + "ERROR_USER_CANCELLED" -> AuthCancelledException( + message = stringProvider?.errorAuthCancelled.nonEmpty() + ?: stringProvider?.authCancelledRecoveryMessage.nonEmpty() ?: firebaseException.message - ?: "Too many requests. Please try again later", + ?: "Authentication was cancelled", cause = firebaseException ) - // FirebaseAuthWebException code for backing out of the OAuth custom tab - "ERROR_WEB_CONTEXT_CANCELED" -> AuthCancelledException( - message = stringProvider?.errorAuthCancelled.nonEmpty() + // These three produce InvalidCredentialsException, so the type-level hook + // is `errorInvalidCredentials`. `errorUnknownAuth` would let a host that + // customises only the unknown-error copy lose all three specific strings. + "ERROR_UNVERIFIED_EMAIL" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorUnverifiedEmail.nonEmpty() ?: firebaseException.message - ?: "Authentication was cancelled", + ?: "Verify your email address before you continue.", + cause = firebaseException + ) + + "ERROR_SECOND_FACTOR_ALREADY_ENROLLED" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorSecondFactorAlreadyEnrolled.nonEmpty() + ?: firebaseException.message + ?: "That verification method is already set up on this account.", + cause = firebaseException + ) + + "ERROR_MAXIMUM_SECOND_FACTOR_COUNT_EXCEEDED" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorMaximumSecondFactorCountExceeded.nonEmpty() + ?: firebaseException.message + ?: "You've reached the limit for verification methods on this account.", + cause = firebaseException + ) + + // Developer setup problems. The user can do nothing about any of them, so + // `message` carries generic translated copy and the raw Firebase + // diagnostic is kept on `cause` for logs. INTERNAL_ERROR and + // ERROR_WEB_INTERNAL_ERROR are deliberately absent — they are backend + // faults, not configuration. + "ERROR_OPERATION_NOT_ALLOWED", + "ERROR_APP_NOT_AUTHORIZED", + "ERROR_UNAUTHORIZED_DOMAIN", + "ERROR_MISSING_CONTINUE_URI", + "ERROR_INVALID_CERT_HASH", + "ERROR_DYNAMIC_LINK_NOT_ACTIVATED", + "ERROR_INVALID_DYNAMIC_LINK_DOMAIN", + "ERROR_INVALID_HOSTING_LINK_DOMAIN", + "ERROR_INVALID_PROVIDER_ID", + "ERROR_ADMIN_RESTRICTED_OPERATION", + "ERROR_UNSUPPORTED_FIRST_FACTOR", + "ERROR_UNSUPPORTED_PASSTHROUGH_OPERATION", + "ERROR_INVALID_REQ_TYPE", + "ERROR_WEB_CONTEXT_ALREADY_PRESENTED", + // Tenant family + "ERROR_INVALID_TENANT_ID", + "ERROR_TENANT_ID_MISMATCH", + "ERROR_UNSUPPORTED_TENANT_OPERATION", + // reCAPTCHA / app verification family + "ERROR_RECAPTCHA_NOT_ENABLED", + "ERROR_CAPTCHA_CHECK_FAILED", + "ERROR_MISSING_RECAPTCHA_TOKEN", + "ERROR_INVALID_RECAPTCHA_TOKEN", + "ERROR_INVALID_RECAPTCHA_ACTION", + "ERROR_MISSING_RECAPTCHA_VERSION", + "ERROR_INVALID_RECAPTCHA_VERSION", + "ERROR_MISSING_CLIENT_TYPE", + "ERROR_MISSING_CLIENT_IDENTIFIER", + "ERROR_ALTERNATE_CLIENT_IDENTIFIER_REQUIRED", + // Email-template settings in the Firebase console. These arrive as + // FirebaseAuthEmailException, which extends FirebaseAuthException + // directly and so lands in this arm. + "ERROR_INVALID_MESSAGE_PAYLOAD", + "ERROR_INVALID_SENDER", + "ERROR_INVALID_RECIPIENT_EMAIL", + // Host integration and project quota. + "ERROR_MISSING_ACTIVITY", + "ERROR_WEB_STORAGE_UNSUPPORTED", + "ERROR_QUOTA_EXCEEDED" -> MisconfigurationException( + message = stringProvider?.unknownErrorRecoveryMessage.nonEmpty() + ?: "An unknown error occurred.", cause = firebaseException ) else -> UnknownException( message = stringProvider?.errorUnknownAuth.nonEmpty() + ?: stringProvider?.unknownErrorRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "An unknown authentication error occurred", cause = firebaseException @@ -550,6 +834,19 @@ abstract class AuthException( } } + // Rate limiting arrives as a plain FirebaseTooManyRequestsException, which is NOT a + // FirebaseAuthException and carries no error code. Without this arm it falls to the + // FirebaseException branch below and a throttled user is told they are offline. + is FirebaseTooManyRequestsException -> { + TooManyRequestsException( + message = stringProvider?.errorTooManyRequests.nonEmpty() + ?: stringProvider?.tooManyRequestsRecoveryMessage.nonEmpty() + ?: firebaseException.message + ?: "Too many requests. Please try again later", + cause = firebaseException + ) + } + is FirebaseException -> { val msg = firebaseException.message ?: "" if (msg.contains("PASSWORD_DOES_NOT_MEET_REQUIREMENTS", ignoreCase = true)) { @@ -565,6 +862,7 @@ abstract class AuthException( } else { NetworkException( message = stringProvider?.errorNetworkGeneric.nonEmpty() + ?: stringProvider?.networkErrorRecoveryMessage.nonEmpty() ?: msg.ifEmpty { "Network error occurred" }, cause = firebaseException ) @@ -577,6 +875,7 @@ abstract class AuthException( ) { AuthCancelledException( message = stringProvider?.errorAuthCancelled.nonEmpty() + ?: stringProvider?.authCancelledRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Authentication was cancelled", cause = firebaseException @@ -584,6 +883,7 @@ abstract class AuthException( } else { UnknownException( message = stringProvider?.errorUnknownAuth.nonEmpty() + ?: stringProvider?.unknownErrorRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "An unknown error occurred", cause = firebaseException diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt index 7e1c3698d6..558a74d4b0 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt @@ -628,4 +628,45 @@ interface AuthUIStringProvider { /** Error when authentication is cancelled. Return empty to use the Firebase SDK message. */ val errorAuthCancelled: String + + // ============================================================================================= + // AuthException messages selected by Firebase Auth error code + // + // Every member below has a default so that adding one is not a breaking change, and each + // default delegates to a coarser member rather than returning a hardcoded English literal — + // a host that has implemented this interface itself keeps getting its own translated copy. + // ============================================================================================= + + /** Error when sign-in fails and the server will not say whether the email or the password was wrong. */ + val errorIncorrectEmailOrPassword: String get() = errorInvalidCredentials + + /** Error when the SMS verification session is gone and a new code has to be requested. */ + val errorInvalidVerificationId: String get() = errorInvalidCredentials + + /** Error when phone verification did not complete and has to be retried. */ + val errorRetryPhoneAuth: String get() = errorInvalidCredentials + + /** Error when the supplied credentials belong to a different account than the one being confirmed. */ + val errorUserMismatch: String get() = errorUnknownAuth + + /** Error when the phone number is not set up as a verification method on the account. */ + val errorPhoneNumberNotEnrolled: String get() = errorInvalidCredentials + + /** Error when a sign-in or verification session has expired. */ + val errorSessionExpired: String get() = errorInvalidCredentials + + /** Error when the sign-in session expired part-way through two-step verification. */ + val errorMultiFactorSessionExpired: String get() = errorSessionExpired + + /** Error when an emailed sign-in or password reset link has expired or is malformed. */ + val errorActionCodeInvalid: String get() = errorInvalidCredentials + + /** Error when the account email has to be verified before the operation can continue. */ + val errorUnverifiedEmail: String get() = errorUnknownAuth + + /** Error when adding a verification method that is already set up on the account. */ + val errorSecondFactorAlreadyEnrolled: String get() = errorUnknownAuth + + /** Error when the account already has the maximum number of verification methods. */ + val errorMaximumSecondFactorCountExceeded: String get() = errorUnknownAuth } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt index e25776a76b..463cf5794c 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt @@ -567,4 +567,37 @@ class DefaultAuthUIStringProvider( override val errorAuthCancelled: String get() = localizedContext.getString(R.string.fui_error_auth_cancelled) + + override val errorIncorrectEmailOrPassword: String + get() = localizedContext.getString(R.string.fui_error_incorrect_email_or_password) + + override val errorInvalidVerificationId: String + get() = localizedContext.getString(R.string.fui_error_invalid_verification_id) + + override val errorRetryPhoneAuth: String + get() = localizedContext.getString(R.string.fui_error_retry_phone_auth) + + override val errorUserMismatch: String + get() = localizedContext.getString(R.string.fui_error_user_mismatch) + + override val errorPhoneNumberNotEnrolled: String + get() = localizedContext.getString(R.string.fui_error_phone_number_not_enrolled) + + override val errorSessionExpired: String + get() = localizedContext.getString(R.string.fui_error_session_expired) + + override val errorMultiFactorSessionExpired: String + get() = localizedContext.getString(R.string.fui_error_multi_factor_session_expired) + + override val errorActionCodeInvalid: String + get() = localizedContext.getString(R.string.fui_error_action_code_invalid) + + override val errorUnverifiedEmail: String + get() = localizedContext.getString(R.string.fui_error_unverified_email) + + override val errorSecondFactorAlreadyEnrolled: String + get() = localizedContext.getString(R.string.fui_error_second_factor_already_enrolled) + + override val errorMaximumSecondFactorCountExceeded: String + get() = localizedContext.getString(R.string.fui_error_maximum_second_factor_count_exceeded) } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt index ddb18c6426..483f775c94 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt @@ -143,16 +143,24 @@ internal fun getRecoveryMessage( stringProvider: AuthUIStringProvider ): String { return when (error) { - is AuthException.NetworkException -> stringProvider.networkErrorRecoveryMessage + // AuthException.from already puts generic translated copy on the message and keeps the + // raw diagnostic on the cause, so this arm is belt-and-braces: an instance constructed + // directly with a raw diagnostic still cannot leak it into the dialog. + is AuthException.MisconfigurationException -> stringProvider.unknownErrorRecoveryMessage + is AuthException.NetworkException -> + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.networkErrorRecoveryMessage is AuthException.InvalidCredentialsException -> { - // Use the actual error message from Firebase if available, otherwise fallback to generic message - error.message?.takeIf { it.isNotBlank() && it != "Invalid credentials provided" } + // AuthException.from now picks library-owned copy per Firebase error code, so the + // message is the specific one; the generic string is only the empty-message fallback. + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.invalidCredentialsRecoveryMessage } - is AuthException.UserNotFoundException -> stringProvider.userNotFoundRecoveryMessage + is AuthException.UserNotFoundException -> + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.userNotFoundRecoveryMessage is AuthException.WeakPasswordException -> { // Include specific reason if available - val baseMessage = stringProvider.weakPasswordRecoveryMessage + val baseMessage = error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.weakPasswordRecoveryMessage error.reason?.let { reason -> "$baseMessage\n\nReason: $reason" } ?: baseMessage @@ -165,18 +173,22 @@ internal fun getRecoveryMessage( is AuthException.EmailAlreadyInUseException -> { // Include email if available - val baseMessage = stringProvider.emailAlreadyInUseRecoveryMessage + val baseMessage = error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.emailAlreadyInUseRecoveryMessage error.email?.let { email -> "$baseMessage ($email)" } ?: baseMessage } - is AuthException.TooManyRequestsException -> stringProvider.tooManyRequestsRecoveryMessage + is AuthException.TooManyRequestsException -> + error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.tooManyRequestsRecoveryMessage is AuthException.PhoneVerificationCooldownException -> { // Use the custom message which includes remaining cooldown time error.message ?: stringProvider.unknownErrorRecoveryMessage } - is AuthException.MfaRequiredException -> stringProvider.mfaRequiredRecoveryMessage + is AuthException.MfaRequiredException -> + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.mfaRequiredRecoveryMessage is AuthException.AccountLinkingRequiredException -> { // Use the custom message which includes email and provider details error.message ?: stringProvider.accountLinkingRequiredRecoveryMessage @@ -194,7 +206,8 @@ internal fun getRecoveryMessage( val providerName = error.providerName ?: stringProvider.emailProvider stringProvider.emailLinkCrossDeviceLinkingMessage(providerName) } - is AuthException.AuthCancelledException -> stringProvider.authCancelledRecoveryMessage + is AuthException.AuthCancelledException -> + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.authCancelledRecoveryMessage is AuthException.UnknownException -> { // Use custom message if available (e.g., for configuration errors) error.message?.takeIf { it.isNotBlank() } ?: stringProvider.unknownErrorRecoveryMessage @@ -262,6 +275,7 @@ internal fun isRecoverable(error: AuthException): Boolean { is AuthException.EmailLinkCrossDeviceLinkingException -> true is AuthException.EmailLinkWrongDeviceException -> true is AuthException.EmailLinkDifferentAnonymousUserException -> false + is AuthException.MisconfigurationException -> false // Retrying cannot fix project setup is AuthException.UnknownException -> true else -> true } diff --git a/auth/src/main/res/values/strings.xml b/auth/src/main/res/values/strings.xml index ca6738c108..6942dbbd9a 100644 --- a/auth/src/main/res/values/strings.xml +++ b/auth/src/main/res/values/strings.xml @@ -267,6 +267,19 @@ + + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Choose Authentication Method Set Up SMS Verification diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt new file mode 100644 index 0000000000..577b3c41cf --- /dev/null +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt @@ -0,0 +1,358 @@ +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * 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. + */ + +package com.firebase.ui.auth + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.ui.components.getRecoveryMessage +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import com.google.firebase.FirebaseException +import com.google.firebase.FirebaseTooManyRequestsException +import com.google.firebase.auth.FirebaseAuthException +import com.google.firebase.auth.FirebaseAuthInvalidUserException +import com.google.firebase.auth.FirebaseAuthMultiFactorException +import com.google.firebase.auth.FirebaseAuthUserCollisionException +import com.google.firebase.auth.FirebaseAuthWeakPasswordException +import java.util.Locale +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.kotlin.whenever +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * End-to-end resolution tests: a real Firebase exception goes through [AuthException.from] with a + * real [DefaultAuthUIStringProvider], and the result is rendered through + * [getRecoveryMessage] exactly as the error dialog would render it. + * + * Why the real provider and not a mock: the `fui_error_*` type-level hooks are deliberately blank + * so hosts can override them, so a mocked provider that stubs one proves nothing about what ships. + * Every assertion here fails if a branch of `from()` falls through a blank hook to the raw, + * untranslated Firebase SDK diagnostic. + * + * @suppress Internal test class + */ +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) +class AuthExceptionRecoveryResolutionTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + private val strings: AuthUIStringProvider = DefaultAuthUIStringProvider(context) + + /** Renders [firebaseException] the way the error dialog does, end to end. */ + private fun resolve( + firebaseException: Exception, + provider: AuthUIStringProvider = strings + ): String = getRecoveryMessage(AuthException.from(firebaseException, provider), provider) + + // Verbatim firebase-auth 24.2.0 diagnostics. All are English regardless of device locale. + private val networkDiagnostic = "A network error (such as timeout, interrupted connection or " + + "unreachable host) has occurred." + private val userNotFoundDiagnostic = "There is no user record corresponding to this " + + "identifier. The user may have been deleted." + private val weakPasswordDiagnostic = "The given password is invalid. [ Password should be at " + + "least 6 characters ]" + private val emailInUseDiagnostic = "The email address is already in use by another account." + private val mfaDiagnostic = "Please complete a second factor challenge." + private val cancelledDiagnostic = "User cancelled the sign-in flow." + private val operationNotAllowedDiagnostic = "This operation is not allowed. This may be " + + "because the given sign-in provider is disabled for this Firebase project. Enable it " + + "in the Firebase console, under the sign-in method tab of the Auth section. " + + "[ OPERATION_NOT_ALLOWED ]" + + private fun userCollision(code: String, email: String? = null): FirebaseAuthUserCollisionException { + val exception = mock(FirebaseAuthUserCollisionException::class.java) + whenever(exception.errorCode).thenReturn(code) + whenever(exception.message).thenReturn(emailInUseDiagnostic) + whenever(exception.email).thenReturn(email) + return exception + } + + private fun multiFactor(): FirebaseAuthMultiFactorException { + val exception = mock(FirebaseAuthMultiFactorException::class.java) + whenever(exception.message).thenReturn(mfaDiagnostic) + return exception + } + + // ============================================================================================= + // The six types whose type-level hook is deliberately blank + // ============================================================================================= + + @Test + fun `network failure resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = object : FirebaseException(networkDiagnostic) {} + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.networkErrorRecoveryMessage) + assertThat(resolved).isNotEqualTo(networkDiagnostic) + } + + @Test + fun `user not found resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = + FirebaseAuthInvalidUserException("ERROR_USER_NOT_FOUND", userNotFoundDiagnostic) + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.userNotFoundRecoveryMessage) + assertThat(resolved).isNotEqualTo(userNotFoundDiagnostic) + } + + @Test + fun `an unmapped user code resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = + FirebaseAuthInvalidUserException("ERROR_SOMETHING_NEW", userNotFoundDiagnostic) + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.userNotFoundRecoveryMessage) + assertThat(resolved).isNotEqualTo(userNotFoundDiagnostic) + } + + @Test + fun `weak password resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = FirebaseAuthWeakPasswordException( + "ERROR_WEAK_PASSWORD", + weakPasswordDiagnostic, + "Password should be at least 6 characters" + ) + + val authException = AuthException.from(firebaseException, strings) + + // getRecoveryMessage appends the SDK's untranslated `reason` on top of the base copy, so + // the invariant this test defends is the exception's own message. + assertThat(authException.message).isEqualTo(strings.weakPasswordRecoveryMessage) + assertThat(authException.message).isNotEqualTo(weakPasswordDiagnostic) + assertThat(resolve(firebaseException)).startsWith(strings.weakPasswordRecoveryMessage) + } + + @Test + fun `email already in use resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = userCollision("ERROR_EMAIL_ALREADY_IN_USE") + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.emailAlreadyInUseRecoveryMessage) + assertThat(resolved).isNotEqualTo(emailInUseDiagnostic) + } + + @Test + fun `mfa required resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = multiFactor() + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.mfaRequiredRecoveryMessage) + assertThat(resolved).isNotEqualTo(mfaDiagnostic) + } + + @Test + fun `auth cancelled resolves to the library's own copy, not the SDK diagnostic`() { + for (code in listOf("ERROR_USER_CANCELLED", "ERROR_WEB_CONTEXT_CANCELED")) { + val firebaseException = object : FirebaseAuthException(code, cancelledDiagnostic) {} + + val resolved = resolve(firebaseException) + + assertWithMessage(code).that(resolved).isEqualTo(strings.authCancelledRecoveryMessage) + assertWithMessage(code).that(resolved).isNotEqualTo(cancelledDiagnostic) + } + } + + @Test + fun `too many requests resolves to the library's own copy, not the SDK diagnostic`() { + val diagnostic = "We have blocked all requests from this device due to unusual activity." + val firebaseException = FirebaseTooManyRequestsException(diagnostic) + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.tooManyRequestsRecoveryMessage) + assertThat(resolved).isNotEqualTo(diagnostic) + } + + @Test + fun `account collision resolves to the library's own copy, not the SDK diagnostic`() { + val codes = listOf( + "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL", + "ERROR_CREDENTIAL_ALREADY_IN_USE", + "ERROR_SOME_FUTURE_COLLISION_CODE", + ) + + for (code in codes) { + val resolved = resolve(userCollision(code)) + + assertWithMessage(code).that(resolved) + .isEqualTo(strings.accountLinkingRequiredRecoveryMessage) + assertWithMessage(code).that(resolved).isNotEqualTo(emailInUseDiagnostic) + } + } + + // ============================================================================================= + // Misconfiguration — the diagnostic must not be on the message at all + // ============================================================================================= + + @Test + fun `a disabled sign-in provider never puts the console diagnostic on the message`() { + val firebaseException = + object : FirebaseAuthException("ERROR_OPERATION_NOT_ALLOWED", operationNotAllowedDiagnostic) {} + + val authException = AuthException.from(firebaseException, strings) + + assertThat(authException).isInstanceOf(AuthException.MisconfigurationException::class.java) + // EmailAuthScreen and PhoneAuthScreen render this inline, bypassing getRecoveryMessage. + assertThat(authException.message).isEqualTo(strings.unknownErrorRecoveryMessage) + assertThat(authException.message).doesNotContain("Firebase") + assertThat(authException.message).doesNotContain("OPERATION_NOT_ALLOWED") + // The diagnostic is still there for logs and for hosts. + assertThat(authException.cause).isEqualTo(firebaseException) + assertThat(authException.cause?.message).isEqualTo(operationNotAllowedDiagnostic) + assertThat(resolve(firebaseException)).isEqualTo(strings.unknownErrorRecoveryMessage) + } + + @Test + fun `the email-template and quota codes are misconfiguration, not unknown errors`() { + val codes = listOf( + "ERROR_INVALID_MESSAGE_PAYLOAD", + "ERROR_INVALID_SENDER", + "ERROR_INVALID_RECIPIENT_EMAIL", + "ERROR_MISSING_ACTIVITY", + "ERROR_WEB_STORAGE_UNSUPPORTED", + "ERROR_QUOTA_EXCEEDED", + ) + + for (code in codes) { + val diagnostic = "Raw SDK English naming $code." + val firebaseException = object : FirebaseAuthException(code, diagnostic) {} + val authException = AuthException.from(firebaseException, strings) + + assertWithMessage(code).that(authException) + .isInstanceOf(AuthException.MisconfigurationException::class.java) + assertWithMessage(code).that(authException.message).isNotEqualTo(diagnostic) + assertWithMessage(code).that(authException.cause?.message).isEqualTo(diagnostic) + } + } + + // ============================================================================================= + // Blanket invariant + // ============================================================================================= + + @Test + fun `no Firebase error code resolves to the raw SDK diagnostic`() { + // One representative code per arm of from(), plus codes that hit each else branch. + val codes = listOf( + "ERROR_INVALID_CREDENTIAL", + "ERROR_WRONG_PASSWORD", + "ERROR_INVALID_EMAIL", + "ERROR_MISSING_EMAIL", + "ERROR_MISSING_PASSWORD", + "ERROR_INVALID_PHONE_NUMBER", + "ERROR_MISSING_PHONE_NUMBER", + "ERROR_INVALID_VERIFICATION_CODE", + "ERROR_SESSION_EXPIRED", + "ERROR_INVALID_VERIFICATION_ID", + "ERROR_RETRY_PHONE_AUTH", + "ERROR_USER_MISMATCH", + "ERROR_PHONE_NUMBER_NOT_FOUND", + "ERROR_MULTI_FACTOR_INFO_NOT_FOUND", + "ERROR_MISSING_MULTI_FACTOR_INFO", + "ERROR_INVALID_MULTI_FACTOR_SESSION", + "ERROR_INVALID_CUSTOM_TOKEN", + ) + val diagnostic = "The Firebase SDK's own untranslated English." + + for (code in codes) { + val firebaseException = + com.google.firebase.auth.FirebaseAuthInvalidCredentialsException(code, diagnostic) + assertWithMessage(code).that(resolve(firebaseException)).isNotEqualTo(diagnostic) + assertWithMessage(code).that(AuthException.from(firebaseException, strings).message) + .isNotEqualTo(diagnostic) + } + } + + @Test + fun `no plain auth error code resolves to the raw SDK diagnostic`() { + val codes = listOf( + "ERROR_UNVERIFIED_EMAIL", + "ERROR_SECOND_FACTOR_ALREADY_ENROLLED", + "ERROR_MAXIMUM_SECOND_FACTOR_COUNT_EXCEEDED", + "ERROR_OPERATION_NOT_ALLOWED", + "ERROR_UNAUTHORIZED_DOMAIN", + "ERROR_INVALID_CERT_HASH", + "ERROR_RECAPTCHA_NOT_ENABLED", + "ERROR_QUOTA_EXCEEDED", + // The else branch: neither user-facing nor a known setup problem. + "INTERNAL_ERROR", + "ERROR_WEB_INTERNAL_ERROR", + "ERROR_SOME_FUTURE_AUTH_CODE", + ) + val diagnostic = "The Firebase SDK's own untranslated English." + + for (code in codes) { + val firebaseException = object : FirebaseAuthException(code, diagnostic) {} + assertWithMessage(code).that(resolve(firebaseException)).isNotEqualTo(diagnostic) + assertWithMessage(code).that(AuthException.from(firebaseException, strings).message) + .isNotEqualTo(diagnostic) + } + } + + // ============================================================================================= + // The copy is actually translated, not just library-owned + // ============================================================================================= + + @Test + fun `a French device offline sees French, not the English network diagnostic`() { + val french = DefaultAuthUIStringProvider(context, Locale.FRENCH) + val firebaseException = object : FirebaseException(networkDiagnostic) {} + + val resolved = resolve(firebaseException, french) + + assertThat(resolved).isEqualTo(french.networkErrorRecoveryMessage) + assertThat(resolved).isNotEqualTo(networkDiagnostic) + // Guards against the French resource silently falling back to values/. + assertThat(resolved).isNotEqualTo(strings.networkErrorRecoveryMessage) + } + + @Test + fun `a French device with no such account sees French, not the English diagnostic`() { + val french = DefaultAuthUIStringProvider(context, Locale.FRENCH) + val firebaseException = + FirebaseAuthInvalidUserException("ERROR_USER_NOT_FOUND", userNotFoundDiagnostic) + + val resolved = resolve(firebaseException, french) + + assertThat(resolved).isEqualTo(french.userNotFoundRecoveryMessage) + assertThat(resolved).isNotEqualTo(userNotFoundDiagnostic) + assertThat(resolved).isNotEqualTo(strings.userNotFoundRecoveryMessage) + } + + // ============================================================================================= + // A host's own hook still wins + // ============================================================================================= + + @Test + fun `a host that fills the type-level hook still overrides the generic recovery copy`() { + val hostStrings = mock(AuthUIStringProvider::class.java) + whenever(hostStrings.errorNetworkGeneric).thenReturn("Host network copy") + whenever(hostStrings.networkErrorRecoveryMessage).thenReturn("Generic network copy") + + val result = AuthException.from(object : FirebaseException(networkDiagnostic) {}, hostStrings) + + assertThat(result.message).isEqualTo("Host network copy") + } +} diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt index ea8ec7ecd9..978c84c9fe 100644 --- a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt @@ -16,8 +16,12 @@ package com.firebase.ui.auth import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import com.google.firebase.FirebaseException +import com.google.firebase.FirebaseTooManyRequestsException +import com.google.firebase.auth.FirebaseAuthActionCodeException import com.google.firebase.auth.FirebaseAuthException +import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException import com.google.firebase.auth.FirebaseAuthInvalidUserException import com.google.firebase.auth.FirebaseAuthWeakPasswordException import org.junit.Test @@ -52,19 +56,46 @@ class AuthExceptionTest { } @Test - fun `from() maps FirebaseAuthException with ERROR_TOO_MANY_REQUESTS to TooManyRequestsException`() { - // Arrange - val firebaseException = object : FirebaseAuthException("ERROR_TOO_MANY_REQUESTS", "Too many requests") {} + fun `from() maps FirebaseTooManyRequestsException to TooManyRequestsException`() { + // Arrange — rate limiting arrives as this type, not as a FirebaseAuthException. It carries + // no error code, so the only thing that can select the arm is the exception class itself. + val firebaseException = FirebaseTooManyRequestsException( + "We have blocked all requests from this device due to unusual activity. Try again later." + ) // Act val authException = AuthException.from(firebaseException) - // Assert + // Assert — without a dedicated arm this falls through to `is FirebaseException` and a + // throttled user is told they have no internet connection. assertThat(authException).isInstanceOf(AuthException.TooManyRequestsException::class.java) - assertThat(authException.message).isEqualTo("Too many requests") assertThat(authException.cause).isEqualTo(firebaseException) } + @Test + fun `from() takes the too-many-requests message from the string provider`() { + val firebaseException = FirebaseTooManyRequestsException("Blocked due to unusual activity.") + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorTooManyRequests).thenReturn("Zu viele Versuche") + + val result = AuthException.from(firebaseException, stringProvider) + + assertThat(result).isInstanceOf(AuthException.TooManyRequestsException::class.java) + assertThat(result.message).isEqualTo("Zu viele Versuche") + } + + @Test + fun `from() no longer treats ERROR_TOO_MANY_REQUESTS as a real Firebase Auth code`() { + // firebase-auth 24.2.0 has no such code: every status that means "rate limited" (17010, + // 17052) is turned into a FirebaseTooManyRequestsException instead. Anything still + // carrying this string is not from the SDK, so it is just an unknown code. + val firebaseException = object : FirebaseAuthException("ERROR_TOO_MANY_REQUESTS", "x") {} + + val result = AuthException.from(firebaseException) + + assertThat(result).isInstanceOf(AuthException.UnknownException::class.java) + } + @Test fun `from() maps FirebaseAuthException with unknown error code to UnknownException`() { // Arrange @@ -289,4 +320,272 @@ class AuthExceptionTest { assertThat(exception.failingRequirements).isEqualTo(requirements) } + + // ============================================================================================= + // Per-error-code message selection + // + // Every code below is one the resolved firebase-auth 24.2.0 maps onto the exception type the + // arm matches, so each branch is reachable. The provider member is stubbed to a sentinel that + // exists nowhere else: collapsing two codes onto one branch, or dropping a branch back to the + // arm's generic `else`, changes the message and fails the test. + // ============================================================================================= + + /** The raw text the SDK would put on the exception, which must lose to the provider string. */ + private val sdkText = "The Firebase SDK's own untranslated English." + + private fun invalidCredentials(errorCode: String) = + FirebaseAuthInvalidCredentialsException(errorCode, sdkText) + + @Test + fun `from() routes each invalid-credentials error code to its own string`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorIncorrectEmailOrPassword).thenReturn("s:incorrectEmailOrPassword") + whenever(stringProvider.invalidPassword).thenReturn("s:invalidPassword") + whenever(stringProvider.invalidEmailAddress).thenReturn("s:invalidEmailAddress") + whenever(stringProvider.missingEmailAddress).thenReturn("s:missingEmailAddress") + whenever(stringProvider.requiredField).thenReturn("s:requiredField") + whenever(stringProvider.invalidPhoneNumber).thenReturn("s:invalidPhoneNumber") + whenever(stringProvider.missingPhoneNumber).thenReturn("s:missingPhoneNumber") + whenever(stringProvider.invalidVerificationCode).thenReturn("s:invalidVerificationCode") + whenever(stringProvider.errorSessionExpired).thenReturn("s:sessionExpired") + whenever(stringProvider.errorInvalidVerificationId).thenReturn("s:invalidVerificationId") + whenever(stringProvider.errorRetryPhoneAuth).thenReturn("s:retryPhoneAuth") + whenever(stringProvider.errorUserMismatch).thenReturn("s:userMismatch") + whenever(stringProvider.errorPhoneNumberNotEnrolled).thenReturn("s:phoneNumberNotEnrolled") + whenever(stringProvider.errorMultiFactorSessionExpired).thenReturn("s:multiFactorSessionExpired") + + val expected = mapOf( + "ERROR_INVALID_CREDENTIAL" to "s:incorrectEmailOrPassword", + "ERROR_WRONG_PASSWORD" to "s:invalidPassword", + "ERROR_INVALID_EMAIL" to "s:invalidEmailAddress", + "ERROR_MISSING_EMAIL" to "s:missingEmailAddress", + "ERROR_MISSING_PASSWORD" to "s:requiredField", + "ERROR_MISSING_VERIFICATION_CODE" to "s:requiredField", + "ERROR_INVALID_PHONE_NUMBER" to "s:invalidPhoneNumber", + "ERROR_MISSING_PHONE_NUMBER" to "s:missingPhoneNumber", + "ERROR_INVALID_VERIFICATION_CODE" to "s:invalidVerificationCode", + "ERROR_SESSION_EXPIRED" to "s:sessionExpired", + "ERROR_INVALID_VERIFICATION_ID" to "s:invalidVerificationId", + "ERROR_MISSING_VERIFICATION_ID" to "s:invalidVerificationId", + "ERROR_RETRY_PHONE_AUTH" to "s:retryPhoneAuth", + "ERROR_USER_MISMATCH" to "s:userMismatch", + "ERROR_PHONE_NUMBER_NOT_FOUND" to "s:phoneNumberNotEnrolled", + "ERROR_MULTI_FACTOR_INFO_NOT_FOUND" to "s:phoneNumberNotEnrolled", + "ERROR_INVALID_MULTI_FACTOR_SESSION" to "s:multiFactorSessionExpired", + "ERROR_MISSING_MULTI_FACTOR_SESSION" to "s:multiFactorSessionExpired", + ) + + val actual = expected.keys.associateWith { code -> + val result = AuthException.from(invalidCredentials(code), stringProvider) + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + result.message + } + + assertThat(actual).containsExactlyEntriesIn(expected) + } + + @Test + fun `from() uses ERROR_INVALID_CREDENTIAL copy that does not blame the password`() { + // Under email enumeration protection this single code covers wrong password AND no such + // account, so reusing the wrong-password string would state something false. + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorIncorrectEmailOrPassword).thenReturn("Email or password wrong") + whenever(stringProvider.invalidPassword).thenReturn("Incorrect password.") + + val result = AuthException.from(invalidCredentials("ERROR_INVALID_CREDENTIAL"), stringProvider) + + assertThat(result.message).isEqualTo("Email or password wrong") + } + + @Test + fun `from() prefers the blank-able type-level hook over the per-code string`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorInvalidCredentials).thenReturn("Host-wide override") + whenever(stringProvider.invalidPassword).thenReturn("Incorrect password.") + + val result = AuthException.from(invalidCredentials("ERROR_WRONG_PASSWORD"), stringProvider) + + assertThat(result.message).isEqualTo("Host-wide override") + } + + @Test + fun `from() falls back to the Firebase message when the per-code string is blank`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorInvalidCredentials).thenReturn("") + whenever(stringProvider.invalidPassword).thenReturn("") + + val result = AuthException.from(invalidCredentials("ERROR_WRONG_PASSWORD"), stringProvider) + + assertThat(result.message).isEqualTo(sdkText) + } + + @Test + fun `from() routes custom token codes to MisconfigurationException`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.unknownErrorRecoveryMessage).thenReturn("Une erreur est survenue") + + for (code in listOf("ERROR_INVALID_CUSTOM_TOKEN", "ERROR_CUSTOM_TOKEN_MISMATCH")) { + val firebaseException = invalidCredentials(code) + val result = AuthException.from(firebaseException, stringProvider) + assertThat(result).isInstanceOf(AuthException.MisconfigurationException::class.java) + // The diagnostic names the developer's own token backend; it is renderable nowhere. + assertWithMessage(code).that(result.message).isEqualTo("Une erreur est survenue") + assertWithMessage(code).that(result.cause).isEqualTo(firebaseException) + assertWithMessage(code).that(result.cause?.message).isEqualTo(sdkText) + } + } + + @Test + fun `from() routes expired user tokens to the session-expired copy`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorMultiFactorSessionExpired).thenReturn("Session gone") + + for (code in listOf("ERROR_INVALID_USER_TOKEN", "ERROR_USER_TOKEN_EXPIRED")) { + val firebaseException = FirebaseAuthInvalidUserException(code, sdkText) + val result = AuthException.from(firebaseException, stringProvider) + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(result.message).isEqualTo("Session gone") + } + } + + @Test + fun `from() routes action code failures to the action-code copy`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorActionCodeInvalid).thenReturn("Link no longer valid") + + for (code in listOf("ERROR_EXPIRED_ACTION_CODE", "ERROR_INVALID_ACTION_CODE")) { + val firebaseException = FirebaseAuthActionCodeException(code, sdkText) + val result = AuthException.from(firebaseException, stringProvider) + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(result.message).isEqualTo("Link no longer valid") + } + } + + @Test + fun `from() routes the user-facing plain auth codes to their own strings`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorUnverifiedEmail).thenReturn("s:unverifiedEmail") + whenever(stringProvider.errorSecondFactorAlreadyEnrolled).thenReturn("s:alreadyEnrolled") + whenever(stringProvider.errorMaximumSecondFactorCountExceeded).thenReturn("s:maxFactors") + + val expected = mapOf( + "ERROR_UNVERIFIED_EMAIL" to "s:unverifiedEmail", + "ERROR_SECOND_FACTOR_ALREADY_ENROLLED" to "s:alreadyEnrolled", + "ERROR_MAXIMUM_SECOND_FACTOR_COUNT_EXCEEDED" to "s:maxFactors", + ) + + val actual = expected.keys.associateWith { code -> + val result = AuthException.from(object : FirebaseAuthException(code, sdkText) {}, stringProvider) + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + result.message + } + + assertThat(actual).containsExactlyEntriesIn(expected) + } + + @Test + fun `from() routes developer setup codes to MisconfigurationException, diagnostic on the cause`() { + val configurationCodes = listOf( + "ERROR_OPERATION_NOT_ALLOWED", + "ERROR_APP_NOT_AUTHORIZED", + "ERROR_UNAUTHORIZED_DOMAIN", + "ERROR_MISSING_CONTINUE_URI", + "ERROR_INVALID_CERT_HASH", + "ERROR_DYNAMIC_LINK_NOT_ACTIVATED", + "ERROR_INVALID_DYNAMIC_LINK_DOMAIN", + "ERROR_INVALID_HOSTING_LINK_DOMAIN", + "ERROR_INVALID_PROVIDER_ID", + "ERROR_ADMIN_RESTRICTED_OPERATION", + "ERROR_UNSUPPORTED_FIRST_FACTOR", + "ERROR_UNSUPPORTED_PASSTHROUGH_OPERATION", + "ERROR_INVALID_REQ_TYPE", + "ERROR_WEB_CONTEXT_ALREADY_PRESENTED", + "ERROR_INVALID_TENANT_ID", + "ERROR_TENANT_ID_MISMATCH", + "ERROR_UNSUPPORTED_TENANT_OPERATION", + "ERROR_RECAPTCHA_NOT_ENABLED", + "ERROR_CAPTCHA_CHECK_FAILED", + "ERROR_MISSING_RECAPTCHA_TOKEN", + "ERROR_INVALID_RECAPTCHA_TOKEN", + "ERROR_INVALID_RECAPTCHA_ACTION", + "ERROR_MISSING_RECAPTCHA_VERSION", + "ERROR_INVALID_RECAPTCHA_VERSION", + "ERROR_MISSING_CLIENT_TYPE", + "ERROR_MISSING_CLIENT_IDENTIFIER", + "ERROR_ALTERNATE_CLIENT_IDENTIFIER_REQUIRED", + // Email-template settings in the Firebase console. + "ERROR_INVALID_MESSAGE_PAYLOAD", + "ERROR_INVALID_SENDER", + "ERROR_INVALID_RECIPIENT_EMAIL", + // Host integration and project quota. + "ERROR_MISSING_ACTIVITY", + "ERROR_WEB_STORAGE_UNSUPPORTED", + "ERROR_QUOTA_EXCEEDED", + ) + // EmailAuthScreen and PhoneAuthScreen render `exception.message` inline without going + // through getRecoveryMessage, so the diagnostic must not be on the message at all. It + // stays on the cause, where logs and `exception.cause?.message` still reach it. + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorUnknownAuth).thenReturn("Generic unknown error") + whenever(stringProvider.unknownErrorRecoveryMessage).thenReturn("Une erreur est survenue") + + for (code in configurationCodes) { + val firebaseException = object : FirebaseAuthException(code, sdkText) {} + val result = AuthException.from(firebaseException, stringProvider) + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.MisconfigurationException::class.java) + assertWithMessage(code).that(result.message).isEqualTo("Une erreur est survenue") + assertWithMessage(code).that(result.cause).isEqualTo(firebaseException) + assertWithMessage(code).that(result.cause?.message).isEqualTo(sdkText) + } + } + + @Test + fun `from() keeps internal errors out of MisconfigurationException`() { + // INTERNAL_ERROR is what the SDK falls back to for a status it does not recognise, and + // ERROR_WEB_INTERNAL_ERROR is a backend fault. Neither is a setup problem. + for (code in listOf("INTERNAL_ERROR", "ERROR_WEB_INTERNAL_ERROR")) { + val result = AuthException.from(object : FirebaseAuthException(code, sdkText) {}) + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.UnknownException::class.java) + } + } + + @Test + fun `from() maps ERROR_USER_CANCELLED to AuthCancelledException`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorAuthCancelled).thenReturn("You cancelled") + + for (code in listOf("ERROR_USER_CANCELLED", "ERROR_WEB_CONTEXT_CANCELED")) { + val result = AuthException.from(object : FirebaseAuthException(code, sdkText) {}, stringProvider) + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.AuthCancelledException::class.java) + assertWithMessage(code).that(result.message).isEqualTo("You cancelled") + } + } + + @Test + fun `new AuthUIStringProvider members compile to real JVM default methods`() { + // Decision: every new member must be source- AND binary-compatible, so a host that + // implemented the interface before this change still compiles and still links. An + // abstract JVM method here would break every existing implementor at runtime. + val newMembers = listOf( + "getErrorIncorrectEmailOrPassword", + "getErrorInvalidVerificationId", + "getErrorRetryPhoneAuth", + "getErrorUserMismatch", + "getErrorPhoneNumberNotEnrolled", + "getErrorSessionExpired", + "getErrorMultiFactorSessionExpired", + "getErrorActionCodeInvalid", + "getErrorUnverifiedEmail", + "getErrorSecondFactorAlreadyEnrolled", + "getErrorMaximumSecondFactorCountExceeded", + ) + + for (name in newMembers) { + val method = AuthUIStringProvider::class.java.getMethod(name) + assertWithMessage(name).that(method.isDefault).isTrue() + } + } } \ No newline at end of file diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt index 7ee51ee126..31058c2487 100644 --- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt @@ -26,6 +26,7 @@ import com.firebase.ui.auth.configuration.PasswordRule import com.firebase.ui.auth.configuration.authUIConfiguration import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.ui.components.getRecoveryMessage import com.firebase.ui.auth.util.EmailLinkPersistenceManager import com.firebase.ui.auth.util.MockPersistenceManager import com.google.android.gms.tasks.TaskCompletionSource @@ -33,6 +34,7 @@ import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import com.google.firebase.FirebaseApp import com.google.firebase.FirebaseOptions +import com.google.firebase.FirebaseTooManyRequestsException import com.google.firebase.auth.ActionCodeSettings import com.google.firebase.auth.AuthCredential import com.google.firebase.auth.AuthResult @@ -2264,8 +2266,11 @@ class EmailAuthProviderFirebaseAuthUITest { // "Too many attempts. Please try again later", in Japanese. val localizedMessage = "試行回数が多すぎます。しばらくしてからもう一度お試しください" `when`(mockFirebaseAuth.currentUser).thenReturn(null) - val tooManyRequests = - object : FirebaseAuthException("ERROR_TOO_MANY_REQUESTS", "Too many requests") {} + // Rate limiting is a FirebaseTooManyRequestsException, not a FirebaseAuthException: + // it is not an auth exception at all and carries no error code. + val tooManyRequests = FirebaseTooManyRequestsException( + "We have blocked all requests from this device due to unusual activity." + ) val taskCompletionSource = TaskCompletionSource() taskCompletionSource.setException(tooManyRequests) `when`(mockFirebaseAuth.sendSignInLinkToEmail(anyString(), any())) @@ -2361,4 +2366,168 @@ class EmailAuthProviderFirebaseAuthUITest { assertThat((state as AuthState.Error).exception).hasMessageThat() .isEqualTo(localizedMessage) } + + @Test + fun `signInWithEmailAndPassword - wrong password uses the per-code string, not the invalid-credentials one`() = + runTest { + // "The password is incorrect", in Japanese. ERROR_WRONG_PASSWORD used to share one flat + // arm with every other invalid-credential code, so this string was unreachable. + val localizedMessage = "パスワードが正しくありません" + val wrongPassword = FirebaseAuthInvalidCredentialsException( + "ERROR_WRONG_PASSWORD", + "The password is invalid or the user does not have a password. [ INVALID_PASSWORD ]" + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(wrongPassword) + `when`(mockFirebaseAuth.signInWithEmailAndPassword("test@example.com", "Pass@123")) + .thenReturn(taskCompletionSource.task) + + val config = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val invalidPassword: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithEmailAndPassword( + context = applicationContext, + email = "test@example.com", + password = "Pass@123" + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `signInWithEmailAndPassword - merged invalid-credential code does not blame the password`() = + runTest { + // With email enumeration protection on, a wrong password and a nonexistent account both + // arrive as ERROR_INVALID_CREDENTIAL, so "Incorrect password" would be a false claim. + val incorrectEmailOrPassword = "That email or password isn't correct" + val invalidCredential = FirebaseAuthInvalidCredentialsException( + "ERROR_INVALID_CREDENTIAL", + "The supplied auth credential is incorrect, malformed or has expired." + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(invalidCredential) + `when`(mockFirebaseAuth.signInWithEmailAndPassword("test@example.com", "Pass@123")) + .thenReturn(taskCompletionSource.task) + + val config = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithEmailAndPassword( + context = applicationContext, + email = "test@example.com", + password = "Pass@123" + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(incorrectEmailOrPassword) + assertThat(thrown).hasMessageThat() + .isNotEqualTo(applicationContext.getString(R.string.fui_error_invalid_password)) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(incorrectEmailOrPassword) + } + + @Test + fun `sendSignInLinkToEmail - a disabled provider surfaces as MisconfigurationException`() = + runTest { + // The user can do nothing about a provider left disabled in the Firebase console, so + // the raw diagnostic stays on the cause for logs and never reaches the message. + val rawDiagnostic = "This operation is not allowed. This may be because the given " + + "sign-in provider is disabled for this Firebase project. [ OPERATION_NOT_ALLOWED ]" + `when`(mockFirebaseAuth.currentUser).thenReturn(null) + val notAllowed = + object : FirebaseAuthException("ERROR_OPERATION_NOT_ALLOWED", rawDiagnostic) {} + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(notAllowed) + `when`(mockFirebaseAuth.sendSignInLinkToEmail(anyString(), any())) + .thenReturn(taskCompletionSource.task) + + val provider = AuthProvider.Email( + isEmailLinkSignInEnabled = true, + emailLinkActionCodeSettings = ActionCodeSettings.newBuilder() + .setUrl("https://example.com") + .setHandleCodeInApp(true) + .setAndroidPackageName("com.test", true, null) + .build(), + passwordValidationRules = emptyList() + ) + val config = authUIConfiguration { + context = applicationContext + providers { provider(provider) } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).sendSignInLinkToEmail( + context = applicationContext, + provider = provider, + email = "test@example.com", + credentialForLinking = null + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.MisconfigurationException::class.java) + // EmailAuthScreen renders `exception.message` inline, so the message itself has to be + // clean; the diagnostic is still reachable as `exception.cause?.message`. + assertThat(thrown).hasMessageThat().doesNotContain("Firebase") + assertThat(thrown).hasMessageThat().doesNotContain("OPERATION_NOT_ALLOWED") + assertThat(thrown?.cause).isEqualTo(notAllowed) + assertThat(thrown?.cause).hasMessageThat().isEqualTo(rawDiagnostic) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + val emitted = (state as AuthState.Error).exception + assertThat(emitted).isInstanceOf(AuthException.MisconfigurationException::class.java) + assertThat( + getRecoveryMessage( + emitted as AuthException, + DefaultAuthUIStringProvider(applicationContext) + ) + ).doesNotContain("Firebase") + } } diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt index c095131a13..d1b70a08b5 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt @@ -4,6 +4,7 @@ import com.firebase.ui.auth.AuthException import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider import com.google.common.truth.Truth import com.google.firebase.auth.EmailAuthProvider +import com.google.firebase.auth.FirebaseAuthException import com.google.firebase.auth.GoogleAuthProvider import org.junit.Test import org.junit.runner.RunWith @@ -41,9 +42,22 @@ class ErrorRecoveryDialogLogicTest { // ============================================================================================= @Test - fun `getRecoveryMessage returns network error message for NetworkException`() { + fun `getRecoveryMessage prefers the library-owned message for NetworkException`() { + // Arrange - AuthException.from now puts the configured provider's string on the exception, + // so discarding it here would throw away the host's own translated copy. + val error = AuthException.NetworkException("Pas de connexion Internet") + + // Act + val message = getRecoveryMessage(error, mockStringProvider) + + // Assert + Truth.assertThat(message).isEqualTo("Pas de connexion Internet") + } + + @Test + fun `getRecoveryMessage returns network error message for NetworkException with blank message`() { // Arrange - val error = AuthException.NetworkException("Network error") + val error = AuthException.NetworkException("") // Act val message = getRecoveryMessage(error, mockStringProvider) @@ -77,15 +91,16 @@ class ErrorRecoveryDialogLogicTest { } @Test - fun `getRecoveryMessage returns generic message for InvalidCredentialsException with generic error text`() { - // Arrange - When error message is the generic fallback + fun `getRecoveryMessage shows the hardcoded fallback text for InvalidCredentialsException`() { + // Arrange - The old sentinel dropped this exact string on the floor. It could never match + // real traffic anyway: the SDK formats every message as " [ ]". val error = AuthException.InvalidCredentialsException("Invalid credentials provided") // Act val message = getRecoveryMessage(error, mockStringProvider) - // Assert - Should show the localized generic message - Truth.assertThat(message).isEqualTo("Incorrect password.") + // Assert + Truth.assertThat(message).isEqualTo("Invalid credentials provided") } @Test @@ -101,9 +116,21 @@ class ErrorRecoveryDialogLogicTest { } @Test - fun `getRecoveryMessage returns user not found message for UserNotFoundException`() { + fun `getRecoveryMessage prefers the library-owned message for UserNotFoundException`() { // Arrange - val error = AuthException.UserNotFoundException("User not found") + val error = AuthException.UserNotFoundException("Aucun compte pour cette adresse") + + // Act + val message = getRecoveryMessage(error, mockStringProvider) + + // Assert + Truth.assertThat(message).isEqualTo("Aucun compte pour cette adresse") + } + + @Test + fun `getRecoveryMessage returns user not found message for UserNotFoundException with blank message`() { + // Arrange + val error = AuthException.UserNotFoundException("") // Act val message = getRecoveryMessage(error, mockStringProvider) @@ -116,7 +143,7 @@ class ErrorRecoveryDialogLogicTest { fun `getRecoveryMessage returns weak password message with reason for WeakPasswordException`() { // Arrange val error = AuthException.WeakPasswordException( - "Password is too weak", + "", null, "Password should be at least 8 characters" ) @@ -124,14 +151,14 @@ class ErrorRecoveryDialogLogicTest { // Act val message = getRecoveryMessage(error, mockStringProvider) - // Assert + // Assert - blank message, so the provider string supplies the base and the reason is kept Truth.assertThat(message).isEqualTo("Password not strong enough. Use at least 6 characters and a mix of letters and numbers\n\nReason: Password should be at least 8 characters") } @Test fun `getRecoveryMessage returns weak password message without reason for WeakPasswordException`() { // Arrange - val error = AuthException.WeakPasswordException("Password is too weak", null, null) + val error = AuthException.WeakPasswordException("", null, null) // Act val message = getRecoveryMessage(error, mockStringProvider) @@ -144,7 +171,7 @@ class ErrorRecoveryDialogLogicTest { fun `getRecoveryMessage returns email already in use message with email for EmailAlreadyInUseException`() { // Arrange val error = AuthException.EmailAlreadyInUseException( - "Email already in use", + "", null, "test@example.com" ) @@ -152,14 +179,14 @@ class ErrorRecoveryDialogLogicTest { // Act val message = getRecoveryMessage(error, mockStringProvider) - // Assert + // Assert - blank message, so the provider string supplies the base and the email is kept Truth.assertThat(message).isEqualTo("Email account registration unsuccessful (test@example.com)") } @Test fun `getRecoveryMessage returns email already in use message without email for EmailAlreadyInUseException`() { // Arrange - val error = AuthException.EmailAlreadyInUseException("Email already in use", null, null) + val error = AuthException.EmailAlreadyInUseException("", null, null) // Act val message = getRecoveryMessage(error, mockStringProvider) @@ -168,6 +195,103 @@ class ErrorRecoveryDialogLogicTest { Truth.assertThat(message).isEqualTo("Email account registration unsuccessful") } + // ============================================================================================= + // Misconfiguration — the one message that is never rendered + // ============================================================================================= + + @Test + fun `getRecoveryMessage never shows the raw message for MisconfigurationException`() { + // Arrange - exactly what firebase-auth 24.2.0 puts on a disabled sign-in provider. It is + // untranslated, it names the Firebase console, and the user can do nothing with it. + val rawDiagnostic = "This operation is not allowed. This may be because the given sign-in " + + "provider is disabled for this Firebase project. Enable it in the Firebase " + + "console, under the sign-in method tab of the Auth section. [ OPERATION_NOT_ALLOWED ]" + val error = AuthException.MisconfigurationException(rawDiagnostic) + + // Act + val message = getRecoveryMessage(error, mockStringProvider) + + // Assert + Truth.assertThat(message).isEqualTo("An unknown error occurred.") + Truth.assertThat(message).doesNotContain("Firebase") + Truth.assertThat(message).doesNotContain("OPERATION_NOT_ALLOWED") + } + + @Test + fun `MisconfigurationException from() keeps the raw diagnostic on the cause, not the message`() { + // EmailAuthScreen and PhoneAuthScreen render `exception.message` inline without going + // through getRecoveryMessage, so the diagnostic has to be off the message entirely. + val rawDiagnostic = "The supplied auth credential is malformed. [ INVALID_CERT_HASH ]" + val firebaseException = + object : FirebaseAuthException("ERROR_INVALID_CERT_HASH", rawDiagnostic) {} + + val error = AuthException.from(firebaseException, mockStringProvider) + + Truth.assertThat(error).isInstanceOf(AuthException.MisconfigurationException::class.java) + Truth.assertThat(error.message).isEqualTo("An unknown error occurred.") + Truth.assertThat(error.cause).isEqualTo(firebaseException) + Truth.assertThat(error.cause?.message).isEqualTo(rawDiagnostic) + } + + @Test + fun `isRecoverable returns false for MisconfigurationException`() { + val error = AuthException.MisconfigurationException("Unauthorized domain") + + Truth.assertThat(isRecoverable(error)).isFalse() + } + + // ============================================================================================= + // Subtypes whose arms used to discard error.message outright + // ============================================================================================= + + @Test + fun `getRecoveryMessage prefers the library-owned message for TooManyRequestsException`() { + val error = AuthException.TooManyRequestsException("Trop de tentatives") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Trop de tentatives") + } + + @Test + fun `getRecoveryMessage returns the recovery string for TooManyRequestsException with blank message`() { + val error = AuthException.TooManyRequestsException("") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("This phone number has been used too many times") + } + + @Test + fun `getRecoveryMessage prefers the library-owned message for MfaRequiredException`() { + val error = AuthException.MfaRequiredException("Vérification supplémentaire requise") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Vérification supplémentaire requise") + } + + @Test + fun `getRecoveryMessage returns the recovery string for MfaRequiredException with blank message`() { + val error = AuthException.MfaRequiredException("") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Additional verification required. Please complete multi-factor authentication.") + } + + @Test + fun `getRecoveryMessage prefers the library-owned message for AuthCancelledException`() { + val error = AuthException.AuthCancelledException("Connexion annulée") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Connexion annulée") + } + + @Test + fun `getRecoveryMessage returns the recovery string for AuthCancelledException with blank message`() { + val error = AuthException.AuthCancelledException("") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Authentication was cancelled. Please try again when ready.") + } + // ============================================================================================= // Recovery Action Text Tests // ============================================================================================= diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreenVerificationLifecycleTest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreenVerificationLifecycleTest.kt index f27c81110f..6a67f016b5 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreenVerificationLifecycleTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreenVerificationLifecycleTest.kt @@ -573,8 +573,9 @@ class PhoneAuthScreenVerificationLifecycleTest { settle() // The failure also tears down the verification, which must not append a second, - // spurious cancellation error behind the real one. - assertThat(reportedErrors.map { it.message }).containsExactly("sign-in blew up") + // spurious cancellation error behind the real one. AuthException.from replaces the + // message with renderable copy, so the original text is identified on the cause. + assertThat(reportedErrors.map { it.cause?.message }).containsExactly("sign-in blew up") } } From 81b95e7dff96141fcc88a768422183c661ff6e6c Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Fri, 11 Sep 2026 15:36:00 +0100 Subject: [PATCH 2/8] fix(auth): translate the strings left as English across 84 locales --- auth/src/main/res/values-ar/strings.xml | 12 ++++++- auth/src/main/res/values-b+es+419/strings.xml | 34 ++++++++++++------- auth/src/main/res/values-bg/strings.xml | 12 ++++++- auth/src/main/res/values-bn/strings.xml | 22 ++++++++---- auth/src/main/res/values-ca/strings.xml | 22 ++++++++---- auth/src/main/res/values-cs/strings.xml | 12 ++++++- auth/src/main/res/values-da/strings.xml | 12 ++++++- auth/src/main/res/values-de-rAT/strings.xml | 22 ++++++++---- auth/src/main/res/values-de-rCH/strings.xml | 22 ++++++++---- auth/src/main/res/values-de/strings.xml | 12 ++++++- auth/src/main/res/values-el/strings.xml | 22 ++++++++---- auth/src/main/res/values-en-rAU/strings.xml | 10 ++++++ auth/src/main/res/values-en-rCA/strings.xml | 10 ++++++ auth/src/main/res/values-en-rGB/strings.xml | 10 ++++++ auth/src/main/res/values-en-rIE/strings.xml | 10 ++++++ auth/src/main/res/values-en-rIN/strings.xml | 10 ++++++ auth/src/main/res/values-en-rSG/strings.xml | 10 ++++++ auth/src/main/res/values-en-rZA/strings.xml | 10 ++++++ auth/src/main/res/values-es-rAR/strings.xml | 12 ++++++- auth/src/main/res/values-es-rBO/strings.xml | 12 ++++++- auth/src/main/res/values-es-rCL/strings.xml | 12 ++++++- auth/src/main/res/values-es-rCO/strings.xml | 12 ++++++- auth/src/main/res/values-es-rCR/strings.xml | 12 ++++++- auth/src/main/res/values-es-rDO/strings.xml | 12 ++++++- auth/src/main/res/values-es-rEC/strings.xml | 12 ++++++- auth/src/main/res/values-es-rGT/strings.xml | 12 ++++++- auth/src/main/res/values-es-rHN/strings.xml | 12 ++++++- auth/src/main/res/values-es-rMX/strings.xml | 12 ++++++- auth/src/main/res/values-es-rNI/strings.xml | 12 ++++++- auth/src/main/res/values-es-rPA/strings.xml | 12 ++++++- auth/src/main/res/values-es-rPE/strings.xml | 12 ++++++- auth/src/main/res/values-es-rPR/strings.xml | 12 ++++++- auth/src/main/res/values-es-rPY/strings.xml | 12 ++++++- auth/src/main/res/values-es-rSV/strings.xml | 12 ++++++- auth/src/main/res/values-es-rUS/strings.xml | 12 ++++++- auth/src/main/res/values-es-rUY/strings.xml | 12 ++++++- auth/src/main/res/values-es-rVE/strings.xml | 12 ++++++- auth/src/main/res/values-es/strings.xml | 12 ++++++- auth/src/main/res/values-fa/strings.xml | 22 ++++++++---- auth/src/main/res/values-fi/strings.xml | 12 ++++++- auth/src/main/res/values-fil/strings.xml | 18 +++++++--- auth/src/main/res/values-fr-rCH/strings.xml | 22 ++++++++---- auth/src/main/res/values-fr/strings.xml | 12 ++++++- auth/src/main/res/values-gsw/strings.xml | 12 ++++++- auth/src/main/res/values-gu/strings.xml | 22 ++++++++---- auth/src/main/res/values-hi/strings.xml | 22 ++++++++---- auth/src/main/res/values-hr/strings.xml | 12 ++++++- auth/src/main/res/values-hu/strings.xml | 12 ++++++- auth/src/main/res/values-in/strings.xml | 26 +++++++++----- auth/src/main/res/values-it/strings.xml | 12 ++++++- auth/src/main/res/values-iw/strings.xml | 22 ++++++++---- auth/src/main/res/values-ja/strings.xml | 12 ++++++- auth/src/main/res/values-kn/strings.xml | 22 ++++++++---- auth/src/main/res/values-ko/strings.xml | 12 ++++++- auth/src/main/res/values-ln/strings.xml | 22 ++++++++---- auth/src/main/res/values-lt/strings.xml | 22 ++++++++---- auth/src/main/res/values-lv/strings.xml | 22 ++++++++---- auth/src/main/res/values-mo/strings.xml | 22 ++++++++---- auth/src/main/res/values-mr/strings.xml | 22 ++++++++---- auth/src/main/res/values-ms/strings.xml | 22 ++++++++---- auth/src/main/res/values-nb/strings.xml | 12 ++++++- auth/src/main/res/values-nl/strings.xml | 12 ++++++- auth/src/main/res/values-no/strings.xml | 22 ++++++++---- auth/src/main/res/values-pl/strings.xml | 12 ++++++- auth/src/main/res/values-pt-rBR/strings.xml | 22 ++++++++---- auth/src/main/res/values-pt-rPT/strings.xml | 22 ++++++++---- auth/src/main/res/values-pt/strings.xml | 12 ++++++- auth/src/main/res/values-ro/strings.xml | 12 ++++++- auth/src/main/res/values-ru/strings.xml | 12 ++++++- auth/src/main/res/values-sk/strings.xml | 12 ++++++- auth/src/main/res/values-sl/strings.xml | 22 ++++++++---- auth/src/main/res/values-sr/strings.xml | 22 ++++++++---- auth/src/main/res/values-sv/strings.xml | 12 ++++++- auth/src/main/res/values-ta/strings.xml | 22 ++++++++---- auth/src/main/res/values-th/strings.xml | 22 ++++++++---- auth/src/main/res/values-tl/strings.xml | 18 +++++++--- auth/src/main/res/values-tr/strings.xml | 22 ++++++++---- auth/src/main/res/values-uk/strings.xml | 22 ++++++++---- auth/src/main/res/values-ur/strings.xml | 22 ++++++++---- auth/src/main/res/values-vi/strings.xml | 26 +++++++++----- auth/src/main/res/values-zh-rCN/strings.xml | 22 ++++++++---- auth/src/main/res/values-zh-rHK/strings.xml | 22 ++++++++---- auth/src/main/res/values-zh-rTW/strings.xml | 22 ++++++++---- auth/src/main/res/values-zh/strings.xml | 12 ++++++- 84 files changed, 1098 insertions(+), 258 deletions(-) diff --git a/auth/src/main/res/values-ar/strings.xml b/auth/src/main/res/values-ar/strings.xml index 8367da3751..8b5a1d3198 100755 --- a/auth/src/main/res/values-ar/strings.xml +++ b/auth/src/main/res/values-ar/strings.xml @@ -100,7 +100,7 @@ تمّ التحقّق تلقائيًا من رقم الهاتف. إعادة إرسال الرمز تأكيد ملكية رقم الهاتف - Use a different phone number + استخدام رقم هاتف آخر عند النقر على “%1$s”، قد يتمّ إرسال رسالة قصيرة SMS وقد يتمّ تطبيق رسوم الرسائل والبيانات. يشير النقر على "%1$s" إلى موافقتك على %2$s و%3$s. وقد يتمّ إرسال رسالة قصيرة كما قد تنطبق رسوم الرسائل والبيانات. خطأ في المصادقة @@ -175,4 +175,14 @@ المصادقة متعددة العوامل معطلة حاليًا + البريد الإلكتروني أو كلمة المرور غير صحيحة + لم تعد جلسة التحقق هذه صالحة. اطلب رمزًا جديدًا. + لم تكتمل عملية التحقق من رقم الهاتف. أعد المحاولة. + بيانات الاعتماد هذه تخص حسابًا آخر. + رقم الهاتف هذا غير مُعدّ للتحقق في هذا الحساب. + انتهت صلاحية جلسة تسجيل الدخول. سجِّل الدخول مرة أخرى للمتابعة. + لم يعد هذا الرابط صالحًا. اطلب رابطًا جديدًا. + تحقَّق من عنوان بريدك الإلكتروني قبل المتابعة. + طريقة التحقق هذه مُعدّة بالفعل في هذا الحساب. + لقد وصلت إلى الحد الأقصى لطرق التحقق في هذا الحساب. diff --git a/auth/src/main/res/values-b+es+419/strings.xml b/auth/src/main/res/values-b+es+419/strings.xml index 6039dc47f8..70bcc2cc6c 100755 --- a/auth/src/main/res/values-b+es+419/strings.xml +++ b/auth/src/main/res/values-b+es+419/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -110,17 +110,17 @@ La autenticación fue cancelada. Vuelva a intentarlo cuando esté listo. - Choose Authentication Method - Set Up SMS Verification - Set Up Authenticator App - Verify Your Code + Elige el método de autenticación + Configurar verificación por SMS + Configurar aplicación de autenticación + Verifica tu código - Select a second authentication method to secure your account - Enter your phone number to receive verification codes - Scan the QR code with your authenticator app - Enter the code sent to your phone - Enter the code from your authenticator app - Enter your verification code + Selecciona un segundo método de autenticación para proteger tu cuenta + Introduce tu número de teléfono para recibir códigos de verificación + Escanea el código QR con tu aplicación de autenticación + Introduce el código enviado a tu teléfono + Introduce el código de tu aplicación de autenticación + Introduce tu código de verificación Confirmar contraseña Las contraseñas no coinciden @@ -192,5 +192,15 @@ Reautenticar - Multi-factor authentication is currently disabled + La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-bg/strings.xml b/auth/src/main/res/values-bg/strings.xml index 479a681434..edac37ab7c 100755 --- a/auth/src/main/res/values-bg/strings.xml +++ b/auth/src/main/res/values-bg/strings.xml @@ -100,7 +100,7 @@ Телефонният номер е потвърден автоматично Повторно изпращане на кода Потвърждаване на телефонния номер - Use a different phone number + Използване на друг телефонен номер Докосвайки „%1$s“, може да получите SMS съобщение. То може да се таксува по тарифите за данни и SMS. Докосвайки „%1$s", приемате нашите %2$s и %3$s. Възможно е да получите SMS съобщение. То може да се таксува по тарифите за данни и SMS. Грешка при удостоверяване @@ -175,4 +175,14 @@ Многофакторната автентификация в момента е деактивирана + Имейл адресът или паролата не са правилни + Тази сесия за потвърждаване вече не е валидна. Заявете нов код. + Потвърждаването по телефон не завърши. Опитайте отново. + Тези идентификационни данни принадлежат на друг профил. + Този телефонен номер не е настроен за потвърждаване в този профил. + Сесията ви за вход изтече. Влезте отново, за да продължите. + Тази връзка вече не е валидна. Заявете нова. + Потвърдете имейл адреса си, преди да продължите. + Този метод за потвърждаване вече е настроен в този профил. + Достигнахте ограничението за методи за потвърждаване в този профил. diff --git a/auth/src/main/res/values-bn/strings.xml b/auth/src/main/res/values-bn/strings.xml index 6b0870da65..adf5a41c24 100755 --- a/auth/src/main/res/values-bn/strings.xml +++ b/auth/src/main/res/values-bn/strings.xml @@ -100,15 +100,15 @@ ফোন নম্বরটি নিজে থেকে যাচাই করা হয়েছে কোডটি আবার পাঠান ফোন নম্বর যাচাই করুন - Use a different phone number + অন্য একটি ফোন নম্বর ব্যবহার করুন %1$s এ ট্যাপ করলে আপনি একটি এসএমএস পাঠাতে পারেন। মেসেজ ও ডেটার চার্জ প্রযোজ্য। “%1$s” বোতামে ট্যাপ করার অর্থ, আপনি আমাদের %2$s এবং %3$s-এর সাথে সম্মত। একটি এসএমএস পাঠানো হতে পারে। মেসেজ এবং ডেটার উপরে প্রযোজ্য চার্জ লাগতে পারে। - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + প্রমাণীকরণ সংক্রান্ত সমস্যা + আবার চেষ্টা করুন + অতিরিক্ত যাচাইকরণ প্রয়োজন। অনুগ্রহ করে মাল্টি-ফ্যাক্টর প্রমাণীকরণ সম্পূর্ণ করুন। + অ্যাকাউন্ট লিঙ্ক করা প্রয়োজন। অনুগ্রহ করে অন্য কোনও সাইন-ইন পদ্ধতি ব্যবহার করে দেখুন। + প্রমাণীকরণ বাতিল করা হয়েছে। আপনি প্রস্তুত হলে আবার চেষ্টা করুন। প্রমাণীকরণ পদ্ধতি চয়ন করুন @@ -176,4 +176,14 @@ মাল্টি-ফ্যাক্টর প্রমাণীকরণ বর্তমানে নিষ্ক্রিয় + ইমেল বা পাসওয়ার্ড সঠিক নয় + এই যাচাইকরণ সেশনটি আর বৈধ নেই। নতুন কোডের জন্য অনুরোধ করুন। + ফোন যাচাইকরণ সম্পূর্ণ হয়নি। আবার চেষ্টা করুন। + এই ক্রেডেনশিয়াল অন্য একটি অ্যাকাউন্টের। + এই অ্যাকাউন্টে যাচাইকরণের জন্য এই ফোন নম্বরটি সেট আপ করা নেই। + আপনার সাইন-ইন সেশনের মেয়াদ শেষ হয়ে গেছে। চালিয়ে যেতে আবার সাইন-ইন করুন। + এই লিঙ্কটি আর বৈধ নয়। নতুন একটির জন্য অনুরোধ করুন। + চালিয়ে যাওয়ার আগে আপনার ইমেল অ্যাড্রেস যাচাই করুন। + এই যাচাইকরণ পদ্ধতিটি এই অ্যাকাউন্টে ইতিমধ্যেই সেট আপ করা আছে। + এই অ্যাকাউন্টে যাচাইকরণ পদ্ধতির সীমায় আপনি পৌঁছে গেছেন। diff --git a/auth/src/main/res/values-ca/strings.xml b/auth/src/main/res/values-ca/strings.xml index e457554c9d..fcf65fa867 100755 --- a/auth/src/main/res/values-ca/strings.xml +++ b/auth/src/main/res/values-ca/strings.xml @@ -100,15 +100,15 @@ El número de telèfon s\'ha verificat automàticament Torna a enviar el codi Verifica el número de telèfon - Use a different phone number + Utilitza un altre número de telèfon En tocar %1$s, és possible que s\'enviï un SMS. Es poden aplicar tarifes de dades i missatges. En tocar %1$s, acceptes les nostres %2$s i la nostra %3$s. És possible que s\'enviï un SMS. Es poden aplicar tarifes de dades i missatges. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Error d\'autenticació + Torna-ho a provar + Cal una verificació addicional. Completeu l\'autenticació multifactor. + Cal enllaçar el compte. Proveu un altre mètode d\'inici de sessió. + L\'autenticació s\'ha cancel·lat. Torneu-ho a provar quan estigueu a punt. Trieu el mètode d\'autenticació @@ -176,4 +176,14 @@ L\'autenticació multifactor està desactivada actualment + Aquest correu electrònic o aquesta contrasenya no són correctes + Aquesta sessió de verificació ja no és vàlida. Sol·licita un codi nou. + La verificació per telèfon no s\'ha completat. Torna-ho a provar. + Aquestes credencials pertanyen a un altre compte. + Aquest número de telèfon no està configurat per a la verificació en aquest compte. + La teva sessió ha caducat. Torna a iniciar la sessió per continuar. + Aquest enllaç ja no és vàlid. Sol·licita\'n un de nou. + Verifica la teva adreça electrònica abans de continuar. + Aquest mètode de verificació ja està configurat en aquest compte. + Has assolit el límit de mètodes de verificació d\'aquest compte. diff --git a/auth/src/main/res/values-cs/strings.xml b/auth/src/main/res/values-cs/strings.xml index 64e5b18c18..e606edf054 100755 --- a/auth/src/main/res/values-cs/strings.xml +++ b/auth/src/main/res/values-cs/strings.xml @@ -100,7 +100,7 @@ Telefonní číslo bylo automaticky ověřeno Znovu poslat kód Ověřit telefonní číslo - Use a different phone number + Použít jiné telefonní číslo Po klepnutí na možnost %1$s může být odeslána SMS. Mohou být účtovány poplatky za zprávy a data. Klepnutím na tlačítko %1$s vyjadřujete svůj souhlas s dokumenty %2$s a %3$s. Může být odeslána SMS a mohou být účtovány poplatky za zprávy a data. Chyba ověření @@ -175,4 +175,14 @@ Vícefaktorové ověřování je aktuálně zakázáno + E-mail nebo heslo nejsou správné + Tato ověřovací relace už není platná. Vyžádejte si nový kód. + Ověření telefonu se nedokončilo. Zkuste to znovu. + Tyto přihlašovací údaje patří k jinému účtu. + Toto telefonní číslo není u tohoto účtu nastaveno pro ověřování. + Platnost vašeho přihlášení vypršela. Pokračujte opětovným přihlášením. + Tento odkaz už není platný. Vyžádejte si nový. + Před pokračováním ověřte svou e-mailovou adresu. + Tento způsob ověření je u tohoto účtu už nastavený. + Dosáhli jste limitu způsobů ověření pro tento účet. diff --git a/auth/src/main/res/values-da/strings.xml b/auth/src/main/res/values-da/strings.xml index 52c217d8a9..70d0324ec9 100755 --- a/auth/src/main/res/values-da/strings.xml +++ b/auth/src/main/res/values-da/strings.xml @@ -100,7 +100,7 @@ Telefonnummeret blev bekræftet automatisk Send koden igen Bekræft telefonnummer - Use a different phone number + Brug et andet telefonnummer Når du trykker på “%1$s”, sendes der måske en sms. Der opkræves muligvis gebyrer for beskeder og data. Når du trykker på "%1$s", indikerer du, at du accepterer vores %2$s og %3$s. Der sendes måske en sms. Der opkræves muligvis gebyrer for beskeder og data. Godkendelsesfejl @@ -175,4 +175,14 @@ Multifaktorgodkendelse er i øjeblikket deaktiveret + Mailadressen eller adgangskoden er ikke korrekt + Denne bekræftelsessession er ikke længere gyldig. Anmod om en ny kode. + Telefonbekræftelsen blev ikke fuldført. Prøv igen. + Disse loginoplysninger tilhører en anden konto. + Dette telefonnummer er ikke konfigureret til bekræftelse på denne konto. + Din loginsession er udløbet. Log ind igen for at fortsætte. + Dette link er ikke længere gyldigt. Anmod om et nyt. + Bekræft din mailadresse, før du fortsætter. + Denne bekræftelsesmetode er allerede konfigureret på denne konto. + Du har nået grænsen for bekræftelsesmetoder på denne konto. diff --git a/auth/src/main/res/values-de-rAT/strings.xml b/auth/src/main/res/values-de-rAT/strings.xml index ce10dd5d0d..3889d18368 100755 --- a/auth/src/main/res/values-de-rAT/strings.xml +++ b/auth/src/main/res/values-de-rAT/strings.xml @@ -100,14 +100,14 @@ Telefonnummer wurde automatisch bestätigt Code erneut senden Telefonnummer bestätigen - Use a different phone number + Eine andere Telefonnummer verwenden Wenn Sie auf “%1$s” tippen, erhalten Sie möglicherweise eine SMS. Es können Gebühren für SMS und Datenübertragung anfallen. Indem Sie auf “%1$s” tippen, stimmen Sie unseren %2$s und unserer %3$s zu. Sie erhalten möglicherweise eine SMS und es können Gebühren für die Nachricht und die Datenübertragung anfallen. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Authentifizierungsfehler + Erneut versuchen + Zusätzliche Verifizierung erforderlich. Bitte schließen Sie die Multi-Faktor-Authentifizierung ab. + Das Konto muss verknüpft werden. Bitte versuchen Sie eine andere Anmeldemethode. + Die Authentifizierung wurde abgebrochen. Bitte versuchen Sie es erneut, wenn Sie bereit sind. Authentifizierungsmethode auswählen @@ -193,4 +193,14 @@ Die Multi-Faktor-Authentifizierung ist derzeit deaktiviert + E-Mail-Adresse oder Passwort ist nicht korrekt + Diese Bestätigungssitzung ist nicht mehr gültig. Fordern Sie einen neuen Code an. + Die Telefonbestätigung wurde nicht abgeschlossen. Versuchen Sie es erneut. + Diese Anmeldedaten gehören zu einem anderen Konto. + Diese Telefonnummer ist für dieses Konto nicht zur Bestätigung eingerichtet. + Ihre Anmeldesitzung ist abgelaufen. Melden Sie sich erneut an, um fortzufahren. + Dieser Link ist nicht mehr gültig. Fordern Sie einen neuen an. + Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. + Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. + Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. diff --git a/auth/src/main/res/values-de-rCH/strings.xml b/auth/src/main/res/values-de-rCH/strings.xml index f2b070a17c..e08e0e1642 100755 --- a/auth/src/main/res/values-de-rCH/strings.xml +++ b/auth/src/main/res/values-de-rCH/strings.xml @@ -100,15 +100,15 @@ Telefonnummer wurde automatisch bestätigt Code erneut senden Telefonnummer bestätigen - Use a different phone number + Eine andere Telefonnummer verwenden Wenn Sie auf “%1$s” tippen, erhalten Sie möglicherweise eine SMS. Es können Gebühren für SMS und Datenübertragung anfallen. Indem Sie auf “%1$s” tippen, stimmen Sie unseren %2$s und unserer %3$s zu. Sie erhalten möglicherweise eine SMS und es können Gebühren für die Nachricht und die Datenübertragung anfallen. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Authentifizierungsfehler + Erneut versuchen + Zusätzliche Verifizierung erforderlich. Bitte schließen Sie die Multi-Faktor-Authentifizierung ab. + Das Konto muss verknüpft werden. Bitte versuchen Sie eine andere Anmeldemethode. + Die Authentifizierung wurde abgebrochen. Bitte versuchen Sie es erneut, wenn Sie bereit sind. Authentifizierungsmethode auswählen @@ -194,4 +194,14 @@ Die Multi-Faktor-Authentifizierung ist derzeit deaktiviert + E-Mail-Adresse oder Passwort ist nicht korrekt + Diese Bestätigungssitzung ist nicht mehr gültig. Fordern Sie einen neuen Code an. + Die Telefonbestätigung wurde nicht abgeschlossen. Versuchen Sie es erneut. + Diese Anmeldedaten gehören zu einem anderen Konto. + Diese Telefonnummer ist für dieses Konto nicht zur Bestätigung eingerichtet. + Ihre Anmeldesitzung ist abgelaufen. Melden Sie sich erneut an, um fortzufahren. + Dieser Link ist nicht mehr gültig. Fordern Sie einen neuen an. + Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. + Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. + Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. diff --git a/auth/src/main/res/values-de/strings.xml b/auth/src/main/res/values-de/strings.xml index 9fe8f66f83..5646f17a32 100755 --- a/auth/src/main/res/values-de/strings.xml +++ b/auth/src/main/res/values-de/strings.xml @@ -100,7 +100,7 @@ Telefonnummer wurde automatisch bestätigt Code erneut senden Telefonnummer bestätigen - Use a different phone number + Eine andere Telefonnummer verwenden Wenn Sie auf “%1$s” tippen, erhalten Sie möglicherweise eine SMS. Es können Gebühren für SMS und Datenübertragung anfallen. Indem Sie auf "%1$s" tippen, stimmen Sie unseren %2$s und unserer %3$s zu. Sie erhalten möglicherweise eine SMS und es können Gebühren für die Nachricht und die Datenübertragung anfallen. Authentifizierungsfehler @@ -193,4 +193,14 @@ Die Multi-Faktor-Authentifizierung ist derzeit deaktiviert + E-Mail-Adresse oder Passwort ist nicht korrekt + Diese Bestätigungssitzung ist nicht mehr gültig. Fordern Sie einen neuen Code an. + Die Telefonbestätigung wurde nicht abgeschlossen. Versuchen Sie es erneut. + Diese Anmeldedaten gehören zu einem anderen Konto. + Diese Telefonnummer ist für dieses Konto nicht zur Bestätigung eingerichtet. + Ihre Anmeldesitzung ist abgelaufen. Melden Sie sich erneut an, um fortzufahren. + Dieser Link ist nicht mehr gültig. Fordern Sie einen neuen an. + Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. + Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. + Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. diff --git a/auth/src/main/res/values-el/strings.xml b/auth/src/main/res/values-el/strings.xml index 0854ec409f..d23e3d4379 100755 --- a/auth/src/main/res/values-el/strings.xml +++ b/auth/src/main/res/values-el/strings.xml @@ -100,15 +100,15 @@ Ο αριθμός τηλεφώνου επαληθεύτηκε αυτόματα Επανάληψη αποστολής κωδικού Επαλήθευση αριθμού τηλεφώνου - Use a different phone number + Χρήση διαφορετικού αριθμού τηλεφώνου Αν πατήσετε “%1$s”, μπορεί να σταλεί ένα SMS. Ενδέχεται να ισχύουν χρεώσεις μηνυμάτων και δεδομένων. Αν πατήσετε “%1$s”, δηλώνετε ότι αποδέχεστε τους %2$s και την %3$s. Μπορεί να σταλεί ένα SMS. Ενδέχεται να ισχύουν χρεώσεις μηνυμάτων και δεδομένων. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Σφάλμα ελέγχου ταυτότητας + Δοκιμάστε ξανά + Απαιτείται πρόσθετη επαλήθευση. Ολοκληρώστε τον έλεγχο ταυτότητας πολλαπλών παραγόντων. + Ο λογαριασμός πρέπει να συνδεθεί. Δοκιμάστε διαφορετική μέθοδο σύνδεσης. + Ο έλεγχος ταυτότητας ακυρώθηκε. Δοκιμάστε ξανά όταν είστε έτοιμοι. Επιλέξτε μέθοδο ελέγχου ταυτότητας @@ -176,4 +176,14 @@ Ο έλεγχος ταυτότητας πολλαπλών παραγόντων είναι απενεργοποιημένος προς το παρόν + Το ηλεκτρονικό ταχυδρομείο ή ο κωδικός πρόσβασης δεν είναι σωστά + Αυτή η περίοδος επαλήθευσης δεν είναι πλέον έγκυρη. Ζητήστε νέο κωδικό. + Η επαλήθευση τηλεφώνου δεν ολοκληρώθηκε. Δοκιμάστε ξανά. + Αυτά τα διαπιστευτήρια ανήκουν σε διαφορετικό λογαριασμό. + Αυτός ο αριθμός τηλεφώνου δεν έχει ρυθμιστεί για επαλήθευση σε αυτόν τον λογαριασμό. + Η περίοδος σύνδεσής σας έληξε. Συνδεθείτε ξανά για να συνεχίσετε. + Αυτός ο σύνδεσμος δεν είναι πλέον έγκυρος. Ζητήστε νέον. + Επαληθεύστε τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας πριν συνεχίσετε. + Αυτή η μέθοδος επαλήθευσης έχει ήδη ρυθμιστεί σε αυτόν τον λογαριασμό. + Έχετε φτάσει το όριο μεθόδων επαλήθευσης για αυτόν τον λογαριασμό. diff --git a/auth/src/main/res/values-en-rAU/strings.xml b/auth/src/main/res/values-en-rAU/strings.xml index 7f8dab7e63..ec70afe9b4 100755 --- a/auth/src/main/res/values-en-rAU/strings.xml +++ b/auth/src/main/res/values-en-rAU/strings.xml @@ -175,4 +175,14 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. diff --git a/auth/src/main/res/values-en-rCA/strings.xml b/auth/src/main/res/values-en-rCA/strings.xml index 2476ca4b8b..4c5d7342ea 100755 --- a/auth/src/main/res/values-en-rCA/strings.xml +++ b/auth/src/main/res/values-en-rCA/strings.xml @@ -175,4 +175,14 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. diff --git a/auth/src/main/res/values-en-rGB/strings.xml b/auth/src/main/res/values-en-rGB/strings.xml index 8c19093629..5b0fae94b3 100755 --- a/auth/src/main/res/values-en-rGB/strings.xml +++ b/auth/src/main/res/values-en-rGB/strings.xml @@ -175,4 +175,14 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. diff --git a/auth/src/main/res/values-en-rIE/strings.xml b/auth/src/main/res/values-en-rIE/strings.xml index ee4d48e585..4bfb4a3987 100755 --- a/auth/src/main/res/values-en-rIE/strings.xml +++ b/auth/src/main/res/values-en-rIE/strings.xml @@ -168,4 +168,14 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. diff --git a/auth/src/main/res/values-en-rIN/strings.xml b/auth/src/main/res/values-en-rIN/strings.xml index ee4d48e585..4bfb4a3987 100755 --- a/auth/src/main/res/values-en-rIN/strings.xml +++ b/auth/src/main/res/values-en-rIN/strings.xml @@ -168,4 +168,14 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. diff --git a/auth/src/main/res/values-en-rSG/strings.xml b/auth/src/main/res/values-en-rSG/strings.xml index ee4d48e585..4bfb4a3987 100755 --- a/auth/src/main/res/values-en-rSG/strings.xml +++ b/auth/src/main/res/values-en-rSG/strings.xml @@ -168,4 +168,14 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. diff --git a/auth/src/main/res/values-en-rZA/strings.xml b/auth/src/main/res/values-en-rZA/strings.xml index ee4d48e585..4bfb4a3987 100755 --- a/auth/src/main/res/values-en-rZA/strings.xml +++ b/auth/src/main/res/values-en-rZA/strings.xml @@ -168,4 +168,14 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. diff --git a/auth/src/main/res/values-es-rAR/strings.xml b/auth/src/main/res/values-es-rAR/strings.xml index 6e6ec21946..ca2ef55622 100755 --- a/auth/src/main/res/values-es-rAR/strings.xml +++ b/auth/src/main/res/values-es-rAR/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rBO/strings.xml b/auth/src/main/res/values-es-rBO/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rBO/strings.xml +++ b/auth/src/main/res/values-es-rBO/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rCL/strings.xml b/auth/src/main/res/values-es-rCL/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rCL/strings.xml +++ b/auth/src/main/res/values-es-rCL/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rCO/strings.xml b/auth/src/main/res/values-es-rCO/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rCO/strings.xml +++ b/auth/src/main/res/values-es-rCO/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rCR/strings.xml b/auth/src/main/res/values-es-rCR/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rCR/strings.xml +++ b/auth/src/main/res/values-es-rCR/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rDO/strings.xml b/auth/src/main/res/values-es-rDO/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rDO/strings.xml +++ b/auth/src/main/res/values-es-rDO/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rEC/strings.xml b/auth/src/main/res/values-es-rEC/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rEC/strings.xml +++ b/auth/src/main/res/values-es-rEC/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rGT/strings.xml b/auth/src/main/res/values-es-rGT/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rGT/strings.xml +++ b/auth/src/main/res/values-es-rGT/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rHN/strings.xml b/auth/src/main/res/values-es-rHN/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rHN/strings.xml +++ b/auth/src/main/res/values-es-rHN/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rMX/strings.xml b/auth/src/main/res/values-es-rMX/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rMX/strings.xml +++ b/auth/src/main/res/values-es-rMX/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rNI/strings.xml b/auth/src/main/res/values-es-rNI/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rNI/strings.xml +++ b/auth/src/main/res/values-es-rNI/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rPA/strings.xml b/auth/src/main/res/values-es-rPA/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rPA/strings.xml +++ b/auth/src/main/res/values-es-rPA/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rPE/strings.xml b/auth/src/main/res/values-es-rPE/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rPE/strings.xml +++ b/auth/src/main/res/values-es-rPE/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rPR/strings.xml b/auth/src/main/res/values-es-rPR/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rPR/strings.xml +++ b/auth/src/main/res/values-es-rPR/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rPY/strings.xml b/auth/src/main/res/values-es-rPY/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rPY/strings.xml +++ b/auth/src/main/res/values-es-rPY/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rSV/strings.xml b/auth/src/main/res/values-es-rSV/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rSV/strings.xml +++ b/auth/src/main/res/values-es-rSV/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rUS/strings.xml b/auth/src/main/res/values-es-rUS/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rUS/strings.xml +++ b/auth/src/main/res/values-es-rUS/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rUY/strings.xml b/auth/src/main/res/values-es-rUY/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rUY/strings.xml +++ b/auth/src/main/res/values-es-rUY/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es-rVE/strings.xml b/auth/src/main/res/values-es-rVE/strings.xml index f95dda99e2..6dd455cd7e 100755 --- a/auth/src/main/res/values-es-rVE/strings.xml +++ b/auth/src/main/res/values-es-rVE/strings.xml @@ -100,7 +100,7 @@ Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación @@ -186,4 +186,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-es/strings.xml b/auth/src/main/res/values-es/strings.xml index ac5767d33a..79327ae560 100755 --- a/auth/src/main/res/values-es/strings.xml +++ b/auth/src/main/res/values-es/strings.xml @@ -100,7 +100,7 @@ Se ha verificado automáticamente el número de teléfono Volver a enviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Al tocar %1$s, podría enviarse un SMS. Es posible que se apliquen cargos de mensajería y de uso de datos. Si tocas %1$s, confirmas que aceptas nuestras %2$s y nuestra %3$s. Podría enviarse un SMS, por lo que es posible que se apliquen cargos de mensajería y de uso de datos. Error de autenticación @@ -193,4 +193,14 @@ La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + La verificación telefónica no se ha completado. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión ha caducado. Vuelve a iniciar sesión para continuar. + Este enlace ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Has alcanzado el límite de métodos de verificación de esta cuenta. diff --git a/auth/src/main/res/values-fa/strings.xml b/auth/src/main/res/values-fa/strings.xml index a7f6c74701..3805f2ea88 100755 --- a/auth/src/main/res/values-fa/strings.xml +++ b/auth/src/main/res/values-fa/strings.xml @@ -100,15 +100,15 @@ شماره تلفن به‌طور خودکار به‌تأیید رسید ارسال مجدد کد تأیید شماره تلفن - Use a different phone number + استفاده از شماره تلفن دیگر با ضربه زدن روی «%1$s»، پیامکی برایتان ارسال می‌شود. هزینه پیام و داده اعمال می‌شود. درصورت ضربه‌زدن روی «%1$s»، موافقتتان را با %2$s و %3$s اعلام می‌کنید. پیامکی ارسال می‌شود. ممکن است هزینه داده و «پیام» محاسبه شود. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + خطای احراز هویت + دوباره امتحان کنید + تأیید بیشتری لازم است. لطفاً احراز هویت چندعاملی را کامل کنید. + حساب باید پیوند داده شود. لطفاً روش ورود دیگری را امتحان کنید. + احراز هویت لغو شد. هروقت آماده بودید، دوباره امتحان کنید. روش احراز هویت را انتخاب کنید @@ -176,4 +176,14 @@ احراز هویت چند مرحله‌ای در حال حاضر غیرفعال است + آن ایمیل یا گذرواژه درست نیست + این جلسه تأیید دیگر معتبر نیست. کد جدیدی درخواست کنید. + تأیید شماره تلفن کامل نشد. دوباره امتحان کنید. + این اطلاعات ورود به حساب دیگری تعلق دارد. + این شماره تلفن برای تأیید در این حساب تنظیم نشده است. + جلسه ورود به سیستم شما منقضی شد. برای ادامه، دوباره وارد سیستم شوید. + این پیوند دیگر معتبر نیست. پیوند جدیدی درخواست کنید. + پیش از ادامه، نشانی ایمیلتان را تأیید کنید. + این روش تأیید قبلاً در این حساب تنظیم شده است. + به حداکثر تعداد روش‌های تأیید در این حساب رسیده‌اید. diff --git a/auth/src/main/res/values-fi/strings.xml b/auth/src/main/res/values-fi/strings.xml index 1dbdcef0d0..38346777c6 100755 --- a/auth/src/main/res/values-fi/strings.xml +++ b/auth/src/main/res/values-fi/strings.xml @@ -100,7 +100,7 @@ Puhelinnumero vahvistettu automaattisesti Lähetä koodi uudelleen Vahvista puhelinnumero - Use a different phone number + Käytä toista puhelinnumeroa Kun napautat %1$s, tekstiviesti voidaan lähettää. Datan ja viestien käyttö voi olla maksullista. Napauttamalla %1$s vahvistat hyväksyväsi seuraavat: %2$s ja %3$s. Tekstiviesti voidaan lähettää, ja datan ja viestien käyttö voi olla maksullista. Todennusvirhe @@ -175,4 +175,14 @@ Monivaiheinen todennus on tällä hetkellä poistettu käytöstä + Sähköposti tai salasana on virheellinen + Tämä vahvistusistunto ei ole enää voimassa. Pyydä uusi koodi. + Puhelinvahvistus ei valmistunut. Yritä uudelleen. + Nämä tunnistetiedot kuuluvat toiselle tilille. + Tätä puhelinnumeroa ei ole määritetty vahvistukseen tällä tilillä. + Kirjautumisistuntosi vanheni. Kirjaudu sisään uudelleen jatkaaksesi. + Tämä linkki ei ole enää voimassa. Pyydä uusi. + Vahvista sähköpostiosoitteesi ennen kuin jatkat. + Tämä vahvistustapa on jo määritetty tällä tilillä. + Olet saavuttanut tämän tilin vahvistustapojen enimmäismäärän. diff --git a/auth/src/main/res/values-fil/strings.xml b/auth/src/main/res/values-fil/strings.xml index 39b866524a..2cc95855f0 100755 --- a/auth/src/main/res/values-fil/strings.xml +++ b/auth/src/main/res/values-fil/strings.xml @@ -100,7 +100,7 @@ Awtomatikong na-verify ang numero ng telepono Ipadala Muli ang Code I-verify ang Numero ng Telepono - Use a different phone number + Gumamit ng ibang numero ng telepono Sa pag-tap sa “%1$s,“ maaaring magpadala ng SMS. Maaaring ipatupad ang mga rate ng pagmemensahe at data. Sa pag-tap sa “%1$s”, ipinababatid mo na tinatanggap mo ang aming %2$s at %3$s. Maaaring magpadala ng SMS. Maaaring ipatupad ang mga rate ng pagmemensahe at data. Error sa Pagpapatotoo @@ -146,7 +146,7 @@ Pumili ng paraan ng pag-verify Magdagdag ng karagdagang layer ng seguridad SMS - Authenticator app + App ng authenticator Ang numerong ito ay nauugnay sa ibang account Kinakailangan ang pag-verify I-scan ang QR code gamit ang iyong authenticator app @@ -163,16 +163,26 @@ Mag-authenticate muli Alisin Ipadala muli ang verification email - Secret key + Sikretong key Mag-sign out Naka-sign in bilang Laktawan Gumamit ng ibang paraan - Verification code + Code sa pag-verify Na-verify ang email I-verify Nagpadala kami ng verification email sa %1$s Kasalukuyang naka-disable ang multi-factor authentication + Mali ang email o password na iyon + Wala nang bisa ang verification session na iyon. Humiling ng bagong code. + Hindi nakumpleto ang pag-verify ng telepono. Subukang muli. + Kabilang ang mga kredensyal na iyon sa ibang account. + Hindi naka-set up ang numero ng teleponong iyon para sa pag-verify sa account na ito. + Nag-expire na ang iyong sign-in session. Mag-sign in muli para magpatuloy. + Wala nang bisa ang link na iyon. Humiling ng bago. + I-verify ang iyong email address bago ka magpatuloy. + Naka-set up na ang paraan ng pag-verify na iyon sa account na ito. + Naabot mo na ang limitasyon para sa mga paraan ng pag-verify sa account na ito. diff --git a/auth/src/main/res/values-fr-rCH/strings.xml b/auth/src/main/res/values-fr-rCH/strings.xml index 447df07468..716165de41 100755 --- a/auth/src/main/res/values-fr-rCH/strings.xml +++ b/auth/src/main/res/values-fr-rCH/strings.xml @@ -100,15 +100,15 @@ Numéro de téléphone validé automatiquement Renvoyer le code Valider le numéro de téléphone - Use a different phone number + Utiliser un autre numéro de téléphone En appuyant sur “%1$s”, vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. En appuyant sur “%1$s”, vous acceptez les %2$s et les %3$s. Vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Erreur d\'authentification + Réessayer + Vérification supplémentaire requise. Veuillez compléter l\'authentification à plusieurs facteurs. + Le compte doit être lié. Veuillez essayer une méthode de connexion différente. + L\'authentification a été annulée. Veuillez réessayer quand vous serez prêt. Choisir la méthode d\'authentification @@ -187,4 +187,14 @@ L\'authentification multifacteur est actuellement désactivée + Cet e-mail ou ce mot de passe est incorrect + Cette session de vérification n\'est plus valide. Demandez un nouveau code. + La vérification du numéro de téléphone n\'a pas abouti. Veuillez réessayer. + Ces identifiants appartiennent à un autre compte. + Ce numéro de téléphone n\'est pas configuré comme méthode de vérification sur ce compte. + Votre session de connexion a expiré. Reconnectez-vous pour continuer. + Ce lien n\'est plus valide. Demandez-en un nouveau. + Veuillez vérifier votre adresse e-mail avant de continuer. + Cette méthode de vérification est déjà configurée sur ce compte. + Vous avez atteint la limite de méthodes de vérification pour ce compte. diff --git a/auth/src/main/res/values-fr/strings.xml b/auth/src/main/res/values-fr/strings.xml index e6e2d773ca..587e025d26 100755 --- a/auth/src/main/res/values-fr/strings.xml +++ b/auth/src/main/res/values-fr/strings.xml @@ -100,7 +100,7 @@ Numéro de téléphone validé automatiquement Renvoyer le code Valider le numéro de téléphone - Use a different phone number + Utiliser un autre numéro de téléphone En appuyant sur “%1$s”, vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. En appuyant sur "%1$s", vous acceptez les %2$s et les %3$s. Vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. Erreur d\'authentification @@ -193,4 +193,14 @@ L\'authentification multifacteur est actuellement désactivée + L\'adresse e-mail ou le mot de passe est incorrect + Cette session de vérification n\'est plus valide. Demandez un nouveau code. + La vérification du numéro de téléphone n\'a pas abouti. Réessayez. + Ces identifiants appartiennent à un autre compte. + Ce numéro de téléphone n\'est pas configuré pour la vérification sur ce compte. + Votre session de connexion a expiré. Reconnectez-vous pour continuer. + Ce lien n\'est plus valide. Demandez-en un nouveau. + Vérifiez votre adresse e-mail avant de continuer. + Cette méthode de vérification est déjà configurée sur ce compte. + Vous avez atteint la limite de méthodes de vérification pour ce compte. diff --git a/auth/src/main/res/values-gsw/strings.xml b/auth/src/main/res/values-gsw/strings.xml index ca2068e978..55f8cd02f3 100755 --- a/auth/src/main/res/values-gsw/strings.xml +++ b/auth/src/main/res/values-gsw/strings.xml @@ -100,7 +100,7 @@ Telefonnummer wurde automatisch bestätigt Code erneut senden Telefonnummer bestätigen - Use a different phone number + E anderi Telefonnummere verwände Wenn Sie auf “%1$s” tippen, erhalten Sie möglicherweise eine SMS. Es können Gebühren für SMS und Datenübertragung anfallen. Indem Sie auf “%1$s” tippen, stimmen Sie unseren %2$s und unserer %3$s zu. Sie erhalten möglicherweise eine SMS und es können Gebühren für die Nachricht und die Datenübertragung anfallen. Authentifizierungsfehler @@ -175,4 +175,14 @@ D\'Multi-Faktor-Authentifizierig isch zurziit deaktiviert + Die E-Mail-Adrässe oder s Passwort isch nöd richtig + Die Bestätigungssitzig isch nüme gültig. Fordere Sie e neue Code aa. + D\'Telefonbestätigung isch nöd abgschlosse worde. Versuche Sie es erneut. + Die Aamäldedate ghöre zunemene andere Konto. + Die Telefonnummere isch uf däm Konto nöd als Bestätigungsmethode iigrichtet. + Ihri Aamäldig isch abglaufe. Mälde Sie sich erneut aa, zum wiiterzmache. + De Link isch nüme gültig. Fordere Sie e neue aa. + Bestätige Sie Ihri E-Mail-Adrässe, bevor Sie wiitermache. + Die Bestätigungsmethode isch scho uf däm Konto iigrichtet. + Sie händ d\'Grenze für Bestätigungsmethode uf däm Konto erreicht. diff --git a/auth/src/main/res/values-gu/strings.xml b/auth/src/main/res/values-gu/strings.xml index 38258f327a..b87a35a0f4 100755 --- a/auth/src/main/res/values-gu/strings.xml +++ b/auth/src/main/res/values-gu/strings.xml @@ -100,15 +100,15 @@ ફોન નંબર આપમેળે ચકાસવામાં આવ્યો કોડ ફરીથી મોકલો ફોન નંબર ચકાસો - Use a different phone number + અલગ ફોન નંબરનો ઉપયોગ કરો “%1$s”ને ટૅપ કરવાથી, કદાચ એક SMS મોકલવામાં આવી શકે છે. સંદેશ અને ડેટા શુલ્ક લાગુ થઈ શકે છે. “%1$s” ટૅપ કરીને, તમે સૂચવી રહ્યાં છો કે તમે અમારી %2$s અને %3$sને સ્વીકારો છો. SMS મોકલવામાં આવી શકે છે. સંદેશ અને ડેટા શુલ્ક લાગુ થઈ શકે છે. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + પ્રમાણીકરણ ભૂલ + ફરી પ્રયાસ કરો + વધારાની ચકાસણી જરૂરી છે. કૃપા કરીને મલ્ટિ-ફૅક્ટર પ્રમાણીકરણ પૂર્ણ કરો. + એકાઉન્ટ લિંક કરવાની જરૂર છે. કૃપા કરીને કોઈ અલગ સાઇન ઇન પદ્ધતિ અજમાવો. + પ્રમાણીકરણ રદ કરવામાં આવ્યું. તમે તૈયાર હો ત્યારે ફરી પ્રયાસ કરો. પ્રમાણીકરણ પદ્ધતિ પસંદ કરો @@ -176,4 +176,14 @@ મલ્ટિ-ફેક્ટર પ્રમાણીકરણ હાલમાં અક્ષમ છે + તે ઇમેઇલ અથવા પાસવર્ડ સાચો નથી + તે ચકાસણી સત્ર હવે માન્ય નથી. નવા કોડની વિનંતી કરો. + ફોનની ચકાસણી પૂર્ણ થઈ નથી. ફરી પ્રયાસ કરો. + તે ઓળખપત્રો કોઈ અલગ એકાઉન્ટનાં છે. + તે ફોન નંબર આ એકાઉન્ટ પર ચકાસણી માટે સેટ કરેલો નથી. + તમારું સાઇન ઇન સત્ર સમાપ્ત થઈ ગયું છે. ચાલુ રાખવા માટે ફરી સાઇન ઇન કરો. + તે લિંક હવે માન્ય નથી. નવી લિંકની વિનંતી કરો. + તમે આગળ વધો તે પહેલાં તમારું ઇમેઇલ ઍડ્રેસ ચકાસો. + તે ચકાસણી પદ્ધતિ આ એકાઉન્ટ પર પહેલેથી જ સેટ કરેલી છે. + તમે આ એકાઉન્ટ પર ચકાસણી પદ્ધતિઓની મર્યાદા પર પહોંચી ગયા છો. diff --git a/auth/src/main/res/values-hi/strings.xml b/auth/src/main/res/values-hi/strings.xml index 45173de19c..cc77a9cdcc 100755 --- a/auth/src/main/res/values-hi/strings.xml +++ b/auth/src/main/res/values-hi/strings.xml @@ -100,15 +100,15 @@ फ़ोन नंबर की अपने आप पुष्टि की गई कोड फिर से भेजें फ़ोन नंबर की पुष्टि करें - Use a different phone number + दूसरे फ़ोन नंबर का इस्तेमाल करें “%1$s” पर टैप करने पर, एक मैसेज (एसएमएस) भेजा जा सकता है. मैसेज और डेटा दरें लागू हो सकती हैं. “%1$s” पर टैप करके, आप यह बताते हैं कि आप हमारी %2$s और %3$s को मंज़ूर करते हैं. एक मैसेज (एसएमएस) भेजा जा सकता है. मैसेज और डेटा दरें लागू हो सकती हैं. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + प्रमाणीकरण में गड़बड़ी + फिर से कोशिश करें + अतिरिक्त सत्यापन ज़रूरी है. कृपया मल्टी-फ़ैक्टर प्रमाणीकरण पूरा करें. + खाते को लिंक करना ज़रूरी है. कृपया प्रवेश करने का कोई दूसरा तरीका आज़माएँ. + प्रमाणीकरण रद्द कर दिया गया. तैयार होने पर फिर से कोशिश करें. प्रमाणीकरण विधि चुनें @@ -176,4 +176,14 @@ मल्टी-फैक्टर प्रमाणीकरण वर्तमान में अक्षम है + वह ईमेल या पासवर्ड सही नहीं है + वह पुष्टि करने वाला सेशन अब मान्य नहीं है. नया कोड पाने का अनुरोध करें. + फ़ोन की पुष्टि पूरी नहीं हुई. कृपया फिर से कोशिश करें. + वे क्रेडेंशियल किसी दूसरे खाते के हैं. + वह फ़ोन नंबर इस खाते पर पुष्टि के लिए सेट अप नहीं है. + आपका साइन इन सेशन खत्म हो गया है. जारी रखने के लिए फिर से साइन इन करें. + वह लिंक अब मान्य नहीं है. नया लिंक पाने का अनुरोध करें. + आगे बढ़ने से पहले अपने ईमेल पते की पुष्टि करें. + पुष्टि करने का वह तरीका इस खाते पर पहले से सेट अप है. + आपने इस खाते पर पुष्टि करने के तरीकों की सीमा पूरी कर ली है. diff --git a/auth/src/main/res/values-hr/strings.xml b/auth/src/main/res/values-hr/strings.xml index b6fef2393d..97a393a11a 100755 --- a/auth/src/main/res/values-hr/strings.xml +++ b/auth/src/main/res/values-hr/strings.xml @@ -100,7 +100,7 @@ Telefonski je broj automatski potvrđen Ponovo pošalji kôd Potvrda telefonskog broja - Use a different phone number + Upotrijebi drugi telefonski broj Dodirivanje gumba “%1$s” može dovesti do slanja SMS poruke. Mogu se primijeniti naknade za slanje poruka i podatkovni promet. Ako dodirnete "%1$s", potvrđujete da prihvaćate odredbe koje sadrže %2$s i %3$s. Možda ćemo vam poslati SMS. Moguća je naplata poruke i podatkovnog prometa. Greška provjere identiteta @@ -175,4 +175,14 @@ Višefaktorska autentifikacija trenutno je onemogućena + Ta e-adresa ili zaporka nije točna + Ta sesija potvrde više nije važeća. Zatražite novi kôd. + Potvrda telefonskog broja nije dovršena. Pokušajte ponovno. + Te vjerodajnice pripadaju drugom računu. + Taj telefonski broj nije postavljen za potvrdu na ovom računu. + Vaša je sesija prijave istekla. Prijavite se ponovno da biste nastavili. + Ta veza više nije važeća. Zatražite novu. + Potvrdite svoju e-adresu prije nego što nastavite. + Taj je način potvrde već postavljen na ovom računu. + Dosegnuli ste ograničenje broja načina potvrde na ovom računu. diff --git a/auth/src/main/res/values-hu/strings.xml b/auth/src/main/res/values-hu/strings.xml index 90e888d74f..e381c595b4 100755 --- a/auth/src/main/res/values-hu/strings.xml +++ b/auth/src/main/res/values-hu/strings.xml @@ -100,7 +100,7 @@ A telefonszám automatikusan ellenőrizve Kód újraküldése Telefonszám igazolása - Use a different phone number + Másik telefonszám használata Ha a(z) „%1$s” gombra koppint, a rendszer SMS-t küldhet Önnek. A szolgáltató ezért üzenet- és adatforgalmi díjat számíthat fel. A(z) „%1$s” gombra való koppintással elfogadja a következő dokumentumokat: %2$s és %3$s. A rendszer SMS-t küldhet Önnek. A szolgáltató ezért üzenet- és adatforgalmi díjat számíthat fel. Hitelesítési hiba @@ -175,4 +175,14 @@ A többfaktoros hitelesítés jelenleg le van tiltva + Az e-mail-cím vagy a jelszó nem helyes + Ez az ellenőrzési munkamenet már nem érvényes. Kérjen új kódot. + A telefonszám ellenőrzése nem fejeződött be. Kérjük, próbálja újra. + Ezek a hitelesítő adatok egy másik fiókhoz tartoznak. + Ez a telefonszám nincs beállítva ellenőrzésre ebben a fiókban. + A bejelentkezési munkamenet lejárt. A folytatáshoz jelentkezzen be újra. + Ez a link már nem érvényes. Kérjen újat. + A folytatás előtt erősítse meg az e-mail-címét. + Ez az ellenőrzési módszer már be van állítva ebben a fiókban. + Elérte az ebben a fiókban beállítható ellenőrzési módszerek felső határát. diff --git a/auth/src/main/res/values-in/strings.xml b/auth/src/main/res/values-in/strings.xml index ef5093027a..4d2d1d7ad4 100755 --- a/auth/src/main/res/values-in/strings.xml +++ b/auth/src/main/res/values-in/strings.xml @@ -10,7 +10,7 @@ Twitter GitHub Ponsel - Email + Alamat email Login dengan Google Login dengan Google Login dengan Facebook @@ -31,7 +31,7 @@ Login dengan Yahoo Login dengan Yahoo Berikutnya - Email + Alamat email Nomor Telepon Negara Pilih negara @@ -100,15 +100,15 @@ Nomor telepon terverifikasi secara otomatis Kirim Ulang Kode Verifikasi Nomor Telepon - Use a different phone number + Gunakan nomor telepon lain Dengan mengetuk “%1$s\", SMS mungkin akan dikirim. Mungkin dikenakan biaya pesan & data. Dengan mengetuk “%1$s”, Anda menyatakan bahwa Anda menyetujui %2$s dan %3$s kami. SMS mungkin akan dikirim. Mungkin dikenakan biaya pesan & data. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Error Autentikasi + Coba lagi + Verifikasi tambahan diperlukan. Harap selesaikan autentikasi multi-faktor. + Akun perlu ditautkan. Harap coba metode login yang lain. + Autentikasi dibatalkan. Harap coba lagi saat Anda siap. Pilih Metode Autentikasi @@ -176,4 +176,14 @@ Autentikasi multifaktor saat ini dinonaktifkan + Email atau sandi tersebut salah + Sesi verifikasi tersebut sudah tidak valid. Minta kode baru. + Verifikasi telepon tidak selesai. Harap coba lagi. + Kredensial tersebut milik akun lain. + Nomor telepon tersebut tidak disiapkan untuk verifikasi di akun ini. + Sesi login Anda sudah berakhir. Login lagi untuk melanjutkan. + Link tersebut sudah tidak valid. Minta link baru. + Verifikasi alamat email Anda sebelum melanjutkan. + Metode verifikasi tersebut sudah disiapkan di akun ini. + Anda telah mencapai batas metode verifikasi di akun ini. diff --git a/auth/src/main/res/values-it/strings.xml b/auth/src/main/res/values-it/strings.xml index da7f3a1a6d..f309a26d02 100755 --- a/auth/src/main/res/values-it/strings.xml +++ b/auth/src/main/res/values-it/strings.xml @@ -100,7 +100,7 @@ Numero di telefono verificato automaticamente Invia di nuovo il codice Verifica numero di telefono - Use a different phone number + Usa un altro numero di telefono Se tocchi “%1$s”, è possibile che venga inviato un SMS. Potrebbero essere applicate le tariffe per l\'invio dei messaggi e per il traffico dati. Se tocchi "%1$s", accetti i nostri %2$s e le nostre %3$s. È possibile che venga inviato un SMS. Potrebbero essere applicate le tariffe per l\'invio dei messaggi e per il traffico dati. Errore di autenticazione @@ -175,4 +175,14 @@ L\'autenticazione a più fattori è attualmente disabilitata + L\'email o la password non sono corretti + Questa sessione di verifica non è più valida. Richiedi un nuovo codice. + La verifica del telefono non è stata completata. Riprova. + Queste credenziali appartengono a un altro account. + Questo numero di telefono non è configurato per la verifica su questo account. + La tua sessione di accesso è scaduta. Accedi di nuovo per continuare. + Questo link non è più valido. Richiedine uno nuovo. + Verifica il tuo indirizzo email prima di continuare. + Questo metodo di verifica è già configurato su questo account. + Hai raggiunto il limite di metodi di verifica per questo account. diff --git a/auth/src/main/res/values-iw/strings.xml b/auth/src/main/res/values-iw/strings.xml index af4bc1c8c9..af1244be8b 100755 --- a/auth/src/main/res/values-iw/strings.xml +++ b/auth/src/main/res/values-iw/strings.xml @@ -100,15 +100,15 @@ מספר הטלפון אומת באופן אוטומטי שלח קוד חדש אמת את מספר הטלפון - Use a different phone number + שימוש במספר טלפון אחר הקשה על “%1$s” עשויה לגרום לשליחה של הודעת SMS. ייתכן שיחולו תעריפי הודעות והעברת נתונים. הקשה על “%1$s”, תפורש כהסכמתך ל%2$s ול%3$s. ייתכן שתישלח הודעת SMS. ייתכנו חיובים בגין שליחת הודעות ושימוש בנתונים. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + שגיאת אימות + נסה שוב + נדרש אימות נוסף. יש להשלים אימות רב-שלבי. + יש לקשר את החשבון. יש לנסות שיטת כניסה אחרת. + האימות בוטל. יש לנסות שוב כשתהיו מוכנים. בחר שיטת אימות @@ -176,4 +176,14 @@ אימות רב-גורמי מושבת כעת + האימייל או הסיסמה שגויים + הפעלת האימות הזו כבר לא בתוקף. יש לבקש קוד חדש. + אימות הטלפון לא הושלם. יש לנסות שוב. + פרטי הכניסה האלה שייכים לחשבון אחר. + מספר הטלפון הזה לא מוגדר לאימות בחשבון הזה. + הפעלת הכניסה שלך פגה. יש להיכנס שוב כדי להמשיך. + הקישור הזה כבר לא בתוקף. יש לבקש קישור חדש. + יש לאמת את כתובת האימייל שלך לפני שממשיכים. + שיטת האימות הזו כבר מוגדרת בחשבון הזה. + הגעת למגבלה של שיטות אימות בחשבון הזה. diff --git a/auth/src/main/res/values-ja/strings.xml b/auth/src/main/res/values-ja/strings.xml index d540d4edae..91bfe79e30 100755 --- a/auth/src/main/res/values-ja/strings.xml +++ b/auth/src/main/res/values-ja/strings.xml @@ -100,7 +100,7 @@ 電話番号は自動的に確認されました コードを再送信 電話番号を確認 - Use a different phone number + 別の電話番号を使用 [%1$s] をタップすると、SMS が送信されます。データ通信料がかかることがあります。 [%1$s] をタップすると、%2$s と %3$s に同意したことになり、SMS が送信されます。データ通信料がかかることがあります。 認証エラー @@ -175,4 +175,14 @@ 多要素認証は現在無効になっています + メールアドレスまたはパスワードが正しくありません + この確認セッションは無効になりました。新しいコードをリクエストしてください。 + 電話番号の確認が完了しませんでした。もう一度お試しください。 + この認証情報は別のアカウントのものです。 + この電話番号は、このアカウントの確認方法として設定されていません。 + ログインセッションの有効期限が切れました。続行するには、もう一度ログインしてください。 + このリンクは無効になりました。新しいリンクをリクエストしてください。 + 続行する前にメールアドレスを確認してください。 + この確認方法はこのアカウントですでに設定されています。 + このアカウントで設定できる確認方法の上限に達しました。 diff --git a/auth/src/main/res/values-kn/strings.xml b/auth/src/main/res/values-kn/strings.xml index 2be1d35d14..0de365d313 100755 --- a/auth/src/main/res/values-kn/strings.xml +++ b/auth/src/main/res/values-kn/strings.xml @@ -100,15 +100,15 @@ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಪರಿಶೀಲಿಸಲಾಗಿದೆ ಕೋಡ್ ಪುನಃ ಕಳುಹಿಸಿ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಪರಿಶೀಲಿಸಿ - Use a different phone number + ಬೇರೆ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಬಳಸಿ “%1$s” ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡುವ ಮೂಲಕ, ಎಸ್‌ಎಂಎಸ್‌ ಅನ್ನು ಕಳುಹಿಸಬಹುದಾಗಿದೆ. ಸಂದೇಶ ಮತ್ತು ಡೇಟಾ ದರಗಳು ಅನ್ವಯಿಸಬಹುದು. “%1$s” ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡುವ ಮೂಲಕ, ನೀವು ನಮ್ಮ %2$s ಮತ್ತು %3$s ಸ್ವೀಕರಿಸುತ್ತೀರಿ ಎಂದು ನೀವು ಸೂಚಿಸುತ್ತಿರುವಿರಿ. ಎಸ್‌ಎಂಎಸ್‌ ಅನ್ನು ಕಳುಹಿಸಬಹುದಾಗಿದೆ. ಸಂದೇಶ ಮತ್ತು ಡೇಟಾ ದರಗಳು ಅನ್ವಯಿಸಬಹುದು. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + ಪ್ರಮಾಣೀಕರಣ ದೋಷ + ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ + ಹೆಚ್ಚುವರಿ ಪರಿಶೀಲನೆ ಅಗತ್ಯವಿದೆ. ದಯವಿಟ್ಟು ಬಹು-ಅಂಶ ಪ್ರಮಾಣೀಕರಣವನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ. + ಖಾತೆಯನ್ನು ಲಿಂಕ್ ಮಾಡಬೇಕಾಗಿದೆ. ದಯವಿಟ್ಟು ಬೇರೆ ಸೈನ್ ಇನ್ ವಿಧಾನವನ್ನು ಪ್ರಯತ್ನಿಸಿ. + ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ. ನೀವು ಸಿದ್ಧರಾದಾಗ ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. ದೃಢೀಕರಣ ವಿಧಾನವನ್ನು ಆಯ್ಕೆಮಾಡಿ @@ -176,4 +176,14 @@ ಮಲ್ಟಿ-ಫ್ಯಾಕ್ಟರ್ ದೃಢೀಕರಣವು ಪ್ರಸ್ತುತ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ + ಆ ಇಮೇಲ್ ಅಥವಾ ಪಾಸ್‌ವರ್ಡ್ ಸರಿಯಾಗಿಲ್ಲ + ಆ ಪರಿಶೀಲನಾ ಸೆಶನ್ ಇನ್ನು ಮುಂದೆ ಮಾನ್ಯವಾಗಿಲ್ಲ. ಹೊಸ ಕೋಡ್‌ಗೆ ವಿನಂತಿಸಿ. + ಫೋನ್ ಪರಿಶೀಲನೆ ಪೂರ್ಣಗೊಂಡಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. + ಆ ರುಜುವಾತುಗಳು ಬೇರೆ ಖಾತೆಗೆ ಸೇರಿವೆ. + ಆ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಈ ಖಾತೆಯಲ್ಲಿ ಪರಿಶೀಲನೆಗಾಗಿ ಹೊಂದಿಸಲಾಗಿಲ್ಲ. + ನಿಮ್ಮ ಸೈನ್ ಇನ್ ಸೆಶನ್‌ನ ಅವಧಿ ಮುಗಿದಿದೆ. ಮುಂದುವರಿಸಲು ಮತ್ತೆ ಸೈನ್ ಇನ್ ಮಾಡಿ. + ಆ ಲಿಂಕ್ ಇನ್ನು ಮುಂದೆ ಮಾನ್ಯವಾಗಿಲ್ಲ. ಹೊಸ ಲಿಂಕ್‌ಗೆ ವಿನಂತಿಸಿ. + ಮುಂದುವರಿಯುವ ಮೊದಲು ನಿಮ್ಮ ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ಪರಿಶೀಲಿಸಿ. + ಆ ಪರಿಶೀಲನಾ ವಿಧಾನವನ್ನು ಈ ಖಾತೆಯಲ್ಲಿ ಈಗಾಗಲೇ ಹೊಂದಿಸಲಾಗಿದೆ. + ಈ ಖಾತೆಯಲ್ಲಿನ ಪರಿಶೀಲನಾ ವಿಧಾನಗಳ ಮಿತಿಯನ್ನು ನೀವು ತಲುಪಿದ್ದೀರಿ. diff --git a/auth/src/main/res/values-ko/strings.xml b/auth/src/main/res/values-ko/strings.xml index abcbfc82e9..c338e061d2 100755 --- a/auth/src/main/res/values-ko/strings.xml +++ b/auth/src/main/res/values-ko/strings.xml @@ -100,7 +100,7 @@ 전화번호가 자동으로 확인되었습니다. 코드 재전송 전화번호 인증 - Use a different phone number + 다른 전화번호 사용 “%1$s” 버튼을 탭하면 SMS가 발송될 수 있으며, 메시지 및 데이터 요금이 부과될 수 있습니다. 인증 오류 다시 시도 @@ -174,4 +174,14 @@ 다단계 인증이 현재 비활성화되어 있습니다 + 이메일 또는 비밀번호가 올바르지 않습니다. + 이 인증 세션은 더 이상 유효하지 않습니다. 새 코드를 요청하세요. + 전화번호 인증이 완료되지 않았습니다. 다시 시도하세요. + 이 사용자 인증 정보는 다른 계정에 속해 있습니다. + 이 전화번호는 이 계정의 인증 수단으로 설정되어 있지 않습니다. + 로그인 세션이 만료되었습니다. 계속하려면 다시 로그인하세요. + 이 링크는 더 이상 유효하지 않습니다. 새 링크를 요청하세요. + 계속하기 전에 이메일 주소를 인증하세요. + 이 인증 수단은 이 계정에 이미 설정되어 있습니다. + 이 계정에서 설정할 수 있는 인증 수단 한도에 도달했습니다. diff --git a/auth/src/main/res/values-ln/strings.xml b/auth/src/main/res/values-ln/strings.xml index 1931ef3766..eb731561fc 100755 --- a/auth/src/main/res/values-ln/strings.xml +++ b/auth/src/main/res/values-ln/strings.xml @@ -100,15 +100,15 @@ Numéro de téléphone validé automatiquement Renvoyer le code Valider le numéro de téléphone - Use a different phone number + Salelá nimero mosusu ya telefone En appuyant sur “%1$s”, vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. En appuyant sur “%1$s”, vous acceptez les %2$s et les %3$s. Vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Libunga ya bondimi + Meka lisusu + Bondimi mosusu esengeli. Tosɛngi osilisa bondimisami ya makambo mingi. + Konte esengeli kokangisama. Tosɛngi omeka lolenge mosusu ya kokɔta. + Bondimi e-annulé. Tosɛngi omeka lisusu ntango okozala pene. Pona lolenge ya bondimi @@ -176,4 +176,14 @@ Bondimisami ya makambo mingi ezali sikoyo te + Email to password wana ezali sembo te + Session wana ya bondimi ezali lisusu na ntina te. Sɛnga kode ya sika. + Bondimi ya telefone esili te. Meka lisusu. + Ba code wana ya kokɔta ezali ya konte mosusu. + Nimero wana ya telefone ebongisami te mpo na bondimi na konte oyo. + Session na yo ya kokɔta esili. Kɔta lisusu mpo na kokoba. + Lien wana ezali lisusu malamu te. Sɛnga lien ya sika. + Ndimisa adrɛsɛ na yo ya email liboso ya kokoba. + Lolenge wana ya bondimi ebongisami déjà na konte oyo. + Okómi na ndelo ya balolenge ya bondimi na konte oyo. diff --git a/auth/src/main/res/values-lt/strings.xml b/auth/src/main/res/values-lt/strings.xml index b68b5aabb2..4f6d152c5c 100755 --- a/auth/src/main/res/values-lt/strings.xml +++ b/auth/src/main/res/values-lt/strings.xml @@ -100,15 +100,15 @@ Telefono numeris patvirtintas automatiškai Siųsti kodą iš naujo Patvirtinti telefono numerį - Use a different phone number + Naudoti kitą telefono numerį Palietus „%1$s“ gali būti išsiųstas SMS pranešimas. Gali būti taikomi pranešimų ir duomenų įkainiai. Paliesdami „%1$s“ nurodote, kad sutinkate su %2$s ir %3$s. Gali būti išsiųstas SMS pranešimas, taip pat – taikomi pranešimų ir duomenų įkainiai. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Autentifikavimo klaida + Bandykite dar kartą + Reikalingas papildomas patvirtinimas. Užbaikite kelių veiksnių autentifikavimą. + Paskyrą reikia susieti. Išbandykite kitą prisijungimo būdą. + Autentifikavimas buvo atšauktas. Kai būsite pasirengę, bandykite dar kartą. Pasirinkite autentifikavimo metodą @@ -176,4 +176,14 @@ Daugiafaktoris tapatybės nustatymas šiuo metu išjungtas + Šis el. pašto adresas arba slaptažodis neteisingas + Šis patvirtinimo seansas nebegalioja. Paprašykite naujo kodo. + Telefono numerio patvirtinimas nebuvo baigtas. Bandykite dar kartą. + Šie prisijungimo duomenys priklauso kitai paskyrai. + Šis telefono numeris nenustatytas kaip patvirtinimo būdas šioje paskyroje. + Jūsų prisijungimo seansas baigėsi. Norėdami tęsti, prisijunkite dar kartą. + Ši nuoroda nebegalioja. Paprašykite naujos. + Prieš tęsdami patvirtinkite savo el. pašto adresą. + Šis patvirtinimo būdas šioje paskyroje jau nustatytas. + Pasiekėte šios paskyros patvirtinimo būdų ribą. diff --git a/auth/src/main/res/values-lv/strings.xml b/auth/src/main/res/values-lv/strings.xml index fb0f2ca26e..6cd5dd8bce 100755 --- a/auth/src/main/res/values-lv/strings.xml +++ b/auth/src/main/res/values-lv/strings.xml @@ -100,15 +100,15 @@ Tālruņa numurs tika automātiski verificēts Vēlreiz nosūtīt kodu Verificēt tālruņa numuru - Use a different phone number + Izmantot citu tālruņa numuru Pieskaroties pogai %1$s, var tikt nosūtīta īsziņa. Var tikt piemērota maksa par ziņojumiem un datu pārsūtīšanu. Pieskaroties pogai “%1$s”, jūs norādāt, ka piekrītat šādiem dokumentiem: %2$s un %3$s. Var tikt nosūtīta īsziņa. Var tikt piemērota maksa par ziņojumiem un datu pārsūtīšanu. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Autentifikācijas kļūda + Mēģiniet vēlreiz + Nepieciešama papildu verifikācija. Lūdzu, pabeidziet vairāku faktoru autentifikāciju. + Konts ir jāsaista. Lūdzu, izmēģiniet citu pierakstīšanās metodi. + Autentifikācija tika atcelta. Lūdzu, mēģiniet vēlreiz, kad būsiet gatavs. Izvēlieties autentifikācijas metodi @@ -176,4 +176,14 @@ Daudzfaktoru autentifikācija pašlaik ir atspējota + E-pasta adrese vai parole nav pareiza + Šī verifikācijas sesija vairs nav derīga. Pieprasiet jaunu kodu. + Tālruņa numura verifikācija netika pabeigta. Lūdzu, mēģiniet vēlreiz. + Šie akreditācijas dati pieder citam kontam. + Šis tālruņa numurs šajā kontā nav iestatīts verifikācijai. + Jūsu pierakstīšanās sesijai ir beidzies derīguma termiņš. Lai turpinātu, piesakieties vēlreiz. + Šī saite vairs nav derīga. Pieprasiet jaunu. + Pirms turpināt, verificējiet savu e-pasta adresi. + Šī verifikācijas metode šajā kontā jau ir iestatīta. + Jūs esat sasniedzis šī konta verifikācijas metožu ierobežojumu. diff --git a/auth/src/main/res/values-mo/strings.xml b/auth/src/main/res/values-mo/strings.xml index f96444e777..f73acd5d96 100755 --- a/auth/src/main/res/values-mo/strings.xml +++ b/auth/src/main/res/values-mo/strings.xml @@ -100,15 +100,15 @@ Numărul de telefon este verificat automat Retrimiteți codul Confirmați numărul de telefon - Use a different phone number + Folosiți alt număr de telefon Dacă atingeți „%1$s”, poate fi trimis un SMS. Se pot aplica tarife pentru mesaje și date. Dacă atingeți „%1$s”, sunteți de acord cu %2$s și cu %3$s. Poate fi trimis un SMS. Se pot aplica tarife pentru mesaje și date. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Eroare de autentificare + Încercați din nou + Este necesară o verificare suplimentară. Finalizați autentificarea cu mai mulți factori. + Contul trebuie asociat. Încercați altă metodă de conectare. + Autentificarea a fost anulată. Încercați din nou când sunteți gata. Alegeți metoda de autentificare @@ -176,4 +176,14 @@ Autentificarea cu mai mulți factori este dezactivată în prezent + Adresa de e-mail sau parola nu este corectă + Sesiunea de verificare nu mai este validă. Solicitați un cod nou. + Verificarea numărului de telefon nu s-a finalizat. Încercați din nou. + Aceste date de conectare aparțin altui cont. + Acest număr de telefon nu este configurat pentru verificare în acest cont. + Sesiunea de conectare a expirat. Conectați-vă din nou pentru a continua. + Acest link nu mai este valid. Solicitați unul nou. + Confirmați adresa de e-mail înainte de a continua. + Această metodă de verificare este deja configurată în acest cont. + Ați atins limita de metode de verificare pentru acest cont. diff --git a/auth/src/main/res/values-mr/strings.xml b/auth/src/main/res/values-mr/strings.xml index 38d2a9c8da..4886233038 100755 --- a/auth/src/main/res/values-mr/strings.xml +++ b/auth/src/main/res/values-mr/strings.xml @@ -100,15 +100,15 @@ फोन नंबरची अपोआप पडताळणी केली आहे कोड पुन्हा पाठवा फोन नंबरची पडताळणी करा - Use a different phone number + वेगळा फोन नंबर वापरा “%1$s“ वर टॅप केल्याने, एक एसएमएस पाठवला जाऊ शकतो. मेसेज आणि डेटा शुल्क लागू होऊ शकते. “%1$s” वर टॅप करून, तुम्ही सूचित करता की तुम्ही आमचे %2$s आणि %3$s स्वीकारता. एसएमएस पाठवला जाऊ शकतो. मेसेज आणि डेटा दर लागू केले जाऊ शकते. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + प्रमाणीकरण एरर + पुन्हा प्रयत्न करा + अतिरिक्त पडताळणी आवश्यक आहे. कृपया मल्टी-फॅक्टर प्रमाणीकरण पूर्ण करा. + खाते लिंक करणे आवश्यक आहे. कृपया वेगळी साइन इन पद्धत वापरून पहा. + प्रमाणीकरण रद्द केले गेले. तुम्ही तयार असाल तेव्हा कृपया पुन्हा प्रयत्न करा. प्रमाणीकरण पद्धत निवडा @@ -176,4 +176,14 @@ मल्टी-फॅक्टर ऑथेंटिकेशन सध्या अक्षम आहे + ते ईमेल किंवा पासवर्ड बरोबर नाही + ते पडताळणी सत्र यापुढे वैध नाही. नवीन कोडची विनंती करा. + फोनची पडताळणी पूर्ण झाली नाही. कृपया पुन्हा प्रयत्न करा. + ती क्रेडेन्शियल वेगळ्या खात्याची आहेत. + तो फोन नंबर या खात्यावर पडताळणीसाठी सेट केलेला नाही. + तुमचे साइन इन सत्र एक्स्पायर झाले आहे. सुरू ठेवण्यासाठी पुन्हा साइन इन करा. + ती लिंक यापुढे वैध नाही. नवीन लिंकची विनंती करा. + तुम्ही पुढे सुरू ठेवण्यापूर्वी तुमच्या ईमेल ॲड्रेसची पडताळणी करा. + ती पडताळणी पद्धत या खात्यावर आधीपासून सेट केलेली आहे. + तुम्ही या खात्यावरील पडताळणी पद्धतींची मर्यादा गाठली आहे. diff --git a/auth/src/main/res/values-ms/strings.xml b/auth/src/main/res/values-ms/strings.xml index f06075d773..1c2eed13ac 100755 --- a/auth/src/main/res/values-ms/strings.xml +++ b/auth/src/main/res/values-ms/strings.xml @@ -100,15 +100,15 @@ Nombor telefon disahkan secara automatik Hantar Semula Kod Sahkan Nombor Telefon - Use a different phone number + Gunakan nombor telefon lain Dengan mengetik “%1$s”, SMS akan dihantar. Tertakluk pada kadar mesej & data. Dengan mengetik “%1$s”, anda menyatakan bahawa anda menerima %2$s dan %3$s kami. SMS akan dihantar. Tertakluk pada kadar mesej & data. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Ralat Pengesahan + Cuba lagi + Pengesahan tambahan diperlukan. Sila lengkapkan pengesahan berbilang faktor. + Akaun perlu dipautkan. Sila cuba kaedah log masuk yang lain. + Pengesahan dibatalkan. Sila cuba lagi apabila anda bersedia. Pilih Kaedah Pengesahan @@ -176,4 +176,14 @@ Pengesahan berbilang faktor dilumpuhkan buat masa ini + E-mel atau kata laluan tersebut tidak betul + Sesi pengesahan tersebut tidak sah lagi. Minta kod baharu. + Pengesahan telefon tidak selesai. Sila cuba lagi. + Kelayakan tersebut milik akaun lain. + Nombor telefon tersebut tidak disediakan untuk pengesahan pada akaun ini. + Sesi log masuk anda telah tamat tempoh. Log masuk semula untuk meneruskan. + Pautan tersebut tidak sah lagi. Minta pautan baharu. + Sahkan alamat e-mel anda sebelum anda meneruskan. + Kaedah pengesahan tersebut telah pun disediakan pada akaun ini. + Anda telah mencapai had kaedah pengesahan pada akaun ini. diff --git a/auth/src/main/res/values-nb/strings.xml b/auth/src/main/res/values-nb/strings.xml index ac494d9cd6..f884d23468 100755 --- a/auth/src/main/res/values-nb/strings.xml +++ b/auth/src/main/res/values-nb/strings.xml @@ -100,7 +100,7 @@ Telefonnummeret ble bekreftet automatisk Send koden på nytt Bekreft telefonnummeret - Use a different phone number + Bruk et annet telefonnummer Når du trykker på «%1$s», kan det bli sendt en SMS. Kostnader for meldinger og datatrafikk kan påløpe. Ved å trykke på «%1$s» godtar du %2$s og %3$s våre. Du kan bli tilsendt en SMS. Kostnader for meldinger og datatrafikk kan påløpe. Godkjenningsfeil @@ -175,4 +175,14 @@ Flerfaktorautentisering er for øyeblikket deaktivert + E-postadressen eller passordet er feil + Denne bekreftelsesøkten er ikke lenger gyldig. Be om en ny kode. + Telefonbekreftelsen ble ikke fullført. Prøv igjen. + Denne påloggingsinformasjonen tilhører en annen konto. + Dette telefonnummeret er ikke satt opp for bekreftelse på denne kontoen. + Påloggingsøkten din er utløpt. Logg på igjen for å fortsette. + Denne lenken er ikke lenger gyldig. Be om en ny. + Bekreft e-postadressen din før du fortsetter. + Denne bekreftelsesmetoden er allerede satt opp på denne kontoen. + Du har nådd grensen for bekreftelsesmetoder på denne kontoen. diff --git a/auth/src/main/res/values-nl/strings.xml b/auth/src/main/res/values-nl/strings.xml index 064497191c..279ebe0910 100755 --- a/auth/src/main/res/values-nl/strings.xml +++ b/auth/src/main/res/values-nl/strings.xml @@ -100,7 +100,7 @@ Telefoonnummer is automatisch geverifieerd Code opnieuw verzenden Telefoonnummer verifiëren - Use a different phone number + Gebruik een ander telefoonnummer Als u op “%1$s” tikt, ontvangt u mogelijk een sms. Er kunnen sms- en datakosten in rekening worden gebracht. Als u op "%1$s" tikt, geeft u aan dat u onze %2$s en ons %3$s accepteert. Mogelijk ontvangt u een sms. Er kunnen sms- en datakosten in rekening worden gebracht. Authenticatiefout @@ -175,4 +175,14 @@ Multi-factorauthenticatie is momenteel uitgeschakeld + Dat e-mailadres of wachtwoord is onjuist + Deze verificatiesessie is niet meer geldig. Vraag een nieuwe code aan. + De telefoonverificatie is niet voltooid. Probeer het opnieuw. + Deze inloggegevens horen bij een ander account. + Dat telefoonnummer is niet ingesteld voor verificatie op dit account. + Uw inlogsessie is vervallen. Log opnieuw in om door te gaan. + Deze link is niet meer geldig. Vraag een nieuwe aan. + Bevestig uw e-mailadres voordat u doorgaat. + Deze verificatiemethode is al ingesteld op dit account. + U heeft de limiet voor verificatiemethoden op dit account bereikt. diff --git a/auth/src/main/res/values-no/strings.xml b/auth/src/main/res/values-no/strings.xml index 7df53a222c..3beb1b8265 100755 --- a/auth/src/main/res/values-no/strings.xml +++ b/auth/src/main/res/values-no/strings.xml @@ -100,15 +100,15 @@ Telefonnummeret ble bekreftet automatisk Send koden på nytt Bekreft telefonnummeret - Use a different phone number + Bruk et annet telefonnummer Når du trykker på «%1$s», kan det bli sendt en SMS. Kostnader for meldinger og datatrafikk kan påløpe. Ved å trykke på «%1$s» godtar du %2$s og %3$s våre. Du kan bli tilsendt en SMS. Kostnader for meldinger og datatrafikk kan påløpe. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Godkjenningsfeil + Prøv igjen + Ytterligere verifisering kreves. Vennligst fullfør multifaktorgodkjenning. + Kontoen må kobles. Prøv en annen påloggingsmetode. + Godkjenning ble avbrutt. Prøv igjen når du er klar. Velg autentiseringsmetode @@ -176,4 +176,14 @@ Flerfaktorautentisering er for øyeblikket deaktivert + E-postadressen eller passordet er feil + Denne bekreftelsesøkten er ikke lenger gyldig. Be om en ny kode. + Telefonbekreftelsen ble ikke fullført. Prøv igjen. + Denne påloggingsinformasjonen tilhører en annen konto. + Dette telefonnummeret er ikke satt opp for bekreftelse på denne kontoen. + Påloggingsøkten din er utløpt. Logg på igjen for å fortsette. + Denne lenken er ikke lenger gyldig. Be om en ny. + Bekreft e-postadressen din før du fortsetter. + Denne bekreftelsesmetoden er allerede satt opp på denne kontoen. + Du har nådd grensen for bekreftelsesmetoder på denne kontoen. diff --git a/auth/src/main/res/values-pl/strings.xml b/auth/src/main/res/values-pl/strings.xml index f651ddb899..1f636bb994 100755 --- a/auth/src/main/res/values-pl/strings.xml +++ b/auth/src/main/res/values-pl/strings.xml @@ -100,7 +100,7 @@ Numer telefonu został automatycznie zweryfikowany Wyślij kod ponownie Zweryfikuj numer telefonu - Use a different phone number + Użyj innego numeru telefonu Gdy klikniesz „%1$s”, może zostać wysłany SMS. Może to skutkować pobraniem opłaty za przesłanie wiadomości i danych. Klikając „%1$s", potwierdzasz, że akceptujesz te dokumenty: %2$s i %3$s. Może zostać wysłany SMS. Może to skutkować pobraniem opłat za przesłanie wiadomości i danych. Błąd uwierzytelniania @@ -175,4 +175,14 @@ Uwierzytelnianie wieloskładnikowe jest obecnie wyłączone + Ten adres e-mail lub hasło są nieprawidłowe + Ta sesja weryfikacji nie jest już ważna. Poproś o nowy kod. + Weryfikacja numeru telefonu nie została ukończona. Spróbuj ponownie. + Te dane logowania należą do innego konta. + Ten numer telefonu nie jest skonfigurowany do weryfikacji na tym koncie. + Sesja logowania wygasła. Zaloguj się ponownie, aby kontynuować. + Ten link nie jest już ważny. Poproś o nowy. + Zweryfikuj swój adres e-mail, zanim przejdziesz dalej. + Ta metoda weryfikacji jest już skonfigurowana na tym koncie. + Osiągnięto limit metod weryfikacji na tym koncie. diff --git a/auth/src/main/res/values-pt-rBR/strings.xml b/auth/src/main/res/values-pt-rBR/strings.xml index 76704c93f6..fedd068ffd 100755 --- a/auth/src/main/res/values-pt-rBR/strings.xml +++ b/auth/src/main/res/values-pt-rBR/strings.xml @@ -100,15 +100,15 @@ O número de telefone foi verificado automaticamente Reenviar código Confirmar número de telefone - Use a different phone number + Usar outro número de telefone Se você tocar em “%1$s”, um SMS poderá ser enviado e tarifas de mensagens e de dados serão cobradas. Ao tocar em “%1$s”, você concorda com nossos %2$s e a %3$s. Um SMS poderá ser enviado e tarifas de mensagens e de dados poderão ser cobradas. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Erro de autenticação + Tentar novamente + Verificação adicional necessária. Conclua a autenticação de vários fatores. + A conta precisa ser vinculada. Tente um método de login diferente. + A autenticação foi cancelada. Tente novamente quando estiver pronto. Escolher método de autenticação @@ -194,4 +194,14 @@ A autenticação multifator está atualmente desativada + O e-mail ou a senha está incorreto + Esta sessão de verificação não é mais válida. Solicite um novo código. + A verificação por telefone não foi concluída. Tente novamente. + Essas credenciais pertencem a outra conta. + Esse número de telefone não está configurado para verificação nesta conta. + Sua sessão de login expirou. Faça login novamente para continuar. + Esse link não é mais válido. Solicite um novo. + Verifique seu endereço de e-mail antes de continuar. + Esse método de verificação já está configurado nesta conta. + Você atingiu o limite de métodos de verificação nesta conta. diff --git a/auth/src/main/res/values-pt-rPT/strings.xml b/auth/src/main/res/values-pt-rPT/strings.xml index 8c7a2a173d..c0dd0e0456 100755 --- a/auth/src/main/res/values-pt-rPT/strings.xml +++ b/auth/src/main/res/values-pt-rPT/strings.xml @@ -100,15 +100,15 @@ Número de telefone verificado automaticamente Reenviar código Validar número de telefone - Use a different phone number + Usar outro número de telefone Ao tocar em “%1$s”, pode gerar o envio de uma SMS. Podem aplicar-se tarifas de mensagens e dados. Ao tocar em “%1$s”, indica que aceita os %2$s e a %3$s. Pode gerar o envio de uma SMS. Podem aplicar-se tarifas de dados e de mensagens. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Erro de autenticação + Tentar novamente + Verificação adicional necessária. Conclua a autenticação multifator. + A conta tem de ser associada. Experimente um método de início de sessão diferente. + A autenticação foi cancelada. Tente novamente quando estiver pronto. Escolher método de autenticação @@ -194,4 +194,14 @@ A autenticação multifator está atualmente desativada + O email ou a palavra-passe estão incorretos + Esta sessão de validação já não é válida. Solicite um novo código. + A validação do número de telefone não foi concluída. Tente novamente. + Estas credenciais pertencem a outra conta. + Este número de telefone não está configurado para validação nesta conta. + A sua sessão terminou. Inicie sessão novamente para continuar. + Este link já não é válido. Solicite um novo. + Valide o seu endereço de email antes de continuar. + Este método de validação já está configurado nesta conta. + Atingiu o limite de métodos de validação nesta conta. diff --git a/auth/src/main/res/values-pt/strings.xml b/auth/src/main/res/values-pt/strings.xml index 5ee4a9aa8f..8df734edee 100755 --- a/auth/src/main/res/values-pt/strings.xml +++ b/auth/src/main/res/values-pt/strings.xml @@ -100,7 +100,7 @@ O número de telefone foi verificado automaticamente Reenviar código Confirmar número de telefone - Use a different phone number + Usar outro número de telefone Se você tocar em “%1$s”, um SMS poderá ser enviado e tarifas de mensagens e de dados serão cobradas. Ao tocar em "%1$s", você concorda com nossos %2$s e a %3$s. Um SMS poderá ser enviado e tarifas de mensagens e de dados poderão ser cobradas. Erro de autenticação @@ -193,4 +193,14 @@ A autenticação multifator está atualmente desativada + O e-mail ou a senha está incorreto + Esta sessão de verificação não é mais válida. Solicite um novo código. + A verificação por telefone não foi concluída. Tente novamente. + Essas credenciais pertencem a outra conta. + Esse número de telefone não está configurado para verificação nesta conta. + Sua sessão de login expirou. Faça login novamente para continuar. + Esse link não é mais válido. Solicite um novo. + Verifique seu endereço de e-mail antes de continuar. + Esse método de verificação já está configurado nesta conta. + Você atingiu o limite de métodos de verificação nesta conta. diff --git a/auth/src/main/res/values-ro/strings.xml b/auth/src/main/res/values-ro/strings.xml index 6b0d3a0486..4b8fda9c57 100755 --- a/auth/src/main/res/values-ro/strings.xml +++ b/auth/src/main/res/values-ro/strings.xml @@ -100,7 +100,7 @@ Numărul de telefon este verificat automat Retrimiteți codul Confirmați numărul de telefon - Use a different phone number + Folosiți alt număr de telefon Dacă atingeți „%1$s”, poate fi trimis un SMS. Se pot aplica tarife pentru mesaje și date. Dacă atingeți „%1$s", sunteți de acord cu %2$s și cu %3$s. Poate fi trimis un SMS. Se pot aplica tarife pentru mesaje și date. Eroare de autentificare @@ -175,4 +175,14 @@ Autentificarea cu mai mulți factori este dezactivată în prezent + Adresa de e-mail sau parola nu este corectă + Această sesiune de verificare nu mai este validă. Solicitați un cod nou. + Verificarea numărului de telefon nu a fost finalizată. Încercați din nou. + Aceste date de conectare aparțin altui cont. + Acest număr de telefon nu este configurat pentru verificare în acest cont. + Sesiunea de conectare a expirat. Conectați-vă din nou pentru a continua. + Acest link nu mai este valid. Solicitați unul nou. + Confirmați adresa de e-mail înainte de a continua. + Această metodă de verificare este deja configurată în acest cont. + Ați atins limita de metode de verificare pentru acest cont. diff --git a/auth/src/main/res/values-ru/strings.xml b/auth/src/main/res/values-ru/strings.xml index ebf567549e..bdf4890439 100755 --- a/auth/src/main/res/values-ru/strings.xml +++ b/auth/src/main/res/values-ru/strings.xml @@ -100,7 +100,7 @@ Номер телефона был подтвержден автоматически Отправить код ещё раз Подтвердить номер телефона - Use a different phone number + Использовать другой номер телефона Нажимая кнопку “%1$s”, вы соглашаетесь получить SMS. За его отправку и обмен данными может взиматься плата. Нажимая кнопку "%1$s", вы принимаете %2$s и %3$s, а также соглашаетесь получить SMS. За его отправку и обмен данными может взиматься плата. Ошибка аутентификации @@ -175,4 +175,14 @@ Многофакторная аутентификация в настоящее время отключена + Неправильный адрес электронной почты или пароль + Этот сеанс проверки больше не действителен. Запросите новый код. + Не удалось подтвердить номер телефона. Повторите попытку. + Эти учётные данные принадлежат другому аккаунту. + Этот номер телефона не настроен для подтверждения в этом аккаунте. + Сеанс входа истёк. Войдите ещё раз, чтобы продолжить. + Эта ссылка больше не действительна. Запросите новую. + Подтвердите адрес электронной почты, прежде чем продолжить. + Этот способ подтверждения уже настроен в этом аккаунте. + Достигнут лимит способов подтверждения для этого аккаунта. diff --git a/auth/src/main/res/values-sk/strings.xml b/auth/src/main/res/values-sk/strings.xml index 0786594fd9..c6bc7daa8e 100755 --- a/auth/src/main/res/values-sk/strings.xml +++ b/auth/src/main/res/values-sk/strings.xml @@ -100,7 +100,7 @@ Telefónne číslo bolo automaticky overené Znova odoslať kód Overiť telefónne číslo - Use a different phone number + Použiť iné telefónne číslo Klepnutím na tlačidlo %1$s možno odoslať SMS. Môžu sa účtovať poplatky za správy a dáta. Klepnutím na tlačidlo %1$s vyjadrujete súhlas s dokumentmi %2$s a %3$s. Môže byť odoslaná SMS a môžu sa účtovať poplatky za správy a dáta. Chyba overenia @@ -175,4 +175,14 @@ Viacfaktorové overovanie je momentálne zakázané + Tento e-mail alebo heslo nie je správne + Táto overovacia relácia už nie je platná. Vyžiadajte si nový kód. + Overenie telefónneho čísla sa nedokončilo. Skúste to znova. + Tieto prihlasovacie údaje patria inému účtu. + Toto telefónne číslo nie je v tomto účte nastavené na overovanie. + Vaša prihlasovacia relácia vypršala. Ak chcete pokračovať, prihláste sa znova. + Tento odkaz už nie je platný. Vyžiadajte si nový. + Skôr než budete pokračovať, overte svoju e-mailovú adresu. + Táto metóda overenia je v tomto účte už nastavená. + Dosiahli ste limit metód overenia pre tento účet. diff --git a/auth/src/main/res/values-sl/strings.xml b/auth/src/main/res/values-sl/strings.xml index 3375bd8c01..10c4d3e94d 100755 --- a/auth/src/main/res/values-sl/strings.xml +++ b/auth/src/main/res/values-sl/strings.xml @@ -100,15 +100,15 @@ Telefonska številka je bila samodejno preverjena Ponovno pošlji kodo Preverjanje telefonske številke - Use a different phone number + Uporabi drugo telefonsko številko Če se dotaknete možnosti »%1$s«, bo morda poslano sporočilo SMS. Pošiljanje sporočila in prenos podatkov boste morda morali plačati. Če se dotaknete možnosti »%1$s«, potrjujete, da se strinjate z dokumentoma %2$s in %3$s. Morda bo poslano sporočilo SMS. Pošiljanje sporočila in prenos podatkov boste morda morali plačati. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Napaka pri preverjanju pristnosti + Poskusite znova + Potrebno je dodatno preverjanje. Dokončajte večstopenjsko preverjanje pristnosti. + Račun je treba povezati. Poskusite z drugim načinom prijave. + Preverjanje pristnosti je bilo preklicano. Ko boste pripravljeni, poskusite znova. Izberite način preverjanja pristnosti @@ -176,4 +176,14 @@ Večfaktorska avtentikacija je trenutno onemogočena + E-poštni naslov ali geslo ni pravilno + Ta seja preverjanja ni več veljavna. Zahtevajte novo kodo. + Preverjanje telefonske številke ni bilo dokončano. Poskusite znova. + Ti poverilnici pripadata drugemu računu. + Ta telefonska številka v tem računu ni nastavljena za preverjanje. + Vaša prijavna seja je potekla. Za nadaljevanje se znova prijavite. + Ta povezava ni več veljavna. Zahtevajte novo. + Preden nadaljujete, preverite svoj e-poštni naslov. + Ta način preverjanja je v tem računu že nastavljen. + Dosegli ste omejitev števila načinov preverjanja v tem računu. diff --git a/auth/src/main/res/values-sr/strings.xml b/auth/src/main/res/values-sr/strings.xml index 7266b80e4f..8d333c4b2d 100755 --- a/auth/src/main/res/values-sr/strings.xml +++ b/auth/src/main/res/values-sr/strings.xml @@ -100,15 +100,15 @@ Број телефона је аутоматски верификован Поново пошаљи кôд Верификуј број телефона - Use a different phone number + Користи други број телефона Ако додирнете „%1$s“, можда ћете послати SMS. Могу да вам буду наплаћени трошкови слања поруке и преноса података. Ако додирнете „%1$s“, потврђујете да прихватате документе %2$s и %3$s. Можда ћете послати SMS. Могу да вам буду наплаћени трошкови слања поруке и преноса података. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Грешка при аутентификацији + Пробајте поново + Потребна је додатна верификација. Довршите аутентификацију са више фактора. + Налог треба да се повеже. Пробајте други метод пријављивања. + Аутентификација је отказана. Пробајте поново када будете спремни. Изаберите метод аутентификације @@ -176,4 +176,14 @@ Вишефакторска аутентификација је тренутно онемогућена + Та имејл адреса или лозинка није тачна + Та сесија верификације више не важи. Затражите нови кôд. + Верификација броја телефона није довршена. Пробајте поново. + Ти акредитиви припадају другом налогу. + Тај број телефона није подешен за верификацију на овом налогу. + Сесија пријављивања је истекла. Пријавите се поново да бисте наставили. + Та веза више не важи. Затражите нову. + Верификујте имејл адресу пре него што наставите. + Тај метод верификације је већ подешен на овом налогу. + Достигли сте ограничење броја метода верификације на овом налогу. diff --git a/auth/src/main/res/values-sv/strings.xml b/auth/src/main/res/values-sv/strings.xml index 2964dbcc79..7ac07b9cbf 100755 --- a/auth/src/main/res/values-sv/strings.xml +++ b/auth/src/main/res/values-sv/strings.xml @@ -100,7 +100,7 @@ Telefonnumret verifierades automatiskt Skicka koden igen Verifiera telefonnummer - Use a different phone number + Använd ett annat telefonnummer Genom att trycka på %1$s skickas ett sms. Meddelande- och dataavgifter kan tillkomma. Genom att trycka på %1$s godkänner du våra %2$s och vår %3$s. Ett sms kan skickas. Meddelande- och dataavgifter kan tillkomma. Autentiseringsfel @@ -175,4 +175,14 @@ Multifaktorautentisering är för närvarande inaktiverad + E-postadressen eller lösenordet är felaktigt + Verifieringssessionen är inte längre giltig. Begär en ny kod. + Telefonverifieringen slutfördes inte. Försök igen. + De här inloggningsuppgifterna tillhör ett annat konto. + Det telefonnumret är inte konfigurerat för verifiering på det här kontot. + Din inloggningssession har upphört att gälla. Logga in igen för att fortsätta. + Länken är inte längre giltig. Begär en ny. + Verifiera din e-postadress innan du fortsätter. + Den verifieringsmetoden är redan konfigurerad på det här kontot. + Du har nått gränsen för antalet verifieringsmetoder på det här kontot. diff --git a/auth/src/main/res/values-ta/strings.xml b/auth/src/main/res/values-ta/strings.xml index f268d402b8..1b003ab4cd 100755 --- a/auth/src/main/res/values-ta/strings.xml +++ b/auth/src/main/res/values-ta/strings.xml @@ -100,15 +100,15 @@ ஃபோன் எண் தானாகவே சரிபார்க்கப்பட்டது குறியீட்டை மீண்டும் அனுப்பு ஃபோன் எண்ணைச் சரிபார் - Use a different phone number + வேறு ஃபோன் எண்ணைப் பயன்படுத்து “%1$s” என்பதைத் தட்டுவதன் மூலம், SMS அனுப்பப்படலாம். செய்தி மற்றும் தரவுக் கட்டணங்கள் விதிக்கப்படலாம். “%1$s” என்பதைத் தட்டுவதன் மூலம், எங்கள் %2$s மற்றும் %3$sஐ ஏற்பதாகக் குறிப்பிடுகிறீர்கள். SMS அனுப்பப்படலாம். செய்தி மற்றும் தரவுக் கட்டணங்கள் விதிக்கப்படலாம். - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + அங்கீகாரப் பிழை + மீண்டும் முயலவும் + கூடுதல் சரிபார்ப்பு தேவை. பல்காரணி அங்கீகாரத்தை நிறைவுசெய்யவும். + கணக்கை இணைக்க வேண்டும். வேறு உள்நுழைவு முறையை முயலவும். + அங்கீகாரம் ரத்துசெய்யப்பட்டது. தயாரானதும் மீண்டும் முயலவும். அங்கீகார முறையைத் தேர்ந்தெடுக்கவும் @@ -176,4 +176,14 @@ பல-காரணி அங்கீகாரம் தற்போது முடக்கப்பட்டுள்ளது + அந்த மின்னஞ்சல் முகவரியோ கடவுச்சொல்லோ சரியில்லை + அந்தச் சரிபார்ப்பு அமர்வு இனி செல்லுபடியாகாது. புதிய குறியீட்டைக் கோரவும். + ஃபோன் சரிபார்ப்பு நிறைவடையவில்லை. மீண்டும் முயலவும். + அந்த அனுமதிச் சான்றுகள் வேறொரு கணக்கிற்கு உரியவை. + அந்த ஃபோன் எண் இந்தக் கணக்கில் சரிபார்ப்பிற்காக அமைக்கப்படவில்லை. + உங்கள் உள்நுழைவு அமர்வு காலாவதியானது. தொடர, மீண்டும் உள்நுழையவும். + அந்த இணைப்பு இனி செல்லுபடியாகாது. புதியதொன்றைக் கோரவும். + தொடர்வதற்கு முன் உங்கள் மின்னஞ்சல் முகவரியைச் சரிபார்க்கவும். + அந்தச் சரிபார்ப்பு முறை இந்தக் கணக்கில் ஏற்கெனவே அமைக்கப்பட்டுள்ளது. + இந்தக் கணக்கிற்கான சரிபார்ப்பு முறைகளின் வரம்பை எட்டிவிட்டீர்கள். diff --git a/auth/src/main/res/values-th/strings.xml b/auth/src/main/res/values-th/strings.xml index ad9f71a6b4..f27619270f 100755 --- a/auth/src/main/res/values-th/strings.xml +++ b/auth/src/main/res/values-th/strings.xml @@ -100,15 +100,15 @@ ยืนยันหมายเลขโทรศัพท์โดยอัตโนมัติแล้ว ส่งรหัสอีกครั้ง ยืนยันหมายเลขโทรศัพท์ - Use a different phone number + ใช้หมายเลขโทรศัพท์อื่น เมื่อคุณแตะ “%1$s” ระบบจะส่ง SMS ให้คุณ อาจมีค่าบริการรับส่งข้อความและค่าบริการอินเทอร์เน็ต การแตะ “%1$s” แสดงว่าคุณยอมรับ %2$s และ %3$s ระบบจะส่ง SMS ให้คุณ อาจมีค่าบริการรับส่งข้อความและค่าบริการอินเทอร์เน็ต - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + ข้อผิดพลาดในการตรวจสอบสิทธิ์ + ลองอีกครั้ง + ต้องมีการยืนยันเพิ่มเติม โปรดดำเนินการยืนยันตัวตนแบบหลายปัจจัยให้เสร็จสมบูรณ์ + ต้องลิงก์บัญชี โปรดลองใช้วิธีลงชื่อเข้าใช้วิธีอื่น + การตรวจสอบสิทธิ์ถูกยกเลิก โปรดลองอีกครั้งเมื่อคุณพร้อม เลือกวิธีการตรวจสอบสิทธิ์ @@ -176,4 +176,14 @@ การรับรองความถูกต้องแบบหลายปัจจัยถูกปิดใช้งานในขณะนี้ + อีเมลหรือรหัสผ่านไม่ถูกต้อง + เซสชันการยืนยันนี้ใช้ไม่ได้อีกต่อไป โปรดขอรหัสใหม่ + การยืนยันหมายเลขโทรศัพท์ไม่เสร็จสมบูรณ์ โปรดลองอีกครั้ง + ข้อมูลเข้าสู่ระบบนี้เป็นของบัญชีอื่น + หมายเลขโทรศัพท์นี้ไม่ได้ตั้งค่าไว้สำหรับการยืนยันในบัญชีนี้ + เซสชันการลงชื่อเข้าใช้หมดอายุแล้ว โปรดลงชื่อเข้าใช้อีกครั้งเพื่อดำเนินการต่อ + ลิงก์นี้ใช้ไม่ได้อีกต่อไป โปรดขอลิงก์ใหม่ + โปรดยืนยันที่อยู่อีเมลก่อนดำเนินการต่อ + วิธีการยืนยันนี้ตั้งค่าไว้ในบัญชีนี้แล้ว + คุณใช้วิธีการยืนยันถึงขีดจำกัดของบัญชีนี้แล้ว diff --git a/auth/src/main/res/values-tl/strings.xml b/auth/src/main/res/values-tl/strings.xml index 90e55a4704..2a2fab5359 100755 --- a/auth/src/main/res/values-tl/strings.xml +++ b/auth/src/main/res/values-tl/strings.xml @@ -100,7 +100,7 @@ Awtomatikong na-verify ang numero ng telepono Ipadala Muli ang Code I-verify ang Numero ng Telepono - Use a different phone number + Gumamit ng ibang numero ng telepono Sa pag-tap sa “%1$s,“ maaaring magpadala ng SMS. Maaaring ipatupad ang mga rate ng pagmemensahe at data. Sa pag-tap sa “%1$s”, ipinababatid mo na tinatanggap mo ang aming %2$s at %3$s. Maaaring magpadala ng SMS. Maaaring ipatupad ang mga rate ng pagmemensahe at data. Error sa Pagpapatotoo @@ -146,7 +146,7 @@ Pumili ng paraan ng pag-verify Magdagdag ng karagdagang layer ng seguridad SMS - Authenticator app + App ng authenticator Ang numerong ito ay nauugnay sa ibang account Kinakailangan ang pag-verify I-scan ang QR code gamit ang iyong authenticator app @@ -163,16 +163,26 @@ Mag-authenticate muli Alisin Ipadala muli ang verification email - Secret key + Sikretong key Mag-sign out Naka-sign in bilang Laktawan Gumamit ng ibang paraan - Verification code + Code sa pag-verify Na-verify ang email I-verify Nagpadala kami ng verification email sa %1$s Kasalukuyang naka-disable ang multi-factor authentication + Mali ang email address o password na iyon + Wala nang bisa ang verification session na iyon. Humiling ng bagong code. + Hindi nakumpleto ang pag-verify ng telepono. Subukan muli. + Kabilang ang mga kredensyal na iyon sa ibang account. + Hindi naka-set up ang numero ng teleponong iyon para sa pag-verify sa account na ito. + Nag-expire na ang iyong sign-in session. Mag-sign in muli para magpatuloy. + Wala nang bisa ang link na iyon. Humiling ng bago. + I-verify ang iyong email address bago ka magpatuloy. + Naka-set up na ang paraan ng pag-verify na iyon sa account na ito. + Naabot mo na ang limitasyon ng mga paraan ng pag-verify sa account na ito. diff --git a/auth/src/main/res/values-tr/strings.xml b/auth/src/main/res/values-tr/strings.xml index 15a7d80b91..33243eec8d 100755 --- a/auth/src/main/res/values-tr/strings.xml +++ b/auth/src/main/res/values-tr/strings.xml @@ -100,15 +100,15 @@ Telefon numarası otomatik olarak doğrulandı Kodu Yeniden Gönder Telefon Numarasını Doğrula - Use a different phone number + Farklı bir telefon numarası kullan “%1$s” öğesine dokunarak SMS gönderilebilir. Mesaj ve veri ücretleri uygulanabilir. “%1$s” öğesine dokunarak %2$s ve %3$s hükümlerimizi kabul ettiğinizi bildirirsiniz. SMS gönderilebilir. Mesaj ve veri ücretleri uygulanabilir. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Kimlik Doğrulama Hatası + Tekrar dene + Ek doğrulama gerekiyor. Lütfen çok faktörlü kimlik doğrulamayı tamamlayın. + Hesabın bağlanması gerekiyor. Lütfen farklı bir oturum açma yöntemi deneyin. + Kimlik doğrulama iptal edildi. Hazır olduğunuzda tekrar deneyin. Kimlik Doğrulama Yöntemini Seçin @@ -176,4 +176,14 @@ Çok faktörlü kimlik doğrulama şu anda devre dışı + E-posta adresi veya şifre yanlış + Bu doğrulama oturumu artık geçerli değil. Yeni bir kod isteyin. + Telefon doğrulaması tamamlanmadı. Tekrar deneyin. + Bu kimlik bilgileri başka bir hesaba ait. + Bu telefon numarası, bu hesapta doğrulama için ayarlanmamış. + Oturumunuzun süresi doldu. Devam etmek için tekrar oturum açın. + Bu bağlantı artık geçerli değil. Yeni bir tane isteyin. + Devam etmeden önce e-posta adresinizi doğrulayın. + Bu doğrulama yöntemi bu hesapta zaten ayarlanmış. + Bu hesap için doğrulama yöntemi sınırına ulaştınız. diff --git a/auth/src/main/res/values-uk/strings.xml b/auth/src/main/res/values-uk/strings.xml index 338339d6c1..dfd568dd9b 100755 --- a/auth/src/main/res/values-uk/strings.xml +++ b/auth/src/main/res/values-uk/strings.xml @@ -100,15 +100,15 @@ Номер телефону підтверджено автоматично Повторно надіслати код Підтвердити номер телефону - Use a different phone number + Використати інший номер телефону Коли ви торкнетесь опції “%1$s”, вам може надійти SMS-повідомлення. За SMS і використання трафіку може стягуватися плата. Торкаючись кнопки “%1$s”, ви приймаєте такі документи: %2$s і %3$s. Вам може надійти SMS-повідомлення. За SMS і використання трафіку може стягуватися плата. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Помилка автентифікації + Повторити спробу + Потрібне додаткове підтвердження. Виконайте багатофакторну автентифікацію. + Обліковий запис потрібно зв\'язати. Спробуйте інший спосіб входу. + Автентифікацію скасовано. Повторіть спробу, коли будете готові. Виберіть спосіб автентифікації @@ -176,4 +176,14 @@ Багатофакторна автентифікація наразі вимкнена + Неправильна електронна адреса або пароль + Цей сеанс підтвердження більше не дійсний. Запросіть новий код. + Не вдалося завершити підтвердження номера телефону. Повторіть спробу. + Ці облікові дані належать іншому обліковому запису. + Цей номер телефону не налаштовано для підтвердження в цьому обліковому записі. + Сеанс входу закінчився. Увійдіть знову, щоб продовжити. + Це посилання більше не дійсне. Запросіть нове. + Підтвердьте свою електронну адресу, перш ніж продовжити. + Цей спосіб підтвердження вже налаштовано в цьому обліковому записі. + Ви досягли ліміту способів підтвердження для цього облікового запису. diff --git a/auth/src/main/res/values-ur/strings.xml b/auth/src/main/res/values-ur/strings.xml index aa780c0fb3..1e0c977c8f 100755 --- a/auth/src/main/res/values-ur/strings.xml +++ b/auth/src/main/res/values-ur/strings.xml @@ -100,15 +100,15 @@ فون نمبر کی خودکار طور پر توثیق ہو گئی کوڈ دوبارہ بھیجیں فون نمبر کی توثیق کریں - Use a different phone number + مختلف فون نمبر استعمال کریں %1$s پر تھپتھپانے سے، ایک SMS بھیجا جا سکتا ہے۔ پیغام اور ڈیٹا کی شرحوں کا اطلاق ہو سکتا ہے۔ “%1$s” کو تھپتھپا کر، آپ نشاندہی کر رہے ہیں کہ آپ ہماری %2$s اور %3$s کو قبول کرتے ہیں۔ ایک SMS بھیجا جا سکتا ہے۔ پیغام اور ڈیٹا نرخ لاگو ہو سکتے ہیں۔ - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + تصدیق کی خرابی + دوبارہ کوشش کریں + اضافی تصدیق درکار ہے۔ براہ کرم کثیر عنصری تصدیق مکمل کریں۔ + اکاؤنٹ کو لنک کرنے کی ضرورت ہے۔ براہ کرم مختلف سائن ان طریقہ آزمائیں۔ + تصدیق منسوخ کر دی گئی۔ تیار ہونے پر دوبارہ کوشش کریں۔ تصدیقی طریقہ منتخب کریں @@ -176,4 +176,14 @@ ملٹی فیکٹر تصدیق فی الحال غیر فعال ہے + وہ ای میل یا پاس ورڈ درست نہیں ہے + یہ توثیقی سیشن اب کارآمد نہیں رہا۔ نیا کوڈ طلب کریں۔ + فون کی توثیق مکمل نہیں ہوئی۔ دوبارہ کوشش کریں۔ + یہ اسناد کسی دوسرے اکاؤنٹ سے تعلق رکھتی ہیں۔ + یہ فون نمبر اس اکاؤنٹ پر توثیق کے لیے سیٹ اپ نہیں ہے۔ + آپ کا سائن ان سیشن ختم ہو گیا۔ جاری رکھنے کے لیے دوبارہ سائن ان کریں۔ + یہ لنک اب کارآمد نہیں رہا۔ نیا لنک طلب کریں۔ + جاری رکھنے سے پہلے اپنے ای میل پتے کی توثیق کریں۔ + یہ توثیقی طریقہ اس اکاؤنٹ پر پہلے سے سیٹ اپ ہے۔ + آپ اس اکاؤنٹ پر توثیقی طریقوں کی حد تک پہنچ گئے ہیں۔ diff --git a/auth/src/main/res/values-vi/strings.xml b/auth/src/main/res/values-vi/strings.xml index c2a0b99938..9f458fa80b 100755 --- a/auth/src/main/res/values-vi/strings.xml +++ b/auth/src/main/res/values-vi/strings.xml @@ -10,7 +10,7 @@ Twitter GitHub Điện thoại - Email + Địa chỉ email Đăng nhập bằng Google Đăng nhập bằng Google Đăng nhập bằng Facebook @@ -31,7 +31,7 @@ Đăng nhập bằng Yahoo Đăng nhập bằng Yahoo Tiếp - Email + Địa chỉ email Số điện thoại Quốc gia Chọn quốc gia @@ -100,15 +100,15 @@ Đã tự động xác minh số điện thoại Gửi lại mã Xác minh số điện thoại - Use a different phone number + Dùng số điện thoại khác Bằng cách nhấn vào “%1$s”, bạn có thể nhận được một tin nhắn SMS. Cước tin nhắn và dữ liệu có thể áp dụng. Bằng cách nhấn vào “%1$s”, bạn cho biết rằng bạn chấp nhận %2$s và %3$s của chúng tôi. Bạn có thể nhận được một tin nhắn SMS. Cước tin nhắn và dữ liệu có thể áp dụng. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Lỗi xác thực + Thử lại + Bạn cần xác minh thêm. Vui lòng hoàn tất quy trình xác thực nhiều yếu tố. + Bạn cần liên kết tài khoản. Vui lòng thử một phương thức đăng nhập khác. + Quá trình xác thực đã bị hủy. Vui lòng thử lại khi bạn sẵn sàng. Chọn phương thức xác thực @@ -176,4 +176,14 @@ Xác thực đa yếu tố hiện đang bị vô hiệu hóa + Email hoặc mật khẩu không chính xác + Phiên xác minh này không còn hợp lệ. Hãy yêu cầu mã mới. + Quá trình xác minh số điện thoại chưa hoàn tất. Hãy thử lại. + Thông tin đăng nhập này thuộc về một tài khoản khác. + Số điện thoại này chưa được thiết lập để xác minh trên tài khoản này. + Phiên đăng nhập của bạn đã hết hạn. Hãy đăng nhập lại để tiếp tục. + Đường liên kết này không còn hợp lệ. Hãy yêu cầu một đường liên kết mới. + Hãy xác minh địa chỉ email của bạn trước khi tiếp tục. + Phương thức xác minh này đã được thiết lập trên tài khoản này. + Bạn đã đạt đến giới hạn số phương thức xác minh trên tài khoản này. diff --git a/auth/src/main/res/values-zh-rCN/strings.xml b/auth/src/main/res/values-zh-rCN/strings.xml index 12fb4c3dd8..79be0e30b8 100755 --- a/auth/src/main/res/values-zh-rCN/strings.xml +++ b/auth/src/main/res/values-zh-rCN/strings.xml @@ -100,15 +100,15 @@ 电话号码已自动验证 重新发送验证码 验证电话号码 - Use a different phone number + 使用其他电话号码 您点按“%1$s”后,系统会向您发送一条短信。这可能会产生短信费用和上网流量费。 点按“%1$s”即表示您接受我们的%2$s和%3$s。系统会向您发送一条短信。这可能会产生短信费用和上网流量费。 - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + 身份验证错误 + 重试 + 需要额外的验证。请完成多重身份验证。 + 需要关联账户。请尝试其他登录方式。 + 身份验证已取消。准备好后请重试。 选择身份验证方法 @@ -176,4 +176,14 @@ 多重身份验证当前已禁用 + 电子邮件地址或密码不正确 + 该验证会话已失效,请重新获取验证码。 + 电话号码验证未完成,请重试。 + 这些凭据属于其他帐号。 + 该电话号码未在此帐号中设置为验证方式。 + 您的登录会话已过期,请重新登录以继续。 + 该链接已失效,请重新获取。 + 请先验证您的电子邮件地址,然后再继续。 + 该验证方式已在此帐号中设置。 + 您已达到此帐号的验证方式数量上限。 diff --git a/auth/src/main/res/values-zh-rHK/strings.xml b/auth/src/main/res/values-zh-rHK/strings.xml index fb7a627ae6..5fd6213b3a 100755 --- a/auth/src/main/res/values-zh-rHK/strings.xml +++ b/auth/src/main/res/values-zh-rHK/strings.xml @@ -100,15 +100,15 @@ 已自動驗證電話號碼 重新傳送驗證碼 驗證電話號碼 - Use a different phone number + 使用其他電話號碼 輕觸 [%1$s] 後,系統將會傳送一封簡訊。您可能需支付簡訊和數據傳輸費用。 輕觸 [%1$s] 即表示您同意接受我們的《%2$s》和《%3$s》。系統將會傳送簡訊給您,不過您可能需要支付簡訊和數據傳輸費用。 - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + 驗證錯誤 + 重試 + 需要額外驗證。請完成雙重驗證。 + 需要連結帳戶。請嘗試其他登入方式。 + 驗證已取消。準備好後請再試一次。 選擇驗證方法 @@ -176,4 +176,14 @@ 多重身份验证当前已禁用 + 電郵地址或密碼不正確 + 此驗證程序已失效,請重新要求驗證碼。 + 電話驗證尚未完成,請再試一次。 + 這些憑證屬於其他帳戶。 + 此電話號碼並未在此帳戶中設定為驗證方法。 + 您的登入程序已逾時,請重新登入以繼續。 + 此連結已失效,請要求新的連結。 + 請先驗證您的電郵地址,然後再繼續。 + 此驗證方法已在此帳戶中設定。 + 您已達到此帳戶的驗證方法數量上限。 diff --git a/auth/src/main/res/values-zh-rTW/strings.xml b/auth/src/main/res/values-zh-rTW/strings.xml index 63b993c5b7..706063fc6c 100755 --- a/auth/src/main/res/values-zh-rTW/strings.xml +++ b/auth/src/main/res/values-zh-rTW/strings.xml @@ -100,15 +100,15 @@ 已自動驗證電話號碼 重新傳送驗證碼 驗證電話號碼 - Use a different phone number + 使用其他電話號碼 輕觸 [%1$s] 後,系統將會傳送一封簡訊。您可能需支付簡訊和數據傳輸費用。 輕觸 [%1$s] 即表示您同意接受我們的《%2$s》和《%3$s》。系統將會傳送簡訊給您,不過您可能需要支付簡訊和數據傳輸費用。 - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + 驗證錯誤 + 重試 + 需要額外驗證。請完成雙重驗證。 + 需要連結帳戶。請嘗試其他登入方式。 + 驗證已取消。準備好後請再試一次。 選擇驗證方法 @@ -176,4 +176,14 @@ 多重身份验证当前已禁用 + 電子郵件地址或密碼不正確 + 此驗證工作階段已失效,請重新要求驗證碼。 + 電話驗證尚未完成,請再試一次。 + 這些憑證屬於其他帳號。 + 此電話號碼並未在這個帳號中設定為驗證方法。 + 您的登入工作階段已過期,請重新登入以繼續。 + 此連結已失效,請要求新的連結。 + 請先驗證您的電子郵件地址,然後再繼續。 + 此驗證方法已在這個帳號中設定。 + 您已達到這個帳號的驗證方法數量上限。 diff --git a/auth/src/main/res/values-zh/strings.xml b/auth/src/main/res/values-zh/strings.xml index ada6243e74..6d4aa5e0f8 100755 --- a/auth/src/main/res/values-zh/strings.xml +++ b/auth/src/main/res/values-zh/strings.xml @@ -100,7 +100,7 @@ 电话号码已自动验证 重新发送验证码 验证电话号码 - Use a different phone number + 使用其他电话号码 您点按“%1$s”后,系统会向您发送一条短信。这可能会产生短信费用和上网流量费。 点按"%1$s"即表示您接受我们的%2$s和%3$s。系统会向您发送一条短信。这可能会产生短信费用和上网流量费。 身份验证错误 @@ -175,4 +175,14 @@ 多重身份验证当前已禁用 + 电子邮件地址或密码不正确 + 该验证会话已失效,请重新获取验证码。 + 电话号码验证未完成,请重试。 + 这些凭据属于其他帐号。 + 该电话号码未在此帐号中设置为验证方式。 + 您的登录会话已过期,请重新登录以继续。 + 该链接已失效,请重新获取。 + 请先验证您的电子邮件地址,然后再继续。 + 该验证方式已在此帐号中设置。 + 您已达到此帐号的验证方式数量上限。 From ca2c0990d1d2eab1159f038e5c4fcbc0b1c59c0b Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Fri, 11 Sep 2026 20:36:09 +0100 Subject: [PATCH 3/8] fix(auth): stop auth errors leaking the Firebase SDK's English diagnostic --- .../com/firebase/ui/auth/AuthException.kt | 205 ++++++++-- .../string_provider/AuthUIStringProvider.kt | 20 + .../DefaultAuthUIStringProvider.kt | 9 + .../auth/ui/components/ErrorRecoveryDialog.kt | 31 +- auth/src/main/res/values/strings.xml | 5 + .../AuthExceptionRecoveryResolutionTest.kt | 188 ++++++++- .../com/firebase/ui/auth/AuthExceptionTest.kt | 3 +- .../ui/auth/FirebaseAuthErrorCodeSweepTest.kt | 371 ++++++++++++++++++ .../PasswordPolicyMessageLocalizationTest.kt | 313 +++++++++++++++ .../ErrorRecoveryDialogLogicTest.kt | 53 ++- 10 files changed, 1139 insertions(+), 59 deletions(-) create mode 100644 auth/src/test/java/com/firebase/ui/auth/FirebaseAuthErrorCodeSweepTest.kt create mode 100644 auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt diff --git a/auth/src/main/java/com/firebase/ui/auth/AuthException.kt b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt index 75940cbc23..f8153f33ba 100644 --- a/auth/src/main/java/com/firebase/ui/auth/AuthException.kt +++ b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt @@ -29,6 +29,7 @@ import com.google.firebase.auth.FirebaseAuthMultiFactorException import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException import com.google.firebase.auth.FirebaseAuthUserCollisionException import com.google.firebase.auth.FirebaseAuthWeakPasswordException +import java.util.Locale /** * Abstract base class representing all possible authentication exceptions in Firebase Auth UI. @@ -95,6 +96,26 @@ abstract class AuthException( cause: Throwable? = null ) : AuthException(message, cause) + /** + * The account exists, but the sign-in method the user just attempted is not available on it. + * + * The attempt was well-formed and the backend answered definitively that this method cannot + * be used for this account, so the error is not recoverable: the error dialog offers no retry + * and the copy points the user at a different way to sign in. + * + * The type is general, but the copy is not. `ERROR_PASSKEY_ENROLLMENT_NOT_FOUND` is the only + * code routed here today and the dialog's fallback for a blank message is `errorPasskeyNotFound`. + * Routing a second code here means giving it its own string and making that fallback a + * per-method choice. + * + * @property message The detailed error message + * @property cause The underlying [Throwable] that caused this exception + */ + class SignInMethodUnavailableException( + message: String, + cause: Throwable? = null + ) : AuthException(message, cause) + /** * The user account does not exist. * @@ -131,11 +152,16 @@ abstract class AuthException( * This exception is thrown when GIdP password policy enforcement is enabled and the supplied * password fails one or more configured constraints (e.g. minimum length, missing uppercase). * - * [message] is a newline-separated, human-readable description of each failing constraint - * as returned by the server, suitable for direct display in the UI. + * [message] is a newline-separated, human-readable description of each failing constraint, + * suitable for direct display in the UI. Built by [from], each constraint is translated copy + * where the library recognises the server's sentence, and the server's own English where it + * does not. + * + * [failingRequirements] keeps the **raw** server sentences, untranslated, for hosts that + * render the constraints themselves rather than showing [message]. * - * @property message Human-readable description of the failing constraints - * @property failingRequirements The individual constraint strings from the server + * @property message Translated description of the failing constraints + * @property failingRequirements The raw, untranslated constraint strings from the server * @property cause The underlying [Throwable] that caused this exception */ class PasswordPolicyViolationException( @@ -382,17 +408,24 @@ abstract class AuthException( * This method maps known Firebase exception types to their corresponding [AuthException] * subtypes, providing a consistent exception hierarchy for error handling. * - * **Mapping:** - * - [FirebaseException] → [NetworkException] (for network-related errors) - * - [FirebaseTooManyRequestsException] → [TooManyRequestsException] + * **Mapping**, in dispatch order. Several of these types extend one another, so the order + * is load-bearing rather than cosmetic: + * - [FirebaseAuthWeakPasswordException] → [WeakPasswordException], or + * [PasswordPolicyViolationException] when the diagnostic carries a GIdP password-policy + * rejection * - [FirebaseAuthInvalidCredentialsException] → [InvalidCredentialsException], with the - * message selected by `errorCode` + * message selected by `errorCode`; the `errorCode`s in that family that are developer + * setup faults rather than user error (custom token, OIDC nonce, authenticator + * response) → [MisconfigurationException] * - [FirebaseAuthInvalidUserException] → [UserNotFoundException] - * - [FirebaseAuthWeakPasswordException] → [WeakPasswordException] - * - [FirebaseAuthUserCollisionException] → [EmailAlreadyInUseException] * - [FirebaseAuthActionCodeException] → [InvalidCredentialsException] - * - [FirebaseAuthException] with a developer-setup `errorCode` → [MisconfigurationException] + * - [FirebaseAuthUserCollisionException] → [EmailAlreadyInUseException] * - [FirebaseAuthMultiFactorException] → [MfaRequiredException] + * - [FirebaseAuthRecentLoginRequiredException] → [InvalidCredentialsException] + * - [FirebaseAuthException] with a developer-setup `errorCode` → [MisconfigurationException] + * - [FirebaseTooManyRequestsException] → [TooManyRequestsException] + * - [FirebaseException] → [NetworkException] (for network-related errors), or + * [PasswordPolicyViolationException] when the message carries a policy rejection * - Other exceptions → [UnknownException] * * **Example:** @@ -427,14 +460,24 @@ abstract class AuthException( * This is the preferred overload; see the [Context] one above for the exception mapping * table and an example. * - * Given a non-null [stringProvider], the returned exception's `message` is always - * library-owned, translated copy, so it is safe for any consumer to render directly. Each - * branch resolves in this order: the blank-able hook scoped to the exception type, then - * the string for the specific Firebase `errorCode`, then the corresponding generic - * recovery message, and only then the Firebase SDK's own untranslated message. The - * exception is [MisconfigurationException], which never uses the SDK message at all — the - * raw diagnostic lives on `cause`. A `null` [stringProvider] has nothing to resolve - * against and falls back to the SDK message everywhere except [MisconfigurationException]. + * Given a non-null [stringProvider], the `message` on an exception returned by **this + * method** is library-owned translated copy, so it is safe to render directly. Each branch + * resolves in this order: the blank-able hook scoped to the exception type, then the + * string for the specific Firebase `errorCode`, then the corresponding generic recovery + * message, and only then the Firebase SDK's own untranslated message. + * [MisconfigurationException] never uses the SDK message at all — the raw diagnostic lives + * on `cause`. A `null` [stringProvider] has nothing to resolve against and falls back to + * the SDK message everywhere except [MisconfigurationException]. + * [PasswordPolicyViolationException] is partial by design: each requirement sentence the + * backend returns is translated when it is recognised and kept verbatim when it is not. + * + * The guarantee covers `from()` only. Subtypes constructed directly carry whatever + * `message` their caller passed, and the email-link subtypes + * ([InvalidEmailLinkException], [EmailLinkWrongDeviceException], + * [EmailLinkCrossDeviceLinkingException], [EmailLinkPromptForEmailException], + * [EmailLinkDifferentAnonymousUserException], [EmailMismatchException]) bake English into + * their own constructors. `getRecoveryMessage` keeps that out of the error dialog by + * resolving those types through [AuthUIStringProvider] instead of reading `message`. * * @param firebaseException The Firebase exception to convert * @param stringProvider Supplies localized message text; pass `config.stringProvider` @@ -453,15 +496,7 @@ abstract class AuthException( is FirebaseAuthWeakPasswordException -> { val sourceText = firebaseException.reason ?: firebaseException.message ?: "" if (sourceText.contains("PASSWORD_DOES_NOT_MEET_REQUIREMENTS", ignoreCase = true)) { - val requirements = parsePasswordPolicyRequirements(sourceText) - PasswordPolicyViolationException( - message = requirements.joinToString("\n").ifEmpty { - stringProvider?.errorWeakPasswordGeneric.nonEmpty() - ?: "Password does not meet policy requirements" - }, - failingRequirements = requirements, - cause = firebaseException - ) + passwordPolicyViolation(sourceText, firebaseException, stringProvider) } else { WeakPasswordException( message = stringProvider?.errorWeakPasswordGeneric.nonEmpty() @@ -603,14 +638,28 @@ abstract class AuthException( // diagnostic names the setup problem and means nothing to the user, so it // stays on `cause` while `message` carries renderable generic copy. "ERROR_INVALID_CUSTOM_TOKEN", - "ERROR_CUSTOM_TOKEN_MISMATCH" -> MisconfigurationException( + "ERROR_CUSTOM_TOKEN_MISMATCH", + // Same shape: the app built the federated request wrong. + "ERROR_MISSING_OR_INVALID_NONCE", + "ERROR_INVALID_AUTHENTICATOR_RESPONSE" -> MisconfigurationException( message = stringProvider?.unknownErrorRecoveryMessage.nonEmpty() ?: "An unknown error occurred.", cause = firebaseException ) + // Not InvalidCredentialsException, so its `typeLevel` hook is skipped too. + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND" -> SignInMethodUnavailableException( + message = stringProvider?.errorPasskeyNotFound.nonEmpty() + ?: firebaseException.message + ?: "We couldn't find a passkey for this account. " + + "Sign in another way.", + cause = firebaseException + ) + + // Unrecognised codes stay recoverable, but the copy must stay generic. else -> InvalidCredentialsException( message = typeLevel + ?: stringProvider?.unknownErrorRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Invalid credentials provided", cause = firebaseException @@ -727,8 +776,10 @@ abstract class AuthException( } is FirebaseAuthRecentLoginRequiredException -> { + // `errorRecentLoginRequired` ships blank; the MFA string says the same thing. InvalidCredentialsException( message = stringProvider?.errorRecentLoginRequired.nonEmpty() + ?: stringProvider?.mfaErrorRecentLoginRequired.nonEmpty() ?: firebaseException.message ?: "Recent login required for this operation", cause = firebaseException @@ -815,7 +866,8 @@ abstract class AuthException( "ERROR_INVALID_MESSAGE_PAYLOAD", "ERROR_INVALID_SENDER", "ERROR_INVALID_RECIPIENT_EMAIL", - // Host integration and project quota. + // Host integration and project quota. ERROR_MISSING_ACTIVITY ships: it is + // declared on the Recaptcha-activity exception, not in the SDK code table. "ERROR_MISSING_ACTIVITY", "ERROR_WEB_STORAGE_UNSUPPORTED", "ERROR_QUOTA_EXCEEDED" -> MisconfigurationException( @@ -850,15 +902,7 @@ abstract class AuthException( is FirebaseException -> { val msg = firebaseException.message ?: "" if (msg.contains("PASSWORD_DOES_NOT_MEET_REQUIREMENTS", ignoreCase = true)) { - val requirements = parsePasswordPolicyRequirements(msg) - PasswordPolicyViolationException( - message = requirements.joinToString("\n").ifEmpty { - stringProvider?.errorWeakPasswordGeneric.nonEmpty() - ?: "Password does not meet policy requirements" - }, - failingRequirements = requirements, - cause = firebaseException - ) + passwordPolicyViolation(msg, firebaseException, stringProvider) } else { NetworkException( message = stringProvider?.errorNetworkGeneric.nonEmpty() @@ -895,6 +939,89 @@ abstract class AuthException( private fun String?.nonEmpty(): String? = this?.ifEmpty { null } + /** + * Builds the [PasswordPolicyViolationException] for a GIdP password-policy rejection: + * `message` is localized one requirement sentence at a time, while `failingRequirements` + * keeps the backend's raw sentences. + */ + private fun passwordPolicyViolation( + sourceText: String, + cause: Exception, + stringProvider: AuthUIStringProvider? + ): PasswordPolicyViolationException { + val requirements = parsePasswordPolicyRequirements(sourceText) + return PasswordPolicyViolationException( + message = requirements + .joinToString("\n") { localizePasswordRequirement(it, stringProvider) ?: it } + .ifEmpty { + // `errorWeakPasswordGeneric` is the host's hook and ships blank. + stringProvider?.errorWeakPasswordGeneric.nonEmpty() + ?: stringProvider?.errorPasswordPolicyGeneric.nonEmpty() + ?: "Password does not meet policy requirements" + }, + failingRequirements = requirements, + cause = cause + ) + } + + /** + * Translates one GIdP password-policy requirement sentence, or returns `null` when the + * sentence is not recognised. + * + * The sentences are the backend's own English, e.g. "Password must contain at least 10 + * characters". On `null` the caller keeps that sentence verbatim, so a reworded or newly + * added requirement degrades to untranslated English rather than to a wrong message. + */ + private fun localizePasswordRequirement( + requirement: String, + stringProvider: AuthUIStringProvider? + ): String? { + if (stringProvider == null) return null + // GIdP writes "upper case" and "lower case" as two words; one word is accepted too. + val text = requirement.lowercase(Locale.ROOT) + return when { + text.contains("upper case") || text.contains("uppercase") -> + stringProvider.passwordMissingUppercase.nonEmpty() + + text.contains("lower case") || text.contains("lowercase") -> + stringProvider.passwordMissingLowercase.nonEmpty() + + // "numeric" is a substring of "non-alphanumeric": the guard keeps the two arms + // disjoint regardless of the order they are tested in. + text.contains("numeric") && !text.contains("non-alphanumeric") -> + stringProvider.passwordMissingDigit.nonEmpty() + + // Unverified wording: the probe project had special characters disabled. + text.contains("non-alphanumeric") || text.contains("special character") -> + stringProvider.passwordMissingSpecialCharacter.nonEmpty() + + // The number is the project's own configured minimum, so it is read out of the + // sentence rather than assumed. + text.contains("at least") -> + firstNumberIn(requirement)?.let { + stringProvider.passwordTooShort(it).nonEmpty() + } + + // "fewer than N" is exclusive, so the maximum passwordTooLong states is N - 1. + // Unverified wording: the probe project left the maximum at its 4096 default. + text.contains("fewer than") -> + firstNumberIn(requirement)?.let { + stringProvider.passwordTooLong(it - 1).nonEmpty() + } + + // "at most N" and "no more than N" are inclusive, so N is the maximum as written. + text.contains("at most") || text.contains("no more than") -> + firstNumberIn(requirement)?.let { + stringProvider.passwordTooLong(it).nonEmpty() + } + + else -> null + } + } + + private fun firstNumberIn(text: String): Int? = + Regex("\\d+").find(text)?.value?.toIntOrNull() + // Finds the [...] content that immediately follows PASSWORD_DOES_NOT_MEET_REQUIREMENTS // in both FirebaseException and FirebaseAuthWeakPasswordException messages. // GIdP returns human-readable requirement strings inside those brackets, e.g. diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt index 558a74d4b0..58d0e4b79a 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt @@ -184,6 +184,9 @@ interface AuthUIStringProvider { /** Error message when password doesn't meet minimum length requirement. Should support string formatting with minimum length parameter. */ fun passwordTooShort(minimumLength: Int): String + /** Error message when the password is longer than the maximum length allowed. Should support string formatting with maximum length parameter. */ + fun passwordTooLong(maximumLength: Int): String + /** Error message when password is missing at least one uppercase letter (A-Z) */ val passwordMissingUppercase: String @@ -669,4 +672,21 @@ interface AuthUIStringProvider { /** Error when the account already has the maximum number of verification methods. */ val errorMaximumSecondFactorCountExceeded: String get() = errorUnknownAuth + + /** + * Error when the password fails the project's password policy and the server named no + * individual requirement. When the server does name them, each one is rendered through + * [passwordTooShort], [passwordTooLong], [passwordMissingUppercase], + * [passwordMissingLowercase], [passwordMissingDigit] and [passwordMissingSpecialCharacter] + * instead, and this string is not used. + */ + val errorPasswordPolicyGeneric: String get() = errorWeakPasswordGeneric + + /** + * Error when the account has no passkey enrolled and the user has to sign in another way. + * + * Defaults to [errorUnknownAuth], not [errorInvalidCredentials]: this message is shown for a + * non-recoverable error, so the dialog offers no retry and credential copy would contradict it. + */ + val errorPasskeyNotFound: String get() = errorUnknownAuth } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt index 463cf5794c..70c4f9aaea 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt @@ -155,6 +155,9 @@ class DefaultAuthUIStringProvider( override fun passwordTooShort(minimumLength: Int): String = localizedContext.getString(R.string.fui_error_password_too_short, minimumLength) + override fun passwordTooLong(maximumLength: Int): String = + localizedContext.getString(R.string.fui_error_password_too_long, maximumLength) + override val passwordMissingUppercase: String get() = localizedContext.getString(R.string.fui_error_password_missing_uppercase) override val passwordMissingLowercase: String @@ -600,4 +603,10 @@ class DefaultAuthUIStringProvider( override val errorMaximumSecondFactorCountExceeded: String get() = localizedContext.getString(R.string.fui_error_maximum_second_factor_count_exceeded) + + override val errorPasswordPolicyGeneric: String + get() = localizedContext.getString(R.string.fui_error_password_policy_generic) + + override val errorPasskeyNotFound: String + get() = localizedContext.getString(R.string.fui_error_passkey_not_found) } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt index 483f775c94..e0a58f3cdb 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt @@ -155,15 +155,15 @@ internal fun getRecoveryMessage( error.message?.takeIf { it.isNotBlank() } ?: stringProvider.invalidCredentialsRecoveryMessage } + is AuthException.SignInMethodUnavailableException -> + // Passkey-specific fallback behind a general type — see the exception's KDoc. + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.errorPasskeyNotFound is AuthException.UserNotFoundException -> error.message?.takeIf { it.isNotBlank() } ?: stringProvider.userNotFoundRecoveryMessage is AuthException.WeakPasswordException -> { - // Include specific reason if available - val baseMessage = error.message?.takeIf { it.isNotBlank() } + // `error.reason` is untranslated SDK text, so it is deliberately not appended. + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.weakPasswordRecoveryMessage - error.reason?.let { reason -> - "$baseMessage\n\nReason: $reason" - } ?: baseMessage } is AuthException.PasswordPolicyViolationException -> { @@ -185,16 +185,18 @@ internal fun getRecoveryMessage( ?: stringProvider.tooManyRequestsRecoveryMessage is AuthException.PhoneVerificationCooldownException -> { // Use the custom message which includes remaining cooldown time - error.message ?: stringProvider.unknownErrorRecoveryMessage + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.unknownErrorRecoveryMessage } is AuthException.MfaRequiredException -> error.message?.takeIf { it.isNotBlank() } ?: stringProvider.mfaRequiredRecoveryMessage is AuthException.AccountLinkingRequiredException -> { // Use the custom message which includes email and provider details - error.message ?: stringProvider.accountLinkingRequiredRecoveryMessage + error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.accountLinkingRequiredRecoveryMessage } is AuthException.DifferentSignInMethodRequiredException -> { - error.message ?: stringProvider.accountLinkingRequiredRecoveryMessage + error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.accountLinkingRequiredRecoveryMessage } is AuthException.EmailMismatchException -> stringProvider.emailMismatchMessage is AuthException.InvalidEmailLinkException -> stringProvider.emailLinkInvalidLinkMessage @@ -237,14 +239,17 @@ internal fun getRecoveryActionText( is AuthException.EmailLinkPromptForEmailException -> stringProvider.continueText is AuthException.EmailLinkCrossDeviceLinkingException -> stringProvider.continueText is AuthException.EmailLinkWrongDeviceException -> stringProvider.continueText - is AuthException.EmailLinkDifferentAnonymousUserException -> stringProvider.dismissAction is AuthException.UserNotFoundException -> stringProvider.signupPageTitle // Navigate to sign-up when user not found + // Every type [isRecoverable] reports `false` for: retry must never be their answer. + is AuthException.EmailLinkDifferentAnonymousUserException, + is AuthException.MisconfigurationException, + is AuthException.SignInMethodUnavailableException, + is AuthException.TooManyRequestsException, + is AuthException.PhoneVerificationCooldownException -> stringProvider.dismissAction is AuthException.NetworkException, is AuthException.InvalidCredentialsException, is AuthException.WeakPasswordException, - is AuthException.PasswordPolicyViolationException, - is AuthException.TooManyRequestsException, - is AuthException.PhoneVerificationCooldownException -> stringProvider.retryAction + is AuthException.PasswordPolicyViolationException -> stringProvider.retryAction is AuthException.UnknownException -> stringProvider.retryAction else -> stringProvider.retryAction @@ -276,6 +281,8 @@ internal fun isRecoverable(error: AuthException): Boolean { is AuthException.EmailLinkWrongDeviceException -> true is AuthException.EmailLinkDifferentAnonymousUserException -> false is AuthException.MisconfigurationException -> false // Retrying cannot fix project setup + // The method is not available on this account; repeating it cannot change that. + is AuthException.SignInMethodUnavailableException -> false is AuthException.UnknownException -> true else -> true } diff --git a/auth/src/main/res/values/strings.xml b/auth/src/main/res/values/strings.xml index 6942dbbd9a..e61933a853 100644 --- a/auth/src/main/res/values/strings.xml +++ b/auth/src/main/res/values/strings.xml @@ -195,6 +195,9 @@ Passwords do not match Password must be at least %1$d characters long + + Password is too long. The maximum length is %1$d. Password must contain at least one uppercase letter Password must contain at least one lowercase letter Password must contain at least one number @@ -279,6 +282,8 @@ Verify your email address before you continue. That verification method is already set up on this account. You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + We couldn\'t find a passkey for this account. Sign in another way. Choose Authentication Method diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt index 577b3c41cf..90fd0627f3 100644 --- a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt @@ -18,20 +18,25 @@ import android.content.Context import androidx.test.core.app.ApplicationProvider import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.ui.components.getRecoveryActionText import com.firebase.ui.auth.ui.components.getRecoveryMessage +import com.firebase.ui.auth.ui.components.isRecoverable import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import com.google.firebase.FirebaseException import com.google.firebase.FirebaseTooManyRequestsException import com.google.firebase.auth.FirebaseAuthException import com.google.firebase.auth.FirebaseAuthInvalidUserException +import com.google.firebase.auth.FirebaseAuthMissingActivityForRecaptchaException import com.google.firebase.auth.FirebaseAuthMultiFactorException +import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException import com.google.firebase.auth.FirebaseAuthUserCollisionException import com.google.firebase.auth.FirebaseAuthWeakPasswordException import java.util.Locale import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock +import org.mockito.kotlin.doCallRealMethod import org.mockito.kotlin.whenever import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config @@ -70,6 +75,8 @@ class AuthExceptionRecoveryResolutionTest { "least 6 characters ]" private val emailInUseDiagnostic = "The email address is already in use by another account." private val mfaDiagnostic = "Please complete a second factor challenge." + private val recentLoginDiagnostic = "This operation is sensitive and requires recent " + + "authentication. Log in again before retrying this request." private val cancelledDiagnostic = "User cancelled the sign-in flow." private val operationNotAllowedDiagnostic = "This operation is not allowed. This may be " + "because the given sign-in provider is disabled for this Firebase project. Enable it " + @@ -136,8 +143,6 @@ class AuthExceptionRecoveryResolutionTest { val authException = AuthException.from(firebaseException, strings) - // getRecoveryMessage appends the SDK's untranslated `reason` on top of the base copy, so - // the invariant this test defends is the exception's own message. assertThat(authException.message).isEqualTo(strings.weakPasswordRecoveryMessage) assertThat(authException.message).isNotEqualTo(weakPasswordDiagnostic) assertThat(resolve(firebaseException)).startsWith(strings.weakPasswordRecoveryMessage) @@ -231,6 +236,7 @@ class AuthExceptionRecoveryResolutionTest { "ERROR_INVALID_MESSAGE_PAYLOAD", "ERROR_INVALID_SENDER", "ERROR_INVALID_RECIPIENT_EMAIL", + // Declared on FirebaseAuthMissingActivityForRecaptchaException's own constructor. "ERROR_MISSING_ACTIVITY", "ERROR_WEB_STORAGE_UNSUPPORTED", "ERROR_QUOTA_EXCEEDED", @@ -248,13 +254,34 @@ class AuthExceptionRecoveryResolutionTest { } } + @Test + fun `the SDK's own missing-activity exception type reaches the misconfiguration arm`() { + // The synthetic assertions elsewhere pin the `when` arm; this pins that the SDK's own + // type still reaches it, which an SDK release reparenting it would break silently. + val firebaseException = FirebaseAuthMissingActivityForRecaptchaException() + + val authException = AuthException.from(firebaseException, strings) + + assertThat(firebaseException.errorCode).isEqualTo("ERROR_MISSING_ACTIVITY") + assertThat(authException) + .isInstanceOf(AuthException.MisconfigurationException::class.java) + // Retrying cannot conjure the Activity the host never supplied. + assertThat(isRecoverable(authException)).isFalse() + assertThat(authException.message).isEqualTo(strings.unknownErrorRecoveryMessage) + assertThat(authException.message).doesNotContain("Recaptcha") + // The SDK's own English stays on the cause, where logs still reach it. + assertThat(authException.cause).isEqualTo(firebaseException) + assertThat(authException.cause?.message).contains("valid Activity is required") + } + // ============================================================================================= // Blanket invariant // ============================================================================================= @Test fun `no Firebase error code resolves to the raw SDK diagnostic`() { - // One representative code per arm of from(), plus codes that hit each else branch. + // One representative code per named arm of the FirebaseAuthInvalidCredentialsException + // branch of from(), then, below the blank line, the codes that fall through to its `else`. val codes = listOf( "ERROR_INVALID_CREDENTIAL", "ERROR_WRONG_PASSWORD", @@ -273,6 +300,13 @@ class AuthExceptionRecoveryResolutionTest { "ERROR_MISSING_MULTI_FACTOR_INFO", "ERROR_INVALID_MULTI_FACTOR_SESSION", "ERROR_INVALID_CUSTOM_TOKEN", + "ERROR_MISSING_OR_INVALID_NONCE", + "ERROR_INVALID_AUTHENTICATOR_RESPONSE", + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", + + // The first ships in firebase-auth 24.2.0; the second stands in for a future code. + "ERROR_REJECTED_CREDENTIAL", + "ERROR_SOME_FUTURE_CREDENTIAL_CODE", ) val diagnostic = "The Firebase SDK's own untranslated English." @@ -341,6 +375,117 @@ class AuthExceptionRecoveryResolutionTest { assertThat(resolved).isNotEqualTo(strings.userNotFoundRecoveryMessage) } + @Test + fun `a French device changing its email sees French, not the English reauth diagnostic`() { + val french = DefaultAuthUIStringProvider(context, Locale.FRENCH) + val firebaseException = FirebaseAuthRecentLoginRequiredException( + "ERROR_REQUIRES_RECENT_LOGIN", + recentLoginDiagnostic + ) + + val resolved = resolve(firebaseException, french) + + // `errorRecentLoginRequired` ships blank, so the arm falls to the MFA string. + assertThat(resolved).isEqualTo(french.mfaErrorRecentLoginRequired) + assertThat(resolved).isNotEqualTo(recentLoginDiagnostic) + assertThat(resolved).isNotEqualTo(strings.mfaErrorRecentLoginRequired) + } + + @Test + fun `reauthentication required resolves to library copy, not the SDK diagnostic`() { + val firebaseException = FirebaseAuthRecentLoginRequiredException( + "ERROR_REQUIRES_RECENT_LOGIN", + recentLoginDiagnostic + ) + + val result = AuthException.from(firebaseException, strings) + + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(result.message).isEqualTo(strings.mfaErrorRecentLoginRequired) + assertThat(result.cause?.message).isEqualTo(recentLoginDiagnostic) + assertThat(resolve(firebaseException)).isEqualTo(strings.mfaErrorRecentLoginRequired) + } + + // ============================================================================================= + // Developer-setup faults in the invalid-credential family + // ============================================================================================= + + @Test + fun `a bad Sign in with Apple nonce is reported as a misconfiguration, not a bad password`() { + val diagnostic = "The supplied auth credential is malformed, has expired or is " + + "currently unsupported. [ MISSING_OR_INVALID_NONCE ]" + + for (code in listOf("ERROR_MISSING_OR_INVALID_NONCE", "ERROR_INVALID_AUTHENTICATOR_RESPONSE")) { + val firebaseException = + com.google.firebase.auth.FirebaseAuthInvalidCredentialsException(code, diagnostic) + val result = AuthException.from(firebaseException, strings) + + // The host built the federated request wrong, so this must not be recoverable. + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.MisconfigurationException::class.java) + assertWithMessage(code).that(result.message) + .isEqualTo(strings.unknownErrorRecoveryMessage) + assertWithMessage(code).that(result.cause?.message).isEqualTo(diagnostic) + } + } + + @Test + fun `unnamed invalid-credential codes stay recoverable but never show the SDK diagnostic`() { + val diagnostic = "The Firebase SDK's own untranslated English." + + for (code in listOf("ERROR_REJECTED_CREDENTIAL", "ERROR_SOME_FUTURE_CREDENTIAL_CODE")) { + val firebaseException = + com.google.firebase.auth.FirebaseAuthInvalidCredentialsException(code, diagnostic) + val result = AuthException.from(firebaseException, strings) + + // "Mismatching credentials" also covers the wrong account, which signing in fixes. + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.InvalidCredentialsException::class.java) + // But the copy has to be generic — we do not know what the code means. + assertWithMessage(code).that(result.message) + .isEqualTo(strings.unknownErrorRecoveryMessage) + assertWithMessage(code).that(result.message).isNotEqualTo(diagnostic) + assertWithMessage(code).that(result.cause?.message).isEqualTo(diagnostic) + } + } + + @Test + fun `a missing passkey enrolment points at another sign-in method, not a futile retry`() { + val diagnostic = "Cannot find the passkey linked to the current account." + val firebaseException = com.google.firebase.auth.FirebaseAuthInvalidCredentialsException( + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", diagnostic + ) + + val result = AuthException.from(firebaseException, strings) + + // Not InvalidCredentialsException: that is recoverable, so the dialog would offer a retry. + assertThat(result).isInstanceOf(AuthException.SignInMethodUnavailableException::class.java) + assertThat(result).isNotInstanceOf(AuthException.InvalidCredentialsException::class.java) + // Specific copy, not the generic unknown-error string. + assertThat(result.message).isEqualTo(strings.errorPasskeyNotFound) + assertThat(result.message).isNotEqualTo(strings.unknownErrorRecoveryMessage) + assertThat(result.message).isNotEqualTo(diagnostic) + assertThat(result.cause?.message).isEqualTo(diagnostic) + } + + @Test + fun `the dialog offers no retry action for a missing passkey enrolment`() { + val firebaseException = com.google.firebase.auth.FirebaseAuthInvalidCredentialsException( + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", + "Cannot find the passkey linked to the current account." + ) + + val result = AuthException.from(firebaseException, strings) + + // The dialog renders the action button only when isRecoverable is true. + assertThat(isRecoverable(result)).isFalse() + // And the text itself is not a retry invitation, for any caller reading it directly. + assertThat(getRecoveryActionText(result, strings)).isNotEqualTo(strings.retryAction) + assertThat(getRecoveryActionText(result, strings)).isEqualTo(strings.dismissAction) + // The body still says the useful thing. + assertThat(getRecoveryMessage(result, strings)).isEqualTo(strings.errorPasskeyNotFound) + } + // ============================================================================================= // A host's own hook still wins // ============================================================================================= @@ -355,4 +500,41 @@ class AuthExceptionRecoveryResolutionTest { assertThat(result.message).isEqualTo("Host network copy") } + + @Test + fun `a host's credential copy does not hijack the missing-passkey message`() { + // `errorInvalidCredentials` is the hook for a type this arm deliberately does not return, + // so a host overriding both must see its passkey copy, not its password copy. + val hostStrings = mock(AuthUIStringProvider::class.java) + whenever(hostStrings.errorInvalidCredentials) + .thenReturn("Check your password and try again.") + whenever(hostStrings.errorPasskeyNotFound).thenReturn("Use another way to sign in.") + + val result = AuthException.from( + com.google.firebase.auth.FirebaseAuthInvalidCredentialsException( + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", + "Cannot find the passkey linked to the current account." + ), + hostStrings + ) + + assertThat(result).isInstanceOf(AuthException.SignInMethodUnavailableException::class.java) + assertThat(result.message).isEqualTo("Use another way to sign in.") + assertThat(result.message).isNotEqualTo("Check your password and try again.") + } + + @Test + fun `a host that leaves the passkey hook unset gets the generic string, not credential copy`() { + // The interface default is what a host implementing AuthUIStringProvider directly sees; + // credential copy there would sit in a dialog with no retry button. + val hostStrings = mock(AuthUIStringProvider::class.java) + doCallRealMethod().whenever(hostStrings).errorPasskeyNotFound + whenever(hostStrings.errorInvalidCredentials).thenReturn("Check your password and try again.") + whenever(hostStrings.errorUnknownAuth).thenReturn("Something went wrong. Please try later.") + + assertThat(hostStrings.errorPasskeyNotFound) + .isEqualTo("Something went wrong. Please try later.") + assertThat(hostStrings.errorPasskeyNotFound) + .isNotEqualTo("Check your password and try again.") + } } diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt index 978c84c9fe..6109f7c5dd 100644 --- a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt @@ -517,7 +517,8 @@ class AuthExceptionTest { "ERROR_INVALID_MESSAGE_PAYLOAD", "ERROR_INVALID_SENDER", "ERROR_INVALID_RECIPIENT_EMAIL", - // Host integration and project quota. + // Host integration and project quota. The synthetic exception below pins the `when` + // arm; AuthExceptionRecoveryResolutionTest drives the SDK's own missing-activity type. "ERROR_MISSING_ACTIVITY", "ERROR_WEB_STORAGE_UNSUPPORTED", "ERROR_QUOTA_EXCEEDED", diff --git a/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthErrorCodeSweepTest.kt b/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthErrorCodeSweepTest.kt new file mode 100644 index 0000000000..5a4bc0db03 --- /dev/null +++ b/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthErrorCodeSweepTest.kt @@ -0,0 +1,371 @@ +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * 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. + */ + +package com.firebase.ui.auth + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.ui.components.getRecoveryMessage +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import com.google.firebase.auth.FirebaseAuthActionCodeException +import com.google.firebase.auth.FirebaseAuthEmailException +import com.google.firebase.auth.FirebaseAuthException +import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException +import com.google.firebase.auth.FirebaseAuthInvalidUserException +import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException +import com.google.firebase.auth.FirebaseAuthUserCollisionException +import com.google.firebase.auth.FirebaseAuthWeakPasswordException +import com.google.firebase.auth.FirebaseAuthWebException +import java.io.DataInputStream +import java.io.File +import java.io.InputStream +import java.net.URLDecoder +import java.util.Locale +import java.util.jar.JarFile +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * Exhaustive sweep of the invariant `AuthException.from` is built on: given a non-null + * [AuthUIStringProvider], the resulting `message` — and the dialog body rendered from it — is + * always library-owned copy, never the Firebase SDK's untranslated English diagnostic. + * + * Unlike the hand-written allowlists in the other suites here, this one does not let the author + * choose the inputs: it reads every `ERROR_*` literal out of the firebase-auth artifact on the + * classpath and drives all of them through every constructible `FirebaseAuthException` subtype. + * A firebase-auth upgrade that adds error codes therefore widens the suite automatically. + * + * @suppress Internal test class + */ +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) +class FirebaseAuthErrorCodeSweepTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + private val strings: AuthUIStringProvider = DefaultAuthUIStringProvider(context) + + /** + * A diagnostic that cannot collide with any library string, so `doesNotContain` is a sound + * test for "the SDK text leaked into what the user sees". + */ + private val diagnostic = + "ZZ_RAW_SDK_DIAGNOSTIC_ZZ the Firebase SDK's own untranslated English." + + /** Every shape a Firebase error code can arrive in that `from()` dispatches on by type. */ + private fun shapesFor(code: String): List> = listOf( + "FirebaseAuthException" to object : FirebaseAuthException(code, diagnostic) {}, + "FirebaseAuthInvalidCredentialsException" to + FirebaseAuthInvalidCredentialsException(code, diagnostic), + "FirebaseAuthWeakPasswordException" to + FirebaseAuthWeakPasswordException(code, diagnostic, diagnostic), + "FirebaseAuthInvalidUserException" to FirebaseAuthInvalidUserException(code, diagnostic), + "FirebaseAuthUserCollisionException" to + FirebaseAuthUserCollisionException(code, diagnostic), + "FirebaseAuthRecentLoginRequiredException" to + FirebaseAuthRecentLoginRequiredException(code, diagnostic), + "FirebaseAuthActionCodeException" to FirebaseAuthActionCodeException(code, diagnostic), + "FirebaseAuthEmailException" to FirebaseAuthEmailException(code, diagnostic), + "FirebaseAuthWebException" to FirebaseAuthWebException(code, diagnostic), + ) + + // ============================================================================================= + // The extraction itself — if this is wrong, the sweep below proves nothing + // ============================================================================================= + + @Test + fun `the error codes are read from the firebase-auth artifact on the classpath`() { + val codes = FIREBASE_AUTH_ERROR_CODES + + // An empty read makes every other assertion in this file vacuous; this bound catches it. + assertWithMessage( + "Expected ~81 ERROR_* literals across the artifacts holding " + + "$ARTIFACT_PROBE_CLASSES, found ${codes.size}. Either the artifact layout " + + "changed, or a probe class was renamed by an SDK upgrade. Fix the " + + "extraction — do not lower this bound, and do not delete this test." + ).that(codes.size).isAtLeast(79) + + // Anchors across unrelated families, so a partial read cannot pass. + assertThat(codes).containsAtLeast( + "ERROR_INVALID_CREDENTIAL", + "ERROR_USER_NOT_FOUND", + "ERROR_EMAIL_ALREADY_IN_USE", + "ERROR_REQUIRES_RECENT_LOGIN", + "ERROR_OPERATION_NOT_ALLOWED", + "ERROR_SESSION_EXPIRED", + ) + + // The invalid-credential-family codes; ERROR_REJECTED_CREDENTIAL reaches the `else` arm. + assertThat(codes).containsAtLeast( + "ERROR_MISSING_OR_INVALID_NONCE", + "ERROR_REJECTED_CREDENTIAL", + "ERROR_INVALID_AUTHENTICATOR_RESPONSE", + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", + ) + } + + @Test + fun `the extraction reads the whole artifact, not just the internal error-code table`() { + // Not redundant with the size bound above: a read narrowed back to the internal table + // still yields 80 of 81 codes and passes that bound, but fails here. + assertThat(FIREBASE_AUTH_ERROR_CODES).contains("ERROR_MISSING_ACTIVITY") + assertThat(ERROR_CODES_IN_INTERNAL_TABLE).doesNotContain("ERROR_MISSING_ACTIVITY") + + assertWithMessage( + "Every code the internal table declares must still be in the artifact-wide read; " + + "if it is not, the widened extraction is dropping classes." + ).that(FIREBASE_AUTH_ERROR_CODES).containsAtLeastElementsIn(ERROR_CODES_IN_INTERNAL_TABLE) + + assertWithMessage( + "The artifact-wide read must be strictly wider than the internal table — that is the " + + "entire point of widening it." + ).that(FIREBASE_AUTH_ERROR_CODES.size) + .isGreaterThan(ERROR_CODES_IN_INTERNAL_TABLE.size) + } + + @Test + fun `from() names no error code that the firebase-auth artifact does not ship`() { + // The sweep only drives codes that exist, so an arm on a code the SDK never produces is + // invisible to it; this runs the check the other way round. + assertWithMessage( + "No ERROR_* literals found in $FROM_HOLDER_CLASS. `from()` was refactored so its " + + "`when` arms no longer compile into that class, or the class was renamed. " + + "Fix the extraction — do not delete this test." + ).that(NAMED_BY_FROM.size).isAtLeast(60) + + val shipped = FIREBASE_AUTH_ERROR_CODES.toSet() + val named = NAMED_BY_FROM.filterNot { it in shipped } + + assertWithMessage( + "AuthException.from() dispatches on these error codes, none of which exist in the " + + "firebase-auth artifact on the classpath. Each is an unreachable branch: " + + "either the SDK retired the code, or it was never spelled correctly." + ).that(named).isEmpty() + } + + // ============================================================================================= + // The sweep + // ============================================================================================= + + @Test + fun `no shipped error code leaks the SDK diagnostic into the message`() { + for (code in FIREBASE_AUTH_ERROR_CODES) { + for ((shape, firebaseException) in shapesFor(code)) { + val resolved = AuthException.from(firebaseException, strings).message + assertWithMessage("%s as %s -> message", code, shape) + .that(resolved.orEmpty()).doesNotContain(diagnostic) + } + } + } + + @Test + fun `no shipped error code leaks the SDK diagnostic into the error dialog`() { + for (code in FIREBASE_AUTH_ERROR_CODES) { + for ((shape, firebaseException) in shapesFor(code)) { + val authException = AuthException.from(firebaseException, strings) + val rendered = getRecoveryMessage(authException, strings) + + assertWithMessage("%s as %s -> dialog body", code, shape) + .that(rendered).doesNotContain(diagnostic) + // The dialog must still say something; an empty body is its own defect. + assertWithMessage("%s as %s -> dialog body is blank", code, shape) + .that(rendered.isBlank()).isFalse() + // The developer still needs the diagnostic, so it has to survive on the cause. + assertWithMessage("%s as %s -> cause", code, shape) + .that(authException.cause?.message).isEqualTo(diagnostic) + } + } + } + + @Test + fun `no shipped error code leaks the SDK diagnostic on a French device`() { + val french = DefaultAuthUIStringProvider(context, Locale.FRENCH) + + for (code in FIREBASE_AUTH_ERROR_CODES) { + for ((shape, firebaseException) in shapesFor(code)) { + val rendered = getRecoveryMessage(AuthException.from(firebaseException, french), french) + assertWithMessage("%s as %s -> French dialog body", code, shape) + .that(rendered).doesNotContain(diagnostic) + } + } + } + + companion object { + /** + * Landmarks used to locate the artifacts to read. Whichever jar or class directory holds + * each of these is read in full; duplicate containers are read once. + * + * A landmark must be a class only firebase-auth itself ships, because + * `com.google.firebase.auth` is a split package. Do not add + * `com.google.firebase.auth.FirebaseAuthException`: it resolves to firebase-auth-interop, + * whose jar holds zero `ERROR_*` literals, so it reads as "the artifact ships no codes". + */ + val ARTIFACT_PROBE_CLASSES = listOf( + "com.google.firebase.auth.FirebaseAuthMissingActivityForRecaptchaException", + ) + + /** + * Most of firebase-auth's error-code literals live in this one internal class, as + * constant-pool entries rather than declared fields, so they are read out of the class + * file rather than by reflection. + * + * It is named here only so the assertions can say how the whole-artifact total splits. + * **The sweep must not scope itself to this class**: `ERROR_MISSING_ACTIVITY` is declared + * on [com.google.firebase.auth.FirebaseAuthMissingActivityForRecaptchaException]'s + * constructor, not in this table, so a narrower scan reports it unshipped. + */ + const val ERROR_CODE_HOLDER_CLASS = "com.google.android.gms.internal.firebase-auth-api.zzaew" + + /** + * Every `ERROR_*` literal in **every class of** the firebase-auth artifact resolved onto + * this test's classpath, extracted at runtime. Nothing here is hardcoded, so an SDK upgrade + * that adds codes — in the internal table or on a public exception class — widens the sweep + * without anyone remembering to update a list. + */ + val FIREBASE_AUTH_ERROR_CODES: List by lazy { + ARTIFACT_PROBE_CLASSES + .mapNotNull { containerOf(it) } + .distinct() + .flatMap { readUtf8ConstantPools(it) } + .filter { it.matches(Regex("ERROR_[A-Z0-9_]+")) } + .distinct() + .sorted() + } + + /** The subset of [FIREBASE_AUTH_ERROR_CODES] that the old single-class scope could see. */ + val ERROR_CODES_IN_INTERNAL_TABLE: List by lazy { + readUtf8ConstantPool(ERROR_CODE_HOLDER_CLASS) + .filter { it.matches(Regex("ERROR_[A-Z0-9_]+")) } + .distinct() + .sorted() + } + + /** + * Where `AuthException.from` compiles to. Its `when` arms dispatch on string literals, + * which land in this class's constant pool exactly like the SDK's own do. + */ + const val FROM_HOLDER_CLASS = "com.firebase.ui.auth.AuthException\$Companion" + + /** + * Every `ERROR_*` literal `from()` dispatches on, read back out of its own bytecode for + * the same reason the SDK's list is: a hand-maintained copy here would drift from the + * `when` it is supposed to describe, which is the drift this whole suite exists to catch. + */ + val NAMED_BY_FROM: List by lazy { + readUtf8ConstantPool(FROM_HOLDER_CLASS) + .filter { it.matches(Regex("ERROR_[A-Z0-9_]+")) } + .distinct() + .sorted() + } + + /** + * The jar or class directory that [probeClassName] was loaded from, or `null` when the + * class does not resolve or arrives in a shape this does not understand. Gradle resolves + * an AAR's `classes.jar` either as a jar on the classpath or as a directory of extracted + * `.class` files depending on the transform, so both are handled; anything else yields + * `null`, which ends as an empty code list and trips the size guard in the first test + * rather than passing the suite vacuously. + */ + private fun containerOf(probeClassName: String): File? { + val url = FirebaseAuthErrorCodeSweepTest::class.java.classLoader + ?.getResource(probeClassName.replace('.', '/') + ".class") + ?: return null + + return when (url.protocol) { + // "jar:file:/path/to/classes.jar!/com/google/firebase/auth/…" + "jar" -> File( + URLDecoder.decode(url.path.substringBefore("!/").removePrefix("file:"), "UTF-8") + ) + + // Walk the package path back off the file to reach the classpath root: one + // directory per package segment, which is one per '.' in the class name. + "file" -> generateSequence( + File(URLDecoder.decode(url.path, "UTF-8")).parentFile + ) { it.parentFile } + .drop(probeClassName.count { it == '.' }) + .firstOrNull() + + else -> null + } + } + + /** Every CONSTANT_Utf8 entry in every class of [container]. */ + private fun readUtf8ConstantPools(container: File): List = + if (container.isDirectory) { + container.walkTopDown() + .filter { it.isFile && it.extension == "class" } + .flatMap { file -> + file.inputStream().use { readUtf8ConstantPool(it, file.path) }.asSequence() + } + .toList() + } else { + JarFile(container).use { jar -> + jar.entries().asSequence() + .filter { it.name.endsWith(".class") } + .flatMap { entry -> + jar.getInputStream(entry) + .use { readUtf8ConstantPool(it, entry.name) } + .asSequence() + } + .toList() + } + } + + /** + * Returns every CONSTANT_Utf8 entry in [className]'s constant pool. Walking the pool is + * the only way to see string literals that exist solely inside a method body. + */ + private fun readUtf8ConstantPool(className: String): List { + val resource = className.replace('.', '/') + ".class" + val stream = FirebaseAuthErrorCodeSweepTest::class.java.classLoader + ?.getResourceAsStream(resource) + ?: return emptyList() + + return stream.use { readUtf8ConstantPool(it, resource) } + } + + /** Reads one class file's CONSTANT_Utf8 entries. [resource] only names it in failures. */ + private fun readUtf8ConstantPool(source: InputStream, resource: String): List { + return DataInputStream(source.buffered()).let { input -> + require(input.readInt() == -0x35014542) { "$resource is not a class file" } + input.readUnsignedShort() // minor version + input.readUnsignedShort() // major version + + val entryCount = input.readUnsignedShort() + val strings = mutableListOf() + var index = 1 + while (index < entryCount) { + when (val tag = input.readUnsignedByte()) { + 1 -> strings += input.readUTF() + 7, 8, 16, 19, 20 -> input.skipBytes(2) + 15 -> input.skipBytes(3) + 3, 4, 9, 10, 11, 12, 17, 18 -> input.skipBytes(4) + // Long and Double occupy two constant-pool slots each. + 5, 6 -> { + input.skipBytes(8) + index++ + } + else -> error("Unknown constant pool tag $tag at $index in $resource") + } + index++ + } + strings + } + } + } +} diff --git a/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt b/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt new file mode 100644 index 0000000000..5a12ceb882 --- /dev/null +++ b/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt @@ -0,0 +1,313 @@ +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * 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. + */ + +package com.firebase.ui.auth + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.ui.components.getRecoveryMessage +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import com.google.firebase.FirebaseException +import com.google.firebase.auth.FirebaseAuthWeakPasswordException +import java.util.Locale +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * Covers the Google Identity Platform password-policy path, which [FirebaseAuthErrorCodeSweepTest] + * structurally cannot reach. + * + * That sweep's synthetic diagnostic never carries the `PASSWORD_DOES_NOT_MEET_REQUIREMENTS` + * marker, so neither policy branch is ever entered. The fixtures below are the real backend + * strings, captured from a project with `Require` enforcement and a minimum length of 10. + * + * @suppress Internal test class + */ +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) +class PasswordPolicyMessageLocalizationTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + private val strings: AuthUIStringProvider = DefaultAuthUIStringProvider(context) + private val french: AuthUIStringProvider = DefaultAuthUIStringProvider(context, Locale.FRENCH) + + // ============================================================================================= + // The real backend strings + // ============================================================================================= + + private companion object { + /** Verbatim single-constraint probe responses. */ + const val MIN_LENGTH = "Password must contain at least 10 characters" + const val UPPER_CASE = "Password must contain an upper case character" + const val LOWER_CASE = "Password must contain a lower case character" + const val NUMERIC = "Password must contain a numeric character" + + /** + * Unverified: the probe project has special characters disabled. GIdP's wording is + * "non-alphanumeric", which is where the substring collision with [NUMERIC] comes from. + */ + const val NON_ALPHANUMERIC = "Password must contain a non-alphanumeric character" + + /** All failing requirements come back together, comma-separated, inside one bracket. */ + const val ALL_THREE = + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS : Missing password requirements: " + + "[$MIN_LENGTH, $UPPER_CASE, $NUMERIC]" + + /** + * The marker with nothing parseable after it. `parsePasswordPolicyRequirements` returns an + * empty list, and the whole message is then the generic string. + */ + const val UNPARSEABLE = "PASSWORD_DOES_NOT_MEET_REQUIREMENTS" + + val ENGLISH_REQUIREMENT_SENTENCES = + listOf(MIN_LENGTH, UPPER_CASE, LOWER_CASE, NUMERIC) + } + + /** The two exception shapes that carry a policy rejection into `from()`. */ + private fun shapesFor(sourceText: String): List> = listOf( + // `from()` reads `reason` first for this type. + "FirebaseAuthWeakPasswordException" to + FirebaseAuthWeakPasswordException("ERROR_WEAK_PASSWORD", sourceText, sourceText), + "FirebaseException" to FirebaseException(sourceText), + ) + + // ============================================================================================= + // The leak the sweep could not see + // ============================================================================================= + + @Test + fun `a parsed policy rejection never renders the backend's English sentences`() { + for ((shape, exception) in shapesFor(ALL_THREE)) { + val rendered = getRecoveryMessage(AuthException.from(exception, strings), strings) + + for (sentence in ENGLISH_REQUIREMENT_SENTENCES) { + assertWithMessage("%s -> dialog body still contains %s", shape, sentence) + .that(rendered).doesNotContain(sentence) + } + assertWithMessage("%s -> dialog body is blank", shape) + .that(rendered.isBlank()).isFalse() + } + } + + @Test + fun `an unparseable policy rejection never renders the old hardcoded literal`() { + for ((shape, exception) in shapesFor(UNPARSEABLE)) { + val rendered = getRecoveryMessage(AuthException.from(exception, strings), strings) + + // `errorWeakPasswordGeneric` is the host's hook and ships blank. + assertWithMessage("%s -> dialog body fell back to the hardcoded literal", shape) + .that(rendered).doesNotContain("Password does not meet policy requirements") + assertWithMessage("%s -> dialog body", shape) + .that(rendered).isEqualTo(strings.errorPasswordPolicyGeneric) + } + } + + @Test + fun `both policy paths resolve to French on a French device`() { + for (sourceText in listOf(ALL_THREE, UNPARSEABLE)) { + for ((shape, exception) in shapesFor(sourceText)) { + val rendered = getRecoveryMessage(AuthException.from(exception, french), french) + + for (sentence in ENGLISH_REQUIREMENT_SENTENCES) { + assertWithMessage("%s -> French dialog body contains %s", shape, sentence) + .that(rendered).doesNotContain(sentence) + } + assertWithMessage("%s -> French dialog body is not French", shape) + .that(rendered).contains("mot de passe") + } + } + } + + // ============================================================================================= + // What the mapping actually produces + // ============================================================================================= + + @Test + fun `each requirement maps to the library's own translated copy`() { + val rendered = getRecoveryMessage( + AuthException.from(FirebaseException(ALL_THREE), strings), strings + ) + + // The project's own minimum, read back out of the sentence — not the 6 that + // `weakPasswordRecoveryMessage` hardcodes. + assertThat(rendered).contains(strings.passwordTooShort(10)) + assertThat(rendered).contains(strings.passwordMissingUppercase) + assertThat(rendered).contains(strings.passwordMissingDigit) + // Only the three that failed. + assertThat(rendered).doesNotContain(strings.passwordMissingLowercase) + assertThat(rendered.lines()).hasSize(3) + } + + @Test + fun `the lower case requirement maps too`() { + // Not in ALL_THREE, so it needs its own fixture to be covered at all. + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException("PASSWORD_DOES_NOT_MEET_REQUIREMENTS: [$LOWER_CASE]"), strings + ), + strings + ) + + assertThat(rendered).isEqualTo(strings.passwordMissingLowercase) + } + + @Test + fun `the non-alphanumeric requirement maps to the special-character copy, not the digit one`() { + // "numeric" is a substring of "non-alphanumeric", so the digit arm can swallow this. + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException("PASSWORD_DOES_NOT_MEET_REQUIREMENTS: [$NON_ALPHANUMERIC]"), + strings + ), + strings + ) + + assertThat(rendered).isEqualTo(strings.passwordMissingSpecialCharacter) + assertThat(rendered).isNotEqualTo(strings.passwordMissingDigit) + } + + @Test + fun `the digit and special-character requirements stay distinct when both fail`() { + // The pair must resolve to two different strings whichever arm is tested first. + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: [$NUMERIC, $NON_ALPHANUMERIC]" + ), + strings + ), + strings + ) + + assertThat(rendered.lines()).containsExactly( + strings.passwordMissingDigit, + strings.passwordMissingSpecialCharacter, + ).inOrder() + } + + @Test + fun `the spelled-out special character wording maps too`() { + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: " + + "[Password must contain a special character]" + ), + strings + ), + strings + ) + + assertThat(rendered).isEqualTo(strings.passwordMissingSpecialCharacter) + } + + @Test + fun `an exclusive maximum-length requirement states the maximum, not the bound`() { + // "fewer than 4096" permits 4095, and passwordTooLong renders the maximum, not the bound. + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: " + + "[Password must contain fewer than 4096 characters]" + ), + strings + ), + strings + ) + + assertThat(rendered).isEqualTo(strings.passwordTooLong(4095)) + assertThat(rendered).isNotEqualTo(strings.passwordTooLong(4096)) + } + + @Test + fun `an inclusive maximum-length requirement takes the number as written`() { + // "at most N" and "no more than N" are inclusive, so no adjustment applies. + for (wording in listOf("at most 64", "no more than 64")) { + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: " + + "[Password must contain $wording characters]" + ), + strings + ), + strings + ) + + assertWithMessage(wording).that(rendered).isEqualTo(strings.passwordTooLong(64)) + } + } + + @Test + fun `an unrecognised requirement is kept verbatim rather than dropped`() { + val reworded = "Password must not be one of your last 5 passwords" + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: [$UPPER_CASE, $reworded]" + ), + strings + ), + strings + ) + + // The known one is still translated... + assertThat(rendered).contains(strings.passwordMissingUppercase) + // ...and the unknown one is kept verbatim instead of vanishing or blanking the message. + assertThat(rendered).contains(reworded) + } + + @Test + fun `failingRequirements keeps the raw untranslated sentences`() { + val exception = AuthException.from(FirebaseException(ALL_THREE), french) + + assertThat(exception).isInstanceOf(AuthException.PasswordPolicyViolationException::class.java) + val policy = exception as AuthException.PasswordPolicyViolationException + + // This list stays raw even when `message` is French. + assertThat(policy.failingRequirements) + .containsExactly(MIN_LENGTH, UPPER_CASE, NUMERIC).inOrder() + assertThat(policy.message).doesNotContain(MIN_LENGTH) + } + + @Test + fun `a policy rejection stays a PasswordPolicyViolationException, not a WeakPasswordException`() { + for ((shape, exception) in shapesFor(ALL_THREE)) { + assertWithMessage("%s", shape).that(AuthException.from(exception, strings)) + .isInstanceOf(AuthException.PasswordPolicyViolationException::class.java) + } + + // A plain weak-password rejection must not be pulled into the policy type. + val plain = FirebaseAuthWeakPasswordException( + "ERROR_WEAK_PASSWORD", "Password should be at least 6 characters", + "Password should be at least 6 characters" + ) + assertThat(AuthException.from(plain, strings)) + .isInstanceOf(AuthException.WeakPasswordException::class.java) + } + + @Test + fun `a null string provider still leaves the backend sentences readable`() { + // Nothing to resolve against, so the raw sentences are the only output. + val resolved = AuthException.from(FirebaseException(ALL_THREE), null as AuthUIStringProvider?) + + assertThat(resolved.message).contains(MIN_LENGTH) + assertThat(resolved.message).contains(UPPER_CASE) + } +} diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt index d1b70a08b5..5cce9ff04a 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt @@ -6,6 +6,7 @@ import com.google.common.truth.Truth import com.google.firebase.auth.EmailAuthProvider import com.google.firebase.auth.FirebaseAuthException import com.google.firebase.auth.GoogleAuthProvider +import java.lang.reflect.Modifier import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito @@ -140,8 +141,8 @@ class ErrorRecoveryDialogLogicTest { } @Test - fun `getRecoveryMessage returns weak password message with reason for WeakPasswordException`() { - // Arrange + fun `getRecoveryMessage drops the untranslated reason for WeakPasswordException`() { + // Arrange - the reason is the raw SDK string, English in every locale. val error = AuthException.WeakPasswordException( "", null, @@ -151,8 +152,11 @@ class ErrorRecoveryDialogLogicTest { // Act val message = getRecoveryMessage(error, mockStringProvider) - // Assert - blank message, so the provider string supplies the base and the reason is kept - Truth.assertThat(message).isEqualTo("Password not strong enough. Use at least 6 characters and a mix of letters and numbers\n\nReason: Password should be at least 8 characters") + // Assert - blank message, so the provider string supplies the whole body. The reason is + // not appended: it is untranslated, and the provider string already states the minimum. + Truth.assertThat(message).isEqualTo("Password not strong enough. Use at least 6 characters and a mix of letters and numbers") + Truth.assertThat(message).doesNotContain("Reason:") + Truth.assertThat(message).doesNotContain("Password should be at least 8 characters") } @Test @@ -433,4 +437,45 @@ class ErrorRecoveryDialogLogicTest { // Act & Assert Truth.assertThat(isRecoverable(error)).isTrue() } + + @Test + fun `no non-recoverable error offers a retry as its action text`() { + // Derived, so a new non-recoverable type fails here until getRecoveryActionText names it. + Mockito.`when`(mockStringProvider.dismissAction).thenReturn("Dismiss") + + val nonRecoverable = allAuthExceptionSubtypes().filterNot { isRecoverable(it) } + + Truth.assertWithMessage("no non-recoverable subtype was discovered by reflection") + .that(nonRecoverable).isNotEmpty() + for (error in nonRecoverable) { + Truth.assertWithMessage(error::class.simpleName) + .that(getRecoveryActionText(error, mockStringProvider)).isEqualTo("Dismiss") + } + } + + /** One instance of every concrete [AuthException] subtype, built with placeholder arguments. */ + private fun allAuthExceptionSubtypes(): List = + AuthException::class.java.declaredClasses + .filter { AuthException::class.java.isAssignableFrom(it) } + .filterNot { Modifier.isAbstract(it.modifiers) } + .sortedBy { it.name } + .map { instantiate(it) } + + private fun instantiate(type: Class<*>): AuthException { + val constructor = type.declaredConstructors + .filterNot { it.isSynthetic } + .minByOrNull { it.parameterCount } + ?: error("${type.simpleName} has no usable constructor") + val arguments = constructor.parameterTypes.map { parameter -> + when (parameter) { + String::class.java -> "placeholder" + List::class.java -> emptyList() + Long::class.javaPrimitiveType -> 0L + Int::class.javaPrimitiveType -> 0 + Boolean::class.javaPrimitiveType -> false + else -> null + } + } + return constructor.newInstance(*arguments.toTypedArray()) as AuthException + } } From d84e5da38595cfd1978f0c67569eabc14e4b7234 Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Fri, 11 Sep 2026 20:36:09 +0100 Subject: [PATCH 4/8] fix(auth): translate the password policy and passkey strings across 84 locales --- auth/src/main/res/values-ar/strings.xml | 3 +++ auth/src/main/res/values-b+es+419/strings.xml | 3 +++ auth/src/main/res/values-bg/strings.xml | 3 +++ auth/src/main/res/values-bn/strings.xml | 3 +++ auth/src/main/res/values-ca/strings.xml | 3 +++ auth/src/main/res/values-cs/strings.xml | 3 +++ auth/src/main/res/values-da/strings.xml | 3 +++ auth/src/main/res/values-de-rAT/strings.xml | 3 +++ auth/src/main/res/values-de-rCH/strings.xml | 3 +++ auth/src/main/res/values-de/strings.xml | 3 +++ auth/src/main/res/values-el/strings.xml | 3 +++ auth/src/main/res/values-en-rAU/strings.xml | 3 +++ auth/src/main/res/values-en-rCA/strings.xml | 3 +++ auth/src/main/res/values-en-rGB/strings.xml | 3 +++ auth/src/main/res/values-en-rIE/strings.xml | 3 +++ auth/src/main/res/values-en-rIN/strings.xml | 3 +++ auth/src/main/res/values-en-rSG/strings.xml | 3 +++ auth/src/main/res/values-en-rZA/strings.xml | 3 +++ auth/src/main/res/values-es-rAR/strings.xml | 3 +++ auth/src/main/res/values-es-rBO/strings.xml | 3 +++ auth/src/main/res/values-es-rCL/strings.xml | 3 +++ auth/src/main/res/values-es-rCO/strings.xml | 3 +++ auth/src/main/res/values-es-rCR/strings.xml | 3 +++ auth/src/main/res/values-es-rDO/strings.xml | 3 +++ auth/src/main/res/values-es-rEC/strings.xml | 3 +++ auth/src/main/res/values-es-rGT/strings.xml | 3 +++ auth/src/main/res/values-es-rHN/strings.xml | 3 +++ auth/src/main/res/values-es-rMX/strings.xml | 3 +++ auth/src/main/res/values-es-rNI/strings.xml | 3 +++ auth/src/main/res/values-es-rPA/strings.xml | 3 +++ auth/src/main/res/values-es-rPE/strings.xml | 3 +++ auth/src/main/res/values-es-rPR/strings.xml | 3 +++ auth/src/main/res/values-es-rPY/strings.xml | 3 +++ auth/src/main/res/values-es-rSV/strings.xml | 3 +++ auth/src/main/res/values-es-rUS/strings.xml | 3 +++ auth/src/main/res/values-es-rUY/strings.xml | 3 +++ auth/src/main/res/values-es-rVE/strings.xml | 3 +++ auth/src/main/res/values-es/strings.xml | 3 +++ auth/src/main/res/values-fa/strings.xml | 3 +++ auth/src/main/res/values-fi/strings.xml | 3 +++ auth/src/main/res/values-fil/strings.xml | 3 +++ auth/src/main/res/values-fr-rCH/strings.xml | 3 +++ auth/src/main/res/values-fr/strings.xml | 3 +++ auth/src/main/res/values-gsw/strings.xml | 3 +++ auth/src/main/res/values-gu/strings.xml | 3 +++ auth/src/main/res/values-hi/strings.xml | 3 +++ auth/src/main/res/values-hr/strings.xml | 3 +++ auth/src/main/res/values-hu/strings.xml | 3 +++ auth/src/main/res/values-in/strings.xml | 3 +++ auth/src/main/res/values-it/strings.xml | 3 +++ auth/src/main/res/values-iw/strings.xml | 3 +++ auth/src/main/res/values-ja/strings.xml | 3 +++ auth/src/main/res/values-kn/strings.xml | 3 +++ auth/src/main/res/values-ko/strings.xml | 3 +++ auth/src/main/res/values-ln/strings.xml | 3 +++ auth/src/main/res/values-lt/strings.xml | 3 +++ auth/src/main/res/values-lv/strings.xml | 3 +++ auth/src/main/res/values-mo/strings.xml | 3 +++ auth/src/main/res/values-mr/strings.xml | 3 +++ auth/src/main/res/values-ms/strings.xml | 3 +++ auth/src/main/res/values-nb/strings.xml | 3 +++ auth/src/main/res/values-nl/strings.xml | 3 +++ auth/src/main/res/values-no/strings.xml | 3 +++ auth/src/main/res/values-pl/strings.xml | 3 +++ auth/src/main/res/values-pt-rBR/strings.xml | 3 +++ auth/src/main/res/values-pt-rPT/strings.xml | 3 +++ auth/src/main/res/values-pt/strings.xml | 3 +++ auth/src/main/res/values-ro/strings.xml | 3 +++ auth/src/main/res/values-ru/strings.xml | 3 +++ auth/src/main/res/values-sk/strings.xml | 3 +++ auth/src/main/res/values-sl/strings.xml | 3 +++ auth/src/main/res/values-sr/strings.xml | 3 +++ auth/src/main/res/values-sv/strings.xml | 3 +++ auth/src/main/res/values-ta/strings.xml | 3 +++ auth/src/main/res/values-th/strings.xml | 3 +++ auth/src/main/res/values-tl/strings.xml | 3 +++ auth/src/main/res/values-tr/strings.xml | 3 +++ auth/src/main/res/values-uk/strings.xml | 3 +++ auth/src/main/res/values-ur/strings.xml | 3 +++ auth/src/main/res/values-vi/strings.xml | 3 +++ auth/src/main/res/values-zh-rCN/strings.xml | 3 +++ auth/src/main/res/values-zh-rHK/strings.xml | 3 +++ auth/src/main/res/values-zh-rTW/strings.xml | 3 +++ auth/src/main/res/values-zh/strings.xml | 3 +++ 84 files changed, 252 insertions(+) diff --git a/auth/src/main/res/values-ar/strings.xml b/auth/src/main/res/values-ar/strings.xml index 8b5a1d3198..8176f916db 100755 --- a/auth/src/main/res/values-ar/strings.xml +++ b/auth/src/main/res/values-ar/strings.xml @@ -185,4 +185,7 @@ تحقَّق من عنوان بريدك الإلكتروني قبل المتابعة. طريقة التحقق هذه مُعدّة بالفعل في هذا الحساب. لقد وصلت إلى الحد الأقصى لطرق التحقق في هذا الحساب. + كلمة المرور لا تستوفي المتطلبات. جرِّب كلمة مرور أخرى. + كلمة المرور طويلة جدًا. الحد الأقصى للطول هو %1$d. + تعذّر العثور على مفتاح مرور لهذا الحساب. سجِّل الدخول بطريقة أخرى. diff --git a/auth/src/main/res/values-b+es+419/strings.xml b/auth/src/main/res/values-b+es+419/strings.xml index 70bcc2cc6c..b8910f3bc2 100755 --- a/auth/src/main/res/values-b+es+419/strings.xml +++ b/auth/src/main/res/values-b+es+419/strings.xml @@ -203,4 +203,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-bg/strings.xml b/auth/src/main/res/values-bg/strings.xml index edac37ab7c..2246e4bf57 100755 --- a/auth/src/main/res/values-bg/strings.xml +++ b/auth/src/main/res/values-bg/strings.xml @@ -185,4 +185,7 @@ Потвърдете имейл адреса си, преди да продължите. Този метод за потвърждаване вече е настроен в този профил. Достигнахте ограничението за методи за потвърждаване в този профил. + Паролата ви не отговаря на изискванията. Опитайте с друга. + Паролата е твърде дълга. Максималната дължина е %1$d. + Не намерихме код за достъп за този профил. Влезте по друг начин. diff --git a/auth/src/main/res/values-bn/strings.xml b/auth/src/main/res/values-bn/strings.xml index adf5a41c24..d7af9ef9b6 100755 --- a/auth/src/main/res/values-bn/strings.xml +++ b/auth/src/main/res/values-bn/strings.xml @@ -186,4 +186,7 @@ চালিয়ে যাওয়ার আগে আপনার ইমেল অ্যাড্রেস যাচাই করুন। এই যাচাইকরণ পদ্ধতিটি এই অ্যাকাউন্টে ইতিমধ্যেই সেট আপ করা আছে। এই অ্যাকাউন্টে যাচাইকরণ পদ্ধতির সীমায় আপনি পৌঁছে গেছেন। + আপনার পাসওয়ার্ড প্রয়োজনীয় শর্ত পূরণ করে না। অন্য একটি পাসওয়ার্ড ব্যবহার করুন। + পাসওয়ার্ড খুব বড়। সর্বাধিক দৈর্ঘ্য হল %1$d। + এই অ্যাকাউন্টের জন্য পাসকী খুঁজে পাওয়া যায়নি। অন্য উপায়ে সাইন-ইন করুন। diff --git a/auth/src/main/res/values-ca/strings.xml b/auth/src/main/res/values-ca/strings.xml index fcf65fa867..5cae80a872 100755 --- a/auth/src/main/res/values-ca/strings.xml +++ b/auth/src/main/res/values-ca/strings.xml @@ -186,4 +186,7 @@ Verifica la teva adreça electrònica abans de continuar. Aquest mètode de verificació ja està configurat en aquest compte. Has assolit el límit de mètodes de verificació d\'aquest compte. + La teva contrasenya no compleix els requisits. Prova\'n una altra. + La contrasenya és massa llarga. La longitud màxima és %1$d. + No hem trobat cap clau d\'accés per a aquest compte. Inicia la sessió d\'una altra manera. diff --git a/auth/src/main/res/values-cs/strings.xml b/auth/src/main/res/values-cs/strings.xml index e606edf054..8912523b98 100755 --- a/auth/src/main/res/values-cs/strings.xml +++ b/auth/src/main/res/values-cs/strings.xml @@ -185,4 +185,7 @@ Před pokračováním ověřte svou e-mailovou adresu. Tento způsob ověření je u tohoto účtu už nastavený. Dosáhli jste limitu způsobů ověření pro tento účet. + Vaše heslo nesplňuje požadavky. Zkuste jiné. + Heslo je příliš dlouhé. Maximální délka je %1$d. + Pro tento účet jsme nenašli žádný přístupový klíč. Přihlaste se jiným způsobem. diff --git a/auth/src/main/res/values-da/strings.xml b/auth/src/main/res/values-da/strings.xml index 70d0324ec9..72d1d31a92 100755 --- a/auth/src/main/res/values-da/strings.xml +++ b/auth/src/main/res/values-da/strings.xml @@ -185,4 +185,7 @@ Bekræft din mailadresse, før du fortsætter. Denne bekræftelsesmetode er allerede konfigureret på denne konto. Du har nået grænsen for bekræftelsesmetoder på denne konto. + Din adgangskode opfylder ikke kravene. Prøv en anden. + Adgangskoden er for lang. Den maksimale længde er %1$d. + Vi kunne ikke finde en adgangsnøgle til denne konto. Log ind på en anden måde. diff --git a/auth/src/main/res/values-de-rAT/strings.xml b/auth/src/main/res/values-de-rAT/strings.xml index 3889d18368..1df316df21 100755 --- a/auth/src/main/res/values-de-rAT/strings.xml +++ b/auth/src/main/res/values-de-rAT/strings.xml @@ -203,4 +203,7 @@ Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. + Ihr Passwort erfüllt die Anforderungen nicht. Versuchen Sie es mit einem anderen. + Das Passwort ist zu lang. Die maximale Länge beträgt %1$d. + Für dieses Konto wurde kein Passkey gefunden. Melden Sie sich auf andere Weise an. diff --git a/auth/src/main/res/values-de-rCH/strings.xml b/auth/src/main/res/values-de-rCH/strings.xml index e08e0e1642..3b84b28184 100755 --- a/auth/src/main/res/values-de-rCH/strings.xml +++ b/auth/src/main/res/values-de-rCH/strings.xml @@ -204,4 +204,7 @@ Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. + Ihr Passwort erfüllt die Anforderungen nicht. Versuchen Sie es mit einem anderen. + Das Passwort ist zu lang. Die maximale Länge beträgt %1$d. + Für dieses Konto wurde kein Passkey gefunden. Melden Sie sich auf andere Weise an. diff --git a/auth/src/main/res/values-de/strings.xml b/auth/src/main/res/values-de/strings.xml index 5646f17a32..74b54f72f3 100755 --- a/auth/src/main/res/values-de/strings.xml +++ b/auth/src/main/res/values-de/strings.xml @@ -203,4 +203,7 @@ Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. + Ihr Passwort erfüllt die Anforderungen nicht. Versuchen Sie es mit einem anderen. + Das Passwort ist zu lang. Die maximale Länge beträgt %1$d. + Für dieses Konto wurde kein Passkey gefunden. Melden Sie sich auf andere Weise an. diff --git a/auth/src/main/res/values-el/strings.xml b/auth/src/main/res/values-el/strings.xml index d23e3d4379..1c2a2eb70b 100755 --- a/auth/src/main/res/values-el/strings.xml +++ b/auth/src/main/res/values-el/strings.xml @@ -186,4 +186,7 @@ Επαληθεύστε τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας πριν συνεχίσετε. Αυτή η μέθοδος επαλήθευσης έχει ήδη ρυθμιστεί σε αυτόν τον λογαριασμό. Έχετε φτάσει το όριο μεθόδων επαλήθευσης για αυτόν τον λογαριασμό. + Ο κωδικός πρόσβασής σας δεν πληροί τις προϋποθέσεις. Δοκιμάστε έναν άλλον. + Ο κωδικός πρόσβασης είναι πολύ μεγάλος. Το μέγιστο μήκος είναι %1$d. + Δεν βρέθηκε κλειδί πρόσβασης για αυτόν τον λογαριασμό. Συνδεθείτε με άλλον τρόπο. diff --git a/auth/src/main/res/values-en-rAU/strings.xml b/auth/src/main/res/values-en-rAU/strings.xml index ec70afe9b4..ae438e6b1d 100755 --- a/auth/src/main/res/values-en-rAU/strings.xml +++ b/auth/src/main/res/values-en-rAU/strings.xml @@ -185,4 +185,7 @@ Verify your email address before you continue. That verification method is already set up on this account. You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rCA/strings.xml b/auth/src/main/res/values-en-rCA/strings.xml index 4c5d7342ea..cab0bf53b6 100755 --- a/auth/src/main/res/values-en-rCA/strings.xml +++ b/auth/src/main/res/values-en-rCA/strings.xml @@ -185,4 +185,7 @@ Verify your email address before you continue. That verification method is already set up on this account. You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rGB/strings.xml b/auth/src/main/res/values-en-rGB/strings.xml index 5b0fae94b3..62d7a8a3c9 100755 --- a/auth/src/main/res/values-en-rGB/strings.xml +++ b/auth/src/main/res/values-en-rGB/strings.xml @@ -185,4 +185,7 @@ Verify your email address before you continue. That verification method is already set up on this account. You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rIE/strings.xml b/auth/src/main/res/values-en-rIE/strings.xml index 4bfb4a3987..894fc6a864 100755 --- a/auth/src/main/res/values-en-rIE/strings.xml +++ b/auth/src/main/res/values-en-rIE/strings.xml @@ -178,4 +178,7 @@ Verify your email address before you continue. That verification method is already set up on this account. You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rIN/strings.xml b/auth/src/main/res/values-en-rIN/strings.xml index 4bfb4a3987..894fc6a864 100755 --- a/auth/src/main/res/values-en-rIN/strings.xml +++ b/auth/src/main/res/values-en-rIN/strings.xml @@ -178,4 +178,7 @@ Verify your email address before you continue. That verification method is already set up on this account. You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rSG/strings.xml b/auth/src/main/res/values-en-rSG/strings.xml index 4bfb4a3987..894fc6a864 100755 --- a/auth/src/main/res/values-en-rSG/strings.xml +++ b/auth/src/main/res/values-en-rSG/strings.xml @@ -178,4 +178,7 @@ Verify your email address before you continue. That verification method is already set up on this account. You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rZA/strings.xml b/auth/src/main/res/values-en-rZA/strings.xml index 4bfb4a3987..894fc6a864 100755 --- a/auth/src/main/res/values-en-rZA/strings.xml +++ b/auth/src/main/res/values-en-rZA/strings.xml @@ -178,4 +178,7 @@ Verify your email address before you continue. That verification method is already set up on this account. You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-es-rAR/strings.xml b/auth/src/main/res/values-es-rAR/strings.xml index ca2ef55622..57ea7fdebb 100755 --- a/auth/src/main/res/values-es-rAR/strings.xml +++ b/auth/src/main/res/values-es-rAR/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rBO/strings.xml b/auth/src/main/res/values-es-rBO/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rBO/strings.xml +++ b/auth/src/main/res/values-es-rBO/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rCL/strings.xml b/auth/src/main/res/values-es-rCL/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rCL/strings.xml +++ b/auth/src/main/res/values-es-rCL/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rCO/strings.xml b/auth/src/main/res/values-es-rCO/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rCO/strings.xml +++ b/auth/src/main/res/values-es-rCO/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rCR/strings.xml b/auth/src/main/res/values-es-rCR/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rCR/strings.xml +++ b/auth/src/main/res/values-es-rCR/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rDO/strings.xml b/auth/src/main/res/values-es-rDO/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rDO/strings.xml +++ b/auth/src/main/res/values-es-rDO/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rEC/strings.xml b/auth/src/main/res/values-es-rEC/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rEC/strings.xml +++ b/auth/src/main/res/values-es-rEC/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rGT/strings.xml b/auth/src/main/res/values-es-rGT/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rGT/strings.xml +++ b/auth/src/main/res/values-es-rGT/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rHN/strings.xml b/auth/src/main/res/values-es-rHN/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rHN/strings.xml +++ b/auth/src/main/res/values-es-rHN/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rMX/strings.xml b/auth/src/main/res/values-es-rMX/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rMX/strings.xml +++ b/auth/src/main/res/values-es-rMX/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rNI/strings.xml b/auth/src/main/res/values-es-rNI/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rNI/strings.xml +++ b/auth/src/main/res/values-es-rNI/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rPA/strings.xml b/auth/src/main/res/values-es-rPA/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rPA/strings.xml +++ b/auth/src/main/res/values-es-rPA/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rPE/strings.xml b/auth/src/main/res/values-es-rPE/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rPE/strings.xml +++ b/auth/src/main/res/values-es-rPE/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rPR/strings.xml b/auth/src/main/res/values-es-rPR/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rPR/strings.xml +++ b/auth/src/main/res/values-es-rPR/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rPY/strings.xml b/auth/src/main/res/values-es-rPY/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rPY/strings.xml +++ b/auth/src/main/res/values-es-rPY/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rSV/strings.xml b/auth/src/main/res/values-es-rSV/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rSV/strings.xml +++ b/auth/src/main/res/values-es-rSV/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rUS/strings.xml b/auth/src/main/res/values-es-rUS/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rUS/strings.xml +++ b/auth/src/main/res/values-es-rUS/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rUY/strings.xml b/auth/src/main/res/values-es-rUY/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rUY/strings.xml +++ b/auth/src/main/res/values-es-rUY/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rVE/strings.xml b/auth/src/main/res/values-es-rVE/strings.xml index 6dd455cd7e..ae7e28c3cb 100755 --- a/auth/src/main/res/values-es-rVE/strings.xml +++ b/auth/src/main/res/values-es-rVE/strings.xml @@ -196,4 +196,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es/strings.xml b/auth/src/main/res/values-es/strings.xml index 79327ae560..7d61227a4b 100755 --- a/auth/src/main/res/values-es/strings.xml +++ b/auth/src/main/res/values-es/strings.xml @@ -203,4 +203,7 @@ Verifica tu dirección de correo electrónico antes de continuar. Este método de verificación ya está configurado en esta cuenta. Has alcanzado el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No hemos encontrado ninguna clave de acceso para esta cuenta. Inicia sesión de otra forma. diff --git a/auth/src/main/res/values-fa/strings.xml b/auth/src/main/res/values-fa/strings.xml index 3805f2ea88..fd24dc7924 100755 --- a/auth/src/main/res/values-fa/strings.xml +++ b/auth/src/main/res/values-fa/strings.xml @@ -186,4 +186,7 @@ پیش از ادامه، نشانی ایمیلتان را تأیید کنید. این روش تأیید قبلاً در این حساب تنظیم شده است. به حداکثر تعداد روش‌های تأیید در این حساب رسیده‌اید. + گذرواژه شما الزامات را برآورده نمی‌کند. گذرواژه دیگری را امتحان کنید. + گذرواژه خیلی طولانی است. حداکثر طول %1$d است. + کلید عبوری برای این حساب پیدا نشد. به روش دیگری وارد سیستم شوید. diff --git a/auth/src/main/res/values-fi/strings.xml b/auth/src/main/res/values-fi/strings.xml index 38346777c6..68076deea2 100755 --- a/auth/src/main/res/values-fi/strings.xml +++ b/auth/src/main/res/values-fi/strings.xml @@ -185,4 +185,7 @@ Vahvista sähköpostiosoitteesi ennen kuin jatkat. Tämä vahvistustapa on jo määritetty tällä tilillä. Olet saavuttanut tämän tilin vahvistustapojen enimmäismäärän. + Salasanasi ei täytä vaatimuksia. Kokeile toista. + Salasana on liian pitkä. Enimmäispituus on %1$d. + Tälle tilille ei löytynyt avainkoodia. Kirjaudu sisään toisella tavalla. diff --git a/auth/src/main/res/values-fil/strings.xml b/auth/src/main/res/values-fil/strings.xml index 2cc95855f0..b33ac04cad 100755 --- a/auth/src/main/res/values-fil/strings.xml +++ b/auth/src/main/res/values-fil/strings.xml @@ -185,4 +185,7 @@ I-verify ang iyong email address bago ka magpatuloy. Naka-set up na ang paraan ng pag-verify na iyon sa account na ito. Naabot mo na ang limitasyon para sa mga paraan ng pag-verify sa account na ito. + Hindi natutugunan ng iyong password ang mga kinakailangan. Sumubok ng iba. + Masyadong mahaba ang password. Ang maximum na haba ay %1$d. + Wala kaming nakitang passkey para sa account na ito. Mag-sign in sa ibang paraan. diff --git a/auth/src/main/res/values-fr-rCH/strings.xml b/auth/src/main/res/values-fr-rCH/strings.xml index 716165de41..e21e8e4fe2 100755 --- a/auth/src/main/res/values-fr-rCH/strings.xml +++ b/auth/src/main/res/values-fr-rCH/strings.xml @@ -197,4 +197,7 @@ Veuillez vérifier votre adresse e-mail avant de continuer. Cette méthode de vérification est déjà configurée sur ce compte. Vous avez atteint la limite de méthodes de vérification pour ce compte. + Votre mot de passe ne respecte pas les exigences. Essayez-en un autre. + Le mot de passe est trop long. La longueur maximale est de %1$d. + Aucune clé d\'accès n\'a été trouvée pour ce compte. Connectez-vous d\'une autre manière. diff --git a/auth/src/main/res/values-fr/strings.xml b/auth/src/main/res/values-fr/strings.xml index 587e025d26..5211fc9210 100755 --- a/auth/src/main/res/values-fr/strings.xml +++ b/auth/src/main/res/values-fr/strings.xml @@ -203,4 +203,7 @@ Vérifiez votre adresse e-mail avant de continuer. Cette méthode de vérification est déjà configurée sur ce compte. Vous avez atteint la limite de méthodes de vérification pour ce compte. + Votre mot de passe ne respecte pas les exigences. Essayez-en un autre. + Le mot de passe est trop long. La longueur maximale est de %1$d. + Aucune clé d\'accès n\'a été trouvée pour ce compte. Connectez-vous d\'une autre manière. diff --git a/auth/src/main/res/values-gsw/strings.xml b/auth/src/main/res/values-gsw/strings.xml index 55f8cd02f3..12ae38c1da 100755 --- a/auth/src/main/res/values-gsw/strings.xml +++ b/auth/src/main/res/values-gsw/strings.xml @@ -185,4 +185,7 @@ Bestätige Sie Ihri E-Mail-Adrässe, bevor Sie wiitermache. Die Bestätigungsmethode isch scho uf däm Konto iigrichtet. Sie händ d\'Grenze für Bestätigungsmethode uf däm Konto erreicht. + Ihres Passwort erfüllt d Aaforderige nöd. Probiere Sie es mit eme andere. + S Passwort isch z lang. D maximali Läng isch %1$d. + Für das Konto isch kein Passkey gfunde worde. Mälde Sie sich uf en anderi Art aa. diff --git a/auth/src/main/res/values-gu/strings.xml b/auth/src/main/res/values-gu/strings.xml index b87a35a0f4..893c8d0738 100755 --- a/auth/src/main/res/values-gu/strings.xml +++ b/auth/src/main/res/values-gu/strings.xml @@ -186,4 +186,7 @@ તમે આગળ વધો તે પહેલાં તમારું ઇમેઇલ ઍડ્રેસ ચકાસો. તે ચકાસણી પદ્ધતિ આ એકાઉન્ટ પર પહેલેથી જ સેટ કરેલી છે. તમે આ એકાઉન્ટ પર ચકાસણી પદ્ધતિઓની મર્યાદા પર પહોંચી ગયા છો. + તમારો પાસવર્ડ જરૂરિયાતો પૂરી કરતો નથી. બીજો પાસવર્ડ અજમાવો. + પાસવર્ડ ઘણો લાંબો છે. મહત્તમ લંબાઈ %1$d છે. + આ એકાઉન્ટ માટે કોઈ પાસકી મળી નથી. બીજી રીતે સાઇન ઇન કરો. diff --git a/auth/src/main/res/values-hi/strings.xml b/auth/src/main/res/values-hi/strings.xml index cc77a9cdcc..686c17f63a 100755 --- a/auth/src/main/res/values-hi/strings.xml +++ b/auth/src/main/res/values-hi/strings.xml @@ -186,4 +186,7 @@ आगे बढ़ने से पहले अपने ईमेल पते की पुष्टि करें. पुष्टि करने का वह तरीका इस खाते पर पहले से सेट अप है. आपने इस खाते पर पुष्टि करने के तरीकों की सीमा पूरी कर ली है. + आपका पासवर्ड ज़रूरी शर्तें पूरी नहीं करता. कोई दूसरा पासवर्ड आज़माएं. + पासवर्ड बहुत लंबा है. ज़्यादा से ज़्यादा लंबाई %1$d है. + इस खाते के लिए कोई पासकी नहीं मिली. किसी दूसरे तरीके से साइन इन करें. diff --git a/auth/src/main/res/values-hr/strings.xml b/auth/src/main/res/values-hr/strings.xml index 97a393a11a..1a93051c96 100755 --- a/auth/src/main/res/values-hr/strings.xml +++ b/auth/src/main/res/values-hr/strings.xml @@ -185,4 +185,7 @@ Potvrdite svoju e-adresu prije nego što nastavite. Taj je način potvrde već postavljen na ovom računu. Dosegnuli ste ograničenje broja načina potvrde na ovom računu. + Vaša zaporka ne ispunjava uvjete. Pokušajte s drugom. + Zaporka je predugačka. Najveća duljina je %1$d. + Nismo pronašli pristupni ključ za ovaj račun. Prijavite se na drugi način. diff --git a/auth/src/main/res/values-hu/strings.xml b/auth/src/main/res/values-hu/strings.xml index e381c595b4..8356d81458 100755 --- a/auth/src/main/res/values-hu/strings.xml +++ b/auth/src/main/res/values-hu/strings.xml @@ -185,4 +185,7 @@ A folytatás előtt erősítse meg az e-mail-címét. Ez az ellenőrzési módszer már be van állítva ebben a fiókban. Elérte az ebben a fiókban beállítható ellenőrzési módszerek felső határát. + A jelszava nem felel meg a követelményeknek. Próbáljon meg egy másikat. + A jelszó túl hosszú. A maximális hossz %1$d. + Nem találtunk azonosítókulcsot ehhez a fiókhoz. Jelentkezzen be másik módon. diff --git a/auth/src/main/res/values-in/strings.xml b/auth/src/main/res/values-in/strings.xml index 4d2d1d7ad4..8d4b49d890 100755 --- a/auth/src/main/res/values-in/strings.xml +++ b/auth/src/main/res/values-in/strings.xml @@ -186,4 +186,7 @@ Verifikasi alamat email Anda sebelum melanjutkan. Metode verifikasi tersebut sudah disiapkan di akun ini. Anda telah mencapai batas metode verifikasi di akun ini. + Sandi Anda tidak memenuhi persyaratan. Coba sandi lain. + Sandi terlalu panjang. Panjang maksimumnya adalah %1$d. + Kami tidak menemukan kunci sandi untuk akun ini. Login dengan cara lain. diff --git a/auth/src/main/res/values-it/strings.xml b/auth/src/main/res/values-it/strings.xml index f309a26d02..96ec746e41 100755 --- a/auth/src/main/res/values-it/strings.xml +++ b/auth/src/main/res/values-it/strings.xml @@ -185,4 +185,7 @@ Verifica il tuo indirizzo email prima di continuare. Questo metodo di verifica è già configurato su questo account. Hai raggiunto il limite di metodi di verifica per questo account. + La tua password non soddisfa i requisiti. Provane un\'altra. + La password è troppo lunga. La lunghezza massima è %1$d. + Non abbiamo trovato una passkey per questo account. Accedi in un altro modo. diff --git a/auth/src/main/res/values-iw/strings.xml b/auth/src/main/res/values-iw/strings.xml index af1244be8b..229eb36ac7 100755 --- a/auth/src/main/res/values-iw/strings.xml +++ b/auth/src/main/res/values-iw/strings.xml @@ -186,4 +186,7 @@ יש לאמת את כתובת האימייל שלך לפני שממשיכים. שיטת האימות הזו כבר מוגדרת בחשבון הזה. הגעת למגבלה של שיטות אימות בחשבון הזה. + הסיסמה שלך לא עומדת בדרישות. אפשר לנסות סיסמה אחרת. + הסיסמה ארוכה מדי. האורך המקסימלי הוא %1$d. + לא נמצא מפתח גישה לחשבון הזה. אפשר להיכנס בדרך אחרת. diff --git a/auth/src/main/res/values-ja/strings.xml b/auth/src/main/res/values-ja/strings.xml index 91bfe79e30..edf85cf63f 100755 --- a/auth/src/main/res/values-ja/strings.xml +++ b/auth/src/main/res/values-ja/strings.xml @@ -185,4 +185,7 @@ 続行する前にメールアドレスを確認してください。 この確認方法はこのアカウントですでに設定されています。 このアカウントで設定できる確認方法の上限に達しました。 + パスワードが要件を満たしていません。別のパスワードをお試しください。 + パスワードが長すぎます。最大文字数は %1$d です。 + このアカウントのパスキーが見つかりませんでした。別の方法でログインしてください。 diff --git a/auth/src/main/res/values-kn/strings.xml b/auth/src/main/res/values-kn/strings.xml index 0de365d313..219eab5690 100755 --- a/auth/src/main/res/values-kn/strings.xml +++ b/auth/src/main/res/values-kn/strings.xml @@ -186,4 +186,7 @@ ಮುಂದುವರಿಯುವ ಮೊದಲು ನಿಮ್ಮ ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ಪರಿಶೀಲಿಸಿ. ಆ ಪರಿಶೀಲನಾ ವಿಧಾನವನ್ನು ಈ ಖಾತೆಯಲ್ಲಿ ಈಗಾಗಲೇ ಹೊಂದಿಸಲಾಗಿದೆ. ಈ ಖಾತೆಯಲ್ಲಿನ ಪರಿಶೀಲನಾ ವಿಧಾನಗಳ ಮಿತಿಯನ್ನು ನೀವು ತಲುಪಿದ್ದೀರಿ. + ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅಗತ್ಯತೆಗಳನ್ನು ಪೂರೈಸುತ್ತಿಲ್ಲ. ಬೇರೊಂದನ್ನು ಪ್ರಯತ್ನಿಸಿ. + ಪಾಸ್‌ವರ್ಡ್ ತುಂಬಾ ಉದ್ದವಾಗಿದೆ. ಗರಿಷ್ಠ ಉದ್ದ %1$d ಆಗಿದೆ. + ಈ ಖಾತೆಗೆ ಪಾಸ್‌ಕೀ ಕಂಡುಬಂದಿಲ್ಲ. ಬೇರೆ ವಿಧಾನದಲ್ಲಿ ಸೈನ್ ಇನ್ ಮಾಡಿ. diff --git a/auth/src/main/res/values-ko/strings.xml b/auth/src/main/res/values-ko/strings.xml index c338e061d2..68af356fc7 100755 --- a/auth/src/main/res/values-ko/strings.xml +++ b/auth/src/main/res/values-ko/strings.xml @@ -184,4 +184,7 @@ 계속하기 전에 이메일 주소를 인증하세요. 이 인증 수단은 이 계정에 이미 설정되어 있습니다. 이 계정에서 설정할 수 있는 인증 수단 한도에 도달했습니다. + 비밀번호가 요건을 충족하지 않습니다. 다른 비밀번호를 사용해 보세요. + 비밀번호가 너무 깁니다. 최대 길이는 %1$d입니다. + 이 계정의 패스키를 찾을 수 없습니다. 다른 방법으로 로그인하세요. diff --git a/auth/src/main/res/values-ln/strings.xml b/auth/src/main/res/values-ln/strings.xml index eb731561fc..56158ed0f9 100755 --- a/auth/src/main/res/values-ln/strings.xml +++ b/auth/src/main/res/values-ln/strings.xml @@ -186,4 +186,7 @@ Ndimisa adrɛsɛ na yo ya email liboso ya kokoba. Lolenge wana ya bondimi ebongisami déjà na konte oyo. Okómi na ndelo ya balolenge ya bondimi na konte oyo. + Mot de passe na yo ekokisi te makambo esengeli. Meka mosusu. + Mot de passe ezali molayi mingi. Molayi ya likolo ezali %1$d. + Tomonaki te clé ya kokɔta mpo na compte oyo. Kɔta na ndenge mosusu. diff --git a/auth/src/main/res/values-lt/strings.xml b/auth/src/main/res/values-lt/strings.xml index 4f6d152c5c..fea42f95fa 100755 --- a/auth/src/main/res/values-lt/strings.xml +++ b/auth/src/main/res/values-lt/strings.xml @@ -186,4 +186,7 @@ Prieš tęsdami patvirtinkite savo el. pašto adresą. Šis patvirtinimo būdas šioje paskyroje jau nustatytas. Pasiekėte šios paskyros patvirtinimo būdų ribą. + Jūsų slaptažodis neatitinka reikalavimų. Išbandykite kitą. + Slaptažodis per ilgas. Didžiausias ilgis yra %1$d. + Nepavyko rasti šios paskyros prieigos rakto. Prisijunkite kitu būdu. diff --git a/auth/src/main/res/values-lv/strings.xml b/auth/src/main/res/values-lv/strings.xml index 6cd5dd8bce..a64f518365 100755 --- a/auth/src/main/res/values-lv/strings.xml +++ b/auth/src/main/res/values-lv/strings.xml @@ -186,4 +186,7 @@ Pirms turpināt, verificējiet savu e-pasta adresi. Šī verifikācijas metode šajā kontā jau ir iestatīta. Jūs esat sasniedzis šī konta verifikācijas metožu ierobežojumu. + Jūsu parole neatbilst prasībām. Mēģiniet citu. + Parole ir pārāk gara. Maksimālais garums ir %1$d. + Neatradām šī konta piekļuves atslēgu. Piesakieties citā veidā. diff --git a/auth/src/main/res/values-mo/strings.xml b/auth/src/main/res/values-mo/strings.xml index f73acd5d96..ca1ffcb4fb 100755 --- a/auth/src/main/res/values-mo/strings.xml +++ b/auth/src/main/res/values-mo/strings.xml @@ -186,4 +186,7 @@ Confirmați adresa de e-mail înainte de a continua. Această metodă de verificare este deja configurată în acest cont. Ați atins limita de metode de verificare pentru acest cont. + Parola nu îndeplinește cerințele. Încercați alta. + Parola este prea lungă. Lungimea maximă este %1$d. + Nu am găsit o cheie de acces pentru acest cont. Conectați-vă în alt mod. diff --git a/auth/src/main/res/values-mr/strings.xml b/auth/src/main/res/values-mr/strings.xml index 4886233038..771bcceaf9 100755 --- a/auth/src/main/res/values-mr/strings.xml +++ b/auth/src/main/res/values-mr/strings.xml @@ -186,4 +186,7 @@ तुम्ही पुढे सुरू ठेवण्यापूर्वी तुमच्या ईमेल ॲड्रेसची पडताळणी करा. ती पडताळणी पद्धत या खात्यावर आधीपासून सेट केलेली आहे. तुम्ही या खात्यावरील पडताळणी पद्धतींची मर्यादा गाठली आहे. + तुमचा पासवर्ड आवश्यकता पूर्ण करत नाही. दुसरा पासवर्ड वापरून पहा. + पासवर्ड खूप लांब आहे. कमाल लांबी %1$d आहे. + या खात्यासाठी पासकी सापडली नाही. दुसऱ्या पद्धतीने साइन इन करा. diff --git a/auth/src/main/res/values-ms/strings.xml b/auth/src/main/res/values-ms/strings.xml index 1c2eed13ac..fe4783061a 100755 --- a/auth/src/main/res/values-ms/strings.xml +++ b/auth/src/main/res/values-ms/strings.xml @@ -186,4 +186,7 @@ Sahkan alamat e-mel anda sebelum anda meneruskan. Kaedah pengesahan tersebut telah pun disediakan pada akaun ini. Anda telah mencapai had kaedah pengesahan pada akaun ini. + Kata laluan anda tidak memenuhi keperluan. Cuba kata laluan lain. + Kata laluan terlalu panjang. Panjang maksimum ialah %1$d. + Kami tidak menemui kunci laluan untuk akaun ini. Log masuk dengan cara lain. diff --git a/auth/src/main/res/values-nb/strings.xml b/auth/src/main/res/values-nb/strings.xml index f884d23468..46abccbc3a 100755 --- a/auth/src/main/res/values-nb/strings.xml +++ b/auth/src/main/res/values-nb/strings.xml @@ -185,4 +185,7 @@ Bekreft e-postadressen din før du fortsetter. Denne bekreftelsesmetoden er allerede satt opp på denne kontoen. Du har nådd grensen for bekreftelsesmetoder på denne kontoen. + Passordet ditt oppfyller ikke kravene. Prøv et annet. + Passordet er for langt. Maksimal lengde er %1$d. + Vi fant ingen passnøkkel for denne kontoen. Logg på en annen måte. diff --git a/auth/src/main/res/values-nl/strings.xml b/auth/src/main/res/values-nl/strings.xml index 279ebe0910..238601ec1c 100755 --- a/auth/src/main/res/values-nl/strings.xml +++ b/auth/src/main/res/values-nl/strings.xml @@ -185,4 +185,7 @@ Bevestig uw e-mailadres voordat u doorgaat. Deze verificatiemethode is al ingesteld op dit account. U heeft de limiet voor verificatiemethoden op dit account bereikt. + Uw wachtwoord voldoet niet aan de vereisten. Probeer een ander wachtwoord. + Het wachtwoord is te lang. De maximale lengte is %1$d. + We hebben geen toegangssleutel voor dit account gevonden. Log op een andere manier in. diff --git a/auth/src/main/res/values-no/strings.xml b/auth/src/main/res/values-no/strings.xml index 3beb1b8265..df19bd5b65 100755 --- a/auth/src/main/res/values-no/strings.xml +++ b/auth/src/main/res/values-no/strings.xml @@ -186,4 +186,7 @@ Bekreft e-postadressen din før du fortsetter. Denne bekreftelsesmetoden er allerede satt opp på denne kontoen. Du har nådd grensen for bekreftelsesmetoder på denne kontoen. + Passordet ditt oppfyller ikke kravene. Prøv et annet. + Passordet er for langt. Maksimal lengde er %1$d. + Vi fant ingen passnøkkel for denne kontoen. Logg på en annen måte. diff --git a/auth/src/main/res/values-pl/strings.xml b/auth/src/main/res/values-pl/strings.xml index 1f636bb994..76715ce816 100755 --- a/auth/src/main/res/values-pl/strings.xml +++ b/auth/src/main/res/values-pl/strings.xml @@ -185,4 +185,7 @@ Zweryfikuj swój adres e-mail, zanim przejdziesz dalej. Ta metoda weryfikacji jest już skonfigurowana na tym koncie. Osiągnięto limit metod weryfikacji na tym koncie. + Twoje hasło nie spełnia wymagań. Spróbuj innego. + Hasło jest za długie. Maksymalna długość to %1$d. + Nie znaleziono klucza dostępu do tego konta. Zaloguj się w inny sposób. diff --git a/auth/src/main/res/values-pt-rBR/strings.xml b/auth/src/main/res/values-pt-rBR/strings.xml index fedd068ffd..e2f997db53 100755 --- a/auth/src/main/res/values-pt-rBR/strings.xml +++ b/auth/src/main/res/values-pt-rBR/strings.xml @@ -204,4 +204,7 @@ Verifique seu endereço de e-mail antes de continuar. Esse método de verificação já está configurado nesta conta. Você atingiu o limite de métodos de verificação nesta conta. + Sua senha não atende aos requisitos. Tente outra. + A senha é muito longa. O tamanho máximo é %1$d. + Não encontramos uma chave de acesso para esta conta. Faça login de outra forma. diff --git a/auth/src/main/res/values-pt-rPT/strings.xml b/auth/src/main/res/values-pt-rPT/strings.xml index c0dd0e0456..cdf0180a42 100755 --- a/auth/src/main/res/values-pt-rPT/strings.xml +++ b/auth/src/main/res/values-pt-rPT/strings.xml @@ -204,4 +204,7 @@ Valide o seu endereço de email antes de continuar. Este método de validação já está configurado nesta conta. Atingiu o limite de métodos de validação nesta conta. + A sua palavra-passe não cumpre os requisitos. Experimente outra. + A palavra-passe é demasiado longa. O comprimento máximo é %1$d. + Não encontrámos uma chave de acesso para esta conta. Inicie sessão de outra forma. diff --git a/auth/src/main/res/values-pt/strings.xml b/auth/src/main/res/values-pt/strings.xml index 8df734edee..4b6198343d 100755 --- a/auth/src/main/res/values-pt/strings.xml +++ b/auth/src/main/res/values-pt/strings.xml @@ -203,4 +203,7 @@ Verifique seu endereço de e-mail antes de continuar. Esse método de verificação já está configurado nesta conta. Você atingiu o limite de métodos de verificação nesta conta. + Sua senha não atende aos requisitos. Tente outra. + A senha é muito longa. O tamanho máximo é %1$d. + Não encontramos uma chave de acesso para esta conta. Faça login de outra forma. diff --git a/auth/src/main/res/values-ro/strings.xml b/auth/src/main/res/values-ro/strings.xml index 4b8fda9c57..e94512f75d 100755 --- a/auth/src/main/res/values-ro/strings.xml +++ b/auth/src/main/res/values-ro/strings.xml @@ -185,4 +185,7 @@ Confirmați adresa de e-mail înainte de a continua. Această metodă de verificare este deja configurată în acest cont. Ați atins limita de metode de verificare pentru acest cont. + Parola nu îndeplinește cerințele. Încercați alta. + Parola este prea lungă. Lungimea maximă este %1$d. + Nu am găsit o cheie de acces pentru acest cont. Conectați-vă în alt mod. diff --git a/auth/src/main/res/values-ru/strings.xml b/auth/src/main/res/values-ru/strings.xml index bdf4890439..de2d5622f7 100755 --- a/auth/src/main/res/values-ru/strings.xml +++ b/auth/src/main/res/values-ru/strings.xml @@ -185,4 +185,7 @@ Подтвердите адрес электронной почты, прежде чем продолжить. Этот способ подтверждения уже настроен в этом аккаунте. Достигнут лимит способов подтверждения для этого аккаунта. + Пароль не соответствует требованиям. Попробуйте другой. + Пароль слишком длинный. Максимальная длина – %1$d. + Не удалось найти ключ доступа для этого аккаунта. Войдите другим способом. diff --git a/auth/src/main/res/values-sk/strings.xml b/auth/src/main/res/values-sk/strings.xml index c6bc7daa8e..84fe70ddd1 100755 --- a/auth/src/main/res/values-sk/strings.xml +++ b/auth/src/main/res/values-sk/strings.xml @@ -185,4 +185,7 @@ Skôr než budete pokračovať, overte svoju e-mailovú adresu. Táto metóda overenia je v tomto účte už nastavená. Dosiahli ste limit metód overenia pre tento účet. + Vaše heslo nespĺňa požiadavky. Skúste iné. + Heslo je príliš dlhé. Maximálna dĺžka je %1$d. + Pre tento účet sa nenašiel prístupový kľúč. Prihláste sa iným spôsobom. diff --git a/auth/src/main/res/values-sl/strings.xml b/auth/src/main/res/values-sl/strings.xml index 10c4d3e94d..45d7549650 100755 --- a/auth/src/main/res/values-sl/strings.xml +++ b/auth/src/main/res/values-sl/strings.xml @@ -186,4 +186,7 @@ Preden nadaljujete, preverite svoj e-poštni naslov. Ta način preverjanja je v tem računu že nastavljen. Dosegli ste omejitev števila načinov preverjanja v tem računu. + Vaše geslo ne izpolnjuje zahtev. Poskusite z drugim. + Geslo je predolgo. Največja dolžina je %1$d. + Za ta račun nismo našli ključa za dostop. Prijavite se na drug način. diff --git a/auth/src/main/res/values-sr/strings.xml b/auth/src/main/res/values-sr/strings.xml index 8d333c4b2d..bbc34a4c3f 100755 --- a/auth/src/main/res/values-sr/strings.xml +++ b/auth/src/main/res/values-sr/strings.xml @@ -186,4 +186,7 @@ Верификујте имејл адресу пре него што наставите. Тај метод верификације је већ подешен на овом налогу. Достигли сте ограничење броја метода верификације на овом налогу. + Лозинка не испуњава услове. Пробајте другу. + Лозинка је предугачка. Максимална дужина је %1$d. + Нисмо пронашли приступни кључ за овај налог. Пријавите се на други начин. diff --git a/auth/src/main/res/values-sv/strings.xml b/auth/src/main/res/values-sv/strings.xml index 7ac07b9cbf..18e0ce93c4 100755 --- a/auth/src/main/res/values-sv/strings.xml +++ b/auth/src/main/res/values-sv/strings.xml @@ -185,4 +185,7 @@ Verifiera din e-postadress innan du fortsätter. Den verifieringsmetoden är redan konfigurerad på det här kontot. Du har nått gränsen för antalet verifieringsmetoder på det här kontot. + Ditt lösenord uppfyller inte kraven. Prova ett annat. + Lösenordet är för långt. Den maximala längden är %1$d. + Vi hittade ingen nyckel för det här kontot. Logga in på ett annat sätt. diff --git a/auth/src/main/res/values-ta/strings.xml b/auth/src/main/res/values-ta/strings.xml index 1b003ab4cd..acc42e7d69 100755 --- a/auth/src/main/res/values-ta/strings.xml +++ b/auth/src/main/res/values-ta/strings.xml @@ -186,4 +186,7 @@ தொடர்வதற்கு முன் உங்கள் மின்னஞ்சல் முகவரியைச் சரிபார்க்கவும். அந்தச் சரிபார்ப்பு முறை இந்தக் கணக்கில் ஏற்கெனவே அமைக்கப்பட்டுள்ளது. இந்தக் கணக்கிற்கான சரிபார்ப்பு முறைகளின் வரம்பை எட்டிவிட்டீர்கள். + உங்கள் கடவுச்சொல் தேவைகளைப் பூர்த்தி செய்யவில்லை. வேறொன்றை முயலவும். + கடவுச்சொல் மிக நீளமாக உள்ளது. அதிகபட்ச நீளம் %1$d ஆகும். + இந்தக் கணக்கிற்கான கடவுச்சாவி கிடைக்கவில்லை. வேறு முறையில் உள்நுழையவும். diff --git a/auth/src/main/res/values-th/strings.xml b/auth/src/main/res/values-th/strings.xml index f27619270f..86e6f603bc 100755 --- a/auth/src/main/res/values-th/strings.xml +++ b/auth/src/main/res/values-th/strings.xml @@ -186,4 +186,7 @@ โปรดยืนยันที่อยู่อีเมลก่อนดำเนินการต่อ วิธีการยืนยันนี้ตั้งค่าไว้ในบัญชีนี้แล้ว คุณใช้วิธีการยืนยันถึงขีดจำกัดของบัญชีนี้แล้ว + รหัสผ่านของคุณไม่เป็นไปตามข้อกำหนด โปรดลองใช้รหัสผ่านอื่น + รหัสผ่านยาวเกินไป ความยาวสูงสุดคือ %1$d + ไม่พบพาสคีย์สำหรับบัญชีนี้ โปรดลงชื่อเข้าใช้ด้วยวิธีอื่น diff --git a/auth/src/main/res/values-tl/strings.xml b/auth/src/main/res/values-tl/strings.xml index 2a2fab5359..4e5a45a88b 100755 --- a/auth/src/main/res/values-tl/strings.xml +++ b/auth/src/main/res/values-tl/strings.xml @@ -185,4 +185,7 @@ I-verify ang iyong email address bago ka magpatuloy. Naka-set up na ang paraan ng pag-verify na iyon sa account na ito. Naabot mo na ang limitasyon ng mga paraan ng pag-verify sa account na ito. + Hindi natutugunan ng iyong password ang mga kinakailangan. Sumubok ng iba. + Masyadong mahaba ang password. Ang maximum na haba ay %1$d. + Wala kaming nakitang passkey para sa account na ito. Mag-sign in sa ibang paraan. diff --git a/auth/src/main/res/values-tr/strings.xml b/auth/src/main/res/values-tr/strings.xml index 33243eec8d..61db57e950 100755 --- a/auth/src/main/res/values-tr/strings.xml +++ b/auth/src/main/res/values-tr/strings.xml @@ -186,4 +186,7 @@ Devam etmeden önce e-posta adresinizi doğrulayın. Bu doğrulama yöntemi bu hesapta zaten ayarlanmış. Bu hesap için doğrulama yöntemi sınırına ulaştınız. + Şifreniz gereksinimleri karşılamıyor. Başka bir şifre deneyin. + Şifre çok uzun. İzin verilen en fazla uzunluk %1$d. + Bu hesap için parola anahtarı bulunamadı. Başka bir yöntemle oturum açın. diff --git a/auth/src/main/res/values-uk/strings.xml b/auth/src/main/res/values-uk/strings.xml index dfd568dd9b..51b3266682 100755 --- a/auth/src/main/res/values-uk/strings.xml +++ b/auth/src/main/res/values-uk/strings.xml @@ -186,4 +186,7 @@ Підтвердьте свою електронну адресу, перш ніж продовжити. Цей спосіб підтвердження вже налаштовано в цьому обліковому записі. Ви досягли ліміту способів підтвердження для цього облікового запису. + Пароль не відповідає вимогам. Спробуйте інший. + Пароль задовгий. Максимальна довжина – %1$d. + Не вдалося знайти ключ доступу для цього облікового запису. Увійдіть іншим способом. diff --git a/auth/src/main/res/values-ur/strings.xml b/auth/src/main/res/values-ur/strings.xml index 1e0c977c8f..5dd3fccb0f 100755 --- a/auth/src/main/res/values-ur/strings.xml +++ b/auth/src/main/res/values-ur/strings.xml @@ -186,4 +186,7 @@ جاری رکھنے سے پہلے اپنے ای میل پتے کی توثیق کریں۔ یہ توثیقی طریقہ اس اکاؤنٹ پر پہلے سے سیٹ اپ ہے۔ آپ اس اکاؤنٹ پر توثیقی طریقوں کی حد تک پہنچ گئے ہیں۔ + آپ کا پاس ورڈ تقاضے پورے نہیں کرتا۔ کوئی دوسرا آزمائیں۔ + پاس ورڈ بہت طویل ہے۔ زیادہ سے زیادہ طوالت %1$d ہے۔ + اس اکاؤنٹ کے لیے پاس کی نہیں ملی۔ کسی اور طریقے سے سائن ان کریں۔ diff --git a/auth/src/main/res/values-vi/strings.xml b/auth/src/main/res/values-vi/strings.xml index 9f458fa80b..c7213688af 100755 --- a/auth/src/main/res/values-vi/strings.xml +++ b/auth/src/main/res/values-vi/strings.xml @@ -186,4 +186,7 @@ Hãy xác minh địa chỉ email của bạn trước khi tiếp tục. Phương thức xác minh này đã được thiết lập trên tài khoản này. Bạn đã đạt đến giới hạn số phương thức xác minh trên tài khoản này. + Mật khẩu của bạn không đáp ứng các yêu cầu. Hãy thử mật khẩu khác. + Mật khẩu quá dài. Độ dài tối đa là %1$d. + Không tìm thấy mã xác thực cho tài khoản này. Hãy đăng nhập bằng cách khác. diff --git a/auth/src/main/res/values-zh-rCN/strings.xml b/auth/src/main/res/values-zh-rCN/strings.xml index 79be0e30b8..ff28f44625 100755 --- a/auth/src/main/res/values-zh-rCN/strings.xml +++ b/auth/src/main/res/values-zh-rCN/strings.xml @@ -186,4 +186,7 @@ 请先验证您的电子邮件地址,然后再继续。 该验证方式已在此帐号中设置。 您已达到此帐号的验证方式数量上限。 + 您的密码不符合要求,请尝试其他密码。 + 密码过长。最大长度为 %1$d。 + 未找到此账号的通行密钥,请通过其他方式登录。 diff --git a/auth/src/main/res/values-zh-rHK/strings.xml b/auth/src/main/res/values-zh-rHK/strings.xml index 5fd6213b3a..c2c6550563 100755 --- a/auth/src/main/res/values-zh-rHK/strings.xml +++ b/auth/src/main/res/values-zh-rHK/strings.xml @@ -186,4 +186,7 @@ 請先驗證您的電郵地址,然後再繼續。 此驗證方法已在此帳戶中設定。 您已達到此帳戶的驗證方法數量上限。 + 您的密碼不符合要求,請嘗試其他密碼。 + 密碼太長。長度上限為 %1$d。 + 找不到此帳戶的密碼金鑰,請使用其他方式登入。 diff --git a/auth/src/main/res/values-zh-rTW/strings.xml b/auth/src/main/res/values-zh-rTW/strings.xml index 706063fc6c..8b9af3100a 100755 --- a/auth/src/main/res/values-zh-rTW/strings.xml +++ b/auth/src/main/res/values-zh-rTW/strings.xml @@ -186,4 +186,7 @@ 請先驗證您的電子郵件地址,然後再繼續。 此驗證方法已在這個帳號中設定。 您已達到這個帳號的驗證方法數量上限。 + 您的密碼不符合要求,請嘗試其他密碼。 + 密碼太長。長度上限為 %1$d。 + 找不到此帳戶的密碼金鑰,請使用其他方式登入。 diff --git a/auth/src/main/res/values-zh/strings.xml b/auth/src/main/res/values-zh/strings.xml index 6d4aa5e0f8..0bedfcc099 100755 --- a/auth/src/main/res/values-zh/strings.xml +++ b/auth/src/main/res/values-zh/strings.xml @@ -185,4 +185,7 @@ 请先验证您的电子邮件地址,然后再继续。 该验证方式已在此帐号中设置。 您已达到此帐号的验证方式数量上限。 + 您的密码不符合要求,请尝试其他密码。 + 密码过长。最大长度为 %1$d。 + 未找到此账号的通行密钥,请通过其他方式登录。 From 1f81e976afde47e7869258cf2571b720a4f1943c Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Fri, 11 Sep 2026 21:13:00 +0100 Subject: [PATCH 5/8] test(auth): drop the bytecode sweep and tests that assert unreachable inputs --- .../AuthExceptionRecoveryResolutionTest.kt | 24 -- .../com/firebase/ui/auth/AuthExceptionTest.kt | 11 - .../ui/auth/FirebaseAuthErrorCodeSweepTest.kt | 371 ------------------ .../PasswordPolicyMessageLocalizationTest.kt | 10 +- 4 files changed, 4 insertions(+), 412 deletions(-) delete mode 100644 auth/src/test/java/com/firebase/ui/auth/FirebaseAuthErrorCodeSweepTest.kt diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt index 90fd0627f3..d3c2835671 100644 --- a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt @@ -349,31 +349,7 @@ class AuthExceptionRecoveryResolutionTest { // The copy is actually translated, not just library-owned // ============================================================================================= - @Test - fun `a French device offline sees French, not the English network diagnostic`() { - val french = DefaultAuthUIStringProvider(context, Locale.FRENCH) - val firebaseException = object : FirebaseException(networkDiagnostic) {} - - val resolved = resolve(firebaseException, french) - assertThat(resolved).isEqualTo(french.networkErrorRecoveryMessage) - assertThat(resolved).isNotEqualTo(networkDiagnostic) - // Guards against the French resource silently falling back to values/. - assertThat(resolved).isNotEqualTo(strings.networkErrorRecoveryMessage) - } - - @Test - fun `a French device with no such account sees French, not the English diagnostic`() { - val french = DefaultAuthUIStringProvider(context, Locale.FRENCH) - val firebaseException = - FirebaseAuthInvalidUserException("ERROR_USER_NOT_FOUND", userNotFoundDiagnostic) - - val resolved = resolve(firebaseException, french) - - assertThat(resolved).isEqualTo(french.userNotFoundRecoveryMessage) - assertThat(resolved).isNotEqualTo(userNotFoundDiagnostic) - assertThat(resolved).isNotEqualTo(strings.userNotFoundRecoveryMessage) - } @Test fun `a French device changing its email sees French, not the English reauth diagnostic`() { diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt index 6109f7c5dd..42bf0de611 100644 --- a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt @@ -84,17 +84,6 @@ class AuthExceptionTest { assertThat(result.message).isEqualTo("Zu viele Versuche") } - @Test - fun `from() no longer treats ERROR_TOO_MANY_REQUESTS as a real Firebase Auth code`() { - // firebase-auth 24.2.0 has no such code: every status that means "rate limited" (17010, - // 17052) is turned into a FirebaseTooManyRequestsException instead. Anything still - // carrying this string is not from the SDK, so it is just an unknown code. - val firebaseException = object : FirebaseAuthException("ERROR_TOO_MANY_REQUESTS", "x") {} - - val result = AuthException.from(firebaseException) - - assertThat(result).isInstanceOf(AuthException.UnknownException::class.java) - } @Test fun `from() maps FirebaseAuthException with unknown error code to UnknownException`() { diff --git a/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthErrorCodeSweepTest.kt b/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthErrorCodeSweepTest.kt deleted file mode 100644 index 5a4bc0db03..0000000000 --- a/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthErrorCodeSweepTest.kt +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright 2025 Google Inc. All Rights Reserved. - * - * 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. - */ - -package com.firebase.ui.auth - -import android.content.Context -import androidx.test.core.app.ApplicationProvider -import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider -import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider -import com.firebase.ui.auth.ui.components.getRecoveryMessage -import com.google.common.truth.Truth.assertThat -import com.google.common.truth.Truth.assertWithMessage -import com.google.firebase.auth.FirebaseAuthActionCodeException -import com.google.firebase.auth.FirebaseAuthEmailException -import com.google.firebase.auth.FirebaseAuthException -import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException -import com.google.firebase.auth.FirebaseAuthInvalidUserException -import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException -import com.google.firebase.auth.FirebaseAuthUserCollisionException -import com.google.firebase.auth.FirebaseAuthWeakPasswordException -import com.google.firebase.auth.FirebaseAuthWebException -import java.io.DataInputStream -import java.io.File -import java.io.InputStream -import java.net.URLDecoder -import java.util.Locale -import java.util.jar.JarFile -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config - -/** - * Exhaustive sweep of the invariant `AuthException.from` is built on: given a non-null - * [AuthUIStringProvider], the resulting `message` — and the dialog body rendered from it — is - * always library-owned copy, never the Firebase SDK's untranslated English diagnostic. - * - * Unlike the hand-written allowlists in the other suites here, this one does not let the author - * choose the inputs: it reads every `ERROR_*` literal out of the firebase-auth artifact on the - * classpath and drives all of them through every constructible `FirebaseAuthException` subtype. - * A firebase-auth upgrade that adds error codes therefore widens the suite automatically. - * - * @suppress Internal test class - */ -@RunWith(RobolectricTestRunner::class) -@Config(manifest = Config.NONE) -class FirebaseAuthErrorCodeSweepTest { - - private val context: Context = ApplicationProvider.getApplicationContext() - private val strings: AuthUIStringProvider = DefaultAuthUIStringProvider(context) - - /** - * A diagnostic that cannot collide with any library string, so `doesNotContain` is a sound - * test for "the SDK text leaked into what the user sees". - */ - private val diagnostic = - "ZZ_RAW_SDK_DIAGNOSTIC_ZZ the Firebase SDK's own untranslated English." - - /** Every shape a Firebase error code can arrive in that `from()` dispatches on by type. */ - private fun shapesFor(code: String): List> = listOf( - "FirebaseAuthException" to object : FirebaseAuthException(code, diagnostic) {}, - "FirebaseAuthInvalidCredentialsException" to - FirebaseAuthInvalidCredentialsException(code, diagnostic), - "FirebaseAuthWeakPasswordException" to - FirebaseAuthWeakPasswordException(code, diagnostic, diagnostic), - "FirebaseAuthInvalidUserException" to FirebaseAuthInvalidUserException(code, diagnostic), - "FirebaseAuthUserCollisionException" to - FirebaseAuthUserCollisionException(code, diagnostic), - "FirebaseAuthRecentLoginRequiredException" to - FirebaseAuthRecentLoginRequiredException(code, diagnostic), - "FirebaseAuthActionCodeException" to FirebaseAuthActionCodeException(code, diagnostic), - "FirebaseAuthEmailException" to FirebaseAuthEmailException(code, diagnostic), - "FirebaseAuthWebException" to FirebaseAuthWebException(code, diagnostic), - ) - - // ============================================================================================= - // The extraction itself — if this is wrong, the sweep below proves nothing - // ============================================================================================= - - @Test - fun `the error codes are read from the firebase-auth artifact on the classpath`() { - val codes = FIREBASE_AUTH_ERROR_CODES - - // An empty read makes every other assertion in this file vacuous; this bound catches it. - assertWithMessage( - "Expected ~81 ERROR_* literals across the artifacts holding " + - "$ARTIFACT_PROBE_CLASSES, found ${codes.size}. Either the artifact layout " + - "changed, or a probe class was renamed by an SDK upgrade. Fix the " + - "extraction — do not lower this bound, and do not delete this test." - ).that(codes.size).isAtLeast(79) - - // Anchors across unrelated families, so a partial read cannot pass. - assertThat(codes).containsAtLeast( - "ERROR_INVALID_CREDENTIAL", - "ERROR_USER_NOT_FOUND", - "ERROR_EMAIL_ALREADY_IN_USE", - "ERROR_REQUIRES_RECENT_LOGIN", - "ERROR_OPERATION_NOT_ALLOWED", - "ERROR_SESSION_EXPIRED", - ) - - // The invalid-credential-family codes; ERROR_REJECTED_CREDENTIAL reaches the `else` arm. - assertThat(codes).containsAtLeast( - "ERROR_MISSING_OR_INVALID_NONCE", - "ERROR_REJECTED_CREDENTIAL", - "ERROR_INVALID_AUTHENTICATOR_RESPONSE", - "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", - ) - } - - @Test - fun `the extraction reads the whole artifact, not just the internal error-code table`() { - // Not redundant with the size bound above: a read narrowed back to the internal table - // still yields 80 of 81 codes and passes that bound, but fails here. - assertThat(FIREBASE_AUTH_ERROR_CODES).contains("ERROR_MISSING_ACTIVITY") - assertThat(ERROR_CODES_IN_INTERNAL_TABLE).doesNotContain("ERROR_MISSING_ACTIVITY") - - assertWithMessage( - "Every code the internal table declares must still be in the artifact-wide read; " + - "if it is not, the widened extraction is dropping classes." - ).that(FIREBASE_AUTH_ERROR_CODES).containsAtLeastElementsIn(ERROR_CODES_IN_INTERNAL_TABLE) - - assertWithMessage( - "The artifact-wide read must be strictly wider than the internal table — that is the " + - "entire point of widening it." - ).that(FIREBASE_AUTH_ERROR_CODES.size) - .isGreaterThan(ERROR_CODES_IN_INTERNAL_TABLE.size) - } - - @Test - fun `from() names no error code that the firebase-auth artifact does not ship`() { - // The sweep only drives codes that exist, so an arm on a code the SDK never produces is - // invisible to it; this runs the check the other way round. - assertWithMessage( - "No ERROR_* literals found in $FROM_HOLDER_CLASS. `from()` was refactored so its " + - "`when` arms no longer compile into that class, or the class was renamed. " + - "Fix the extraction — do not delete this test." - ).that(NAMED_BY_FROM.size).isAtLeast(60) - - val shipped = FIREBASE_AUTH_ERROR_CODES.toSet() - val named = NAMED_BY_FROM.filterNot { it in shipped } - - assertWithMessage( - "AuthException.from() dispatches on these error codes, none of which exist in the " + - "firebase-auth artifact on the classpath. Each is an unreachable branch: " + - "either the SDK retired the code, or it was never spelled correctly." - ).that(named).isEmpty() - } - - // ============================================================================================= - // The sweep - // ============================================================================================= - - @Test - fun `no shipped error code leaks the SDK diagnostic into the message`() { - for (code in FIREBASE_AUTH_ERROR_CODES) { - for ((shape, firebaseException) in shapesFor(code)) { - val resolved = AuthException.from(firebaseException, strings).message - assertWithMessage("%s as %s -> message", code, shape) - .that(resolved.orEmpty()).doesNotContain(diagnostic) - } - } - } - - @Test - fun `no shipped error code leaks the SDK diagnostic into the error dialog`() { - for (code in FIREBASE_AUTH_ERROR_CODES) { - for ((shape, firebaseException) in shapesFor(code)) { - val authException = AuthException.from(firebaseException, strings) - val rendered = getRecoveryMessage(authException, strings) - - assertWithMessage("%s as %s -> dialog body", code, shape) - .that(rendered).doesNotContain(diagnostic) - // The dialog must still say something; an empty body is its own defect. - assertWithMessage("%s as %s -> dialog body is blank", code, shape) - .that(rendered.isBlank()).isFalse() - // The developer still needs the diagnostic, so it has to survive on the cause. - assertWithMessage("%s as %s -> cause", code, shape) - .that(authException.cause?.message).isEqualTo(diagnostic) - } - } - } - - @Test - fun `no shipped error code leaks the SDK diagnostic on a French device`() { - val french = DefaultAuthUIStringProvider(context, Locale.FRENCH) - - for (code in FIREBASE_AUTH_ERROR_CODES) { - for ((shape, firebaseException) in shapesFor(code)) { - val rendered = getRecoveryMessage(AuthException.from(firebaseException, french), french) - assertWithMessage("%s as %s -> French dialog body", code, shape) - .that(rendered).doesNotContain(diagnostic) - } - } - } - - companion object { - /** - * Landmarks used to locate the artifacts to read. Whichever jar or class directory holds - * each of these is read in full; duplicate containers are read once. - * - * A landmark must be a class only firebase-auth itself ships, because - * `com.google.firebase.auth` is a split package. Do not add - * `com.google.firebase.auth.FirebaseAuthException`: it resolves to firebase-auth-interop, - * whose jar holds zero `ERROR_*` literals, so it reads as "the artifact ships no codes". - */ - val ARTIFACT_PROBE_CLASSES = listOf( - "com.google.firebase.auth.FirebaseAuthMissingActivityForRecaptchaException", - ) - - /** - * Most of firebase-auth's error-code literals live in this one internal class, as - * constant-pool entries rather than declared fields, so they are read out of the class - * file rather than by reflection. - * - * It is named here only so the assertions can say how the whole-artifact total splits. - * **The sweep must not scope itself to this class**: `ERROR_MISSING_ACTIVITY` is declared - * on [com.google.firebase.auth.FirebaseAuthMissingActivityForRecaptchaException]'s - * constructor, not in this table, so a narrower scan reports it unshipped. - */ - const val ERROR_CODE_HOLDER_CLASS = "com.google.android.gms.internal.firebase-auth-api.zzaew" - - /** - * Every `ERROR_*` literal in **every class of** the firebase-auth artifact resolved onto - * this test's classpath, extracted at runtime. Nothing here is hardcoded, so an SDK upgrade - * that adds codes — in the internal table or on a public exception class — widens the sweep - * without anyone remembering to update a list. - */ - val FIREBASE_AUTH_ERROR_CODES: List by lazy { - ARTIFACT_PROBE_CLASSES - .mapNotNull { containerOf(it) } - .distinct() - .flatMap { readUtf8ConstantPools(it) } - .filter { it.matches(Regex("ERROR_[A-Z0-9_]+")) } - .distinct() - .sorted() - } - - /** The subset of [FIREBASE_AUTH_ERROR_CODES] that the old single-class scope could see. */ - val ERROR_CODES_IN_INTERNAL_TABLE: List by lazy { - readUtf8ConstantPool(ERROR_CODE_HOLDER_CLASS) - .filter { it.matches(Regex("ERROR_[A-Z0-9_]+")) } - .distinct() - .sorted() - } - - /** - * Where `AuthException.from` compiles to. Its `when` arms dispatch on string literals, - * which land in this class's constant pool exactly like the SDK's own do. - */ - const val FROM_HOLDER_CLASS = "com.firebase.ui.auth.AuthException\$Companion" - - /** - * Every `ERROR_*` literal `from()` dispatches on, read back out of its own bytecode for - * the same reason the SDK's list is: a hand-maintained copy here would drift from the - * `when` it is supposed to describe, which is the drift this whole suite exists to catch. - */ - val NAMED_BY_FROM: List by lazy { - readUtf8ConstantPool(FROM_HOLDER_CLASS) - .filter { it.matches(Regex("ERROR_[A-Z0-9_]+")) } - .distinct() - .sorted() - } - - /** - * The jar or class directory that [probeClassName] was loaded from, or `null` when the - * class does not resolve or arrives in a shape this does not understand. Gradle resolves - * an AAR's `classes.jar` either as a jar on the classpath or as a directory of extracted - * `.class` files depending on the transform, so both are handled; anything else yields - * `null`, which ends as an empty code list and trips the size guard in the first test - * rather than passing the suite vacuously. - */ - private fun containerOf(probeClassName: String): File? { - val url = FirebaseAuthErrorCodeSweepTest::class.java.classLoader - ?.getResource(probeClassName.replace('.', '/') + ".class") - ?: return null - - return when (url.protocol) { - // "jar:file:/path/to/classes.jar!/com/google/firebase/auth/…" - "jar" -> File( - URLDecoder.decode(url.path.substringBefore("!/").removePrefix("file:"), "UTF-8") - ) - - // Walk the package path back off the file to reach the classpath root: one - // directory per package segment, which is one per '.' in the class name. - "file" -> generateSequence( - File(URLDecoder.decode(url.path, "UTF-8")).parentFile - ) { it.parentFile } - .drop(probeClassName.count { it == '.' }) - .firstOrNull() - - else -> null - } - } - - /** Every CONSTANT_Utf8 entry in every class of [container]. */ - private fun readUtf8ConstantPools(container: File): List = - if (container.isDirectory) { - container.walkTopDown() - .filter { it.isFile && it.extension == "class" } - .flatMap { file -> - file.inputStream().use { readUtf8ConstantPool(it, file.path) }.asSequence() - } - .toList() - } else { - JarFile(container).use { jar -> - jar.entries().asSequence() - .filter { it.name.endsWith(".class") } - .flatMap { entry -> - jar.getInputStream(entry) - .use { readUtf8ConstantPool(it, entry.name) } - .asSequence() - } - .toList() - } - } - - /** - * Returns every CONSTANT_Utf8 entry in [className]'s constant pool. Walking the pool is - * the only way to see string literals that exist solely inside a method body. - */ - private fun readUtf8ConstantPool(className: String): List { - val resource = className.replace('.', '/') + ".class" - val stream = FirebaseAuthErrorCodeSweepTest::class.java.classLoader - ?.getResourceAsStream(resource) - ?: return emptyList() - - return stream.use { readUtf8ConstantPool(it, resource) } - } - - /** Reads one class file's CONSTANT_Utf8 entries. [resource] only names it in failures. */ - private fun readUtf8ConstantPool(source: InputStream, resource: String): List { - return DataInputStream(source.buffered()).let { input -> - require(input.readInt() == -0x35014542) { "$resource is not a class file" } - input.readUnsignedShort() // minor version - input.readUnsignedShort() // major version - - val entryCount = input.readUnsignedShort() - val strings = mutableListOf() - var index = 1 - while (index < entryCount) { - when (val tag = input.readUnsignedByte()) { - 1 -> strings += input.readUTF() - 7, 8, 16, 19, 20 -> input.skipBytes(2) - 15 -> input.skipBytes(3) - 3, 4, 9, 10, 11, 12, 17, 18 -> input.skipBytes(4) - // Long and Double occupy two constant-pool slots each. - 5, 6 -> { - input.skipBytes(8) - index++ - } - else -> error("Unknown constant pool tag $tag at $index in $resource") - } - index++ - } - strings - } - } - } -} diff --git a/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt b/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt index 5a12ceb882..7247fc6faf 100644 --- a/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt @@ -30,12 +30,10 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config /** - * Covers the Google Identity Platform password-policy path, which [FirebaseAuthErrorCodeSweepTest] - * structurally cannot reach. + * Covers the Google Identity Platform password-policy path. * - * That sweep's synthetic diagnostic never carries the `PASSWORD_DOES_NOT_MEET_REQUIREMENTS` - * marker, so neither policy branch is ever entered. The fixtures below are the real backend - * strings, captured from a project with `Require` enforcement and a minimum length of 10. + * The fixtures are the real backend strings, captured from a project with `Require` enforcement + * and a minimum length of 10. * * @suppress Internal test class */ @@ -88,7 +86,7 @@ class PasswordPolicyMessageLocalizationTest { ) // ============================================================================================= - // The leak the sweep could not see + // Untranslated English reaching the dialog // ============================================================================================= @Test From 1032d3363ab34735502cbb3332b2e68366530499 Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Fri, 11 Sep 2026 21:23:03 +0100 Subject: [PATCH 6/8] refactor(auth): derive the dismiss action from isRecoverable instead of duplicating it --- .../auth/ui/components/ErrorRecoveryDialog.kt | 7 +--- .../ErrorRecoveryDialogLogicTest.kt | 42 ------------------- 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt index e0a58f3cdb..2b4e90a768 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt @@ -229,6 +229,7 @@ internal fun getRecoveryActionText( error: AuthException, stringProvider: AuthUIStringProvider ): String { + if (!isRecoverable(error)) return stringProvider.dismissAction return when (error) { is AuthException.AuthCancelledException -> stringProvider.continueText is AuthException.EmailAlreadyInUseException -> stringProvider.signInDefault // Use existing "Sign in" text @@ -240,12 +241,6 @@ internal fun getRecoveryActionText( is AuthException.EmailLinkCrossDeviceLinkingException -> stringProvider.continueText is AuthException.EmailLinkWrongDeviceException -> stringProvider.continueText is AuthException.UserNotFoundException -> stringProvider.signupPageTitle // Navigate to sign-up when user not found - // Every type [isRecoverable] reports `false` for: retry must never be their answer. - is AuthException.EmailLinkDifferentAnonymousUserException, - is AuthException.MisconfigurationException, - is AuthException.SignInMethodUnavailableException, - is AuthException.TooManyRequestsException, - is AuthException.PhoneVerificationCooldownException -> stringProvider.dismissAction is AuthException.NetworkException, is AuthException.InvalidCredentialsException, is AuthException.WeakPasswordException, diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt index 5cce9ff04a..f4c373d07e 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt @@ -6,7 +6,6 @@ import com.google.common.truth.Truth import com.google.firebase.auth.EmailAuthProvider import com.google.firebase.auth.FirebaseAuthException import com.google.firebase.auth.GoogleAuthProvider -import java.lang.reflect.Modifier import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito @@ -437,45 +436,4 @@ class ErrorRecoveryDialogLogicTest { // Act & Assert Truth.assertThat(isRecoverable(error)).isTrue() } - - @Test - fun `no non-recoverable error offers a retry as its action text`() { - // Derived, so a new non-recoverable type fails here until getRecoveryActionText names it. - Mockito.`when`(mockStringProvider.dismissAction).thenReturn("Dismiss") - - val nonRecoverable = allAuthExceptionSubtypes().filterNot { isRecoverable(it) } - - Truth.assertWithMessage("no non-recoverable subtype was discovered by reflection") - .that(nonRecoverable).isNotEmpty() - for (error in nonRecoverable) { - Truth.assertWithMessage(error::class.simpleName) - .that(getRecoveryActionText(error, mockStringProvider)).isEqualTo("Dismiss") - } - } - - /** One instance of every concrete [AuthException] subtype, built with placeholder arguments. */ - private fun allAuthExceptionSubtypes(): List = - AuthException::class.java.declaredClasses - .filter { AuthException::class.java.isAssignableFrom(it) } - .filterNot { Modifier.isAbstract(it.modifiers) } - .sortedBy { it.name } - .map { instantiate(it) } - - private fun instantiate(type: Class<*>): AuthException { - val constructor = type.declaredConstructors - .filterNot { it.isSynthetic } - .minByOrNull { it.parameterCount } - ?: error("${type.simpleName} has no usable constructor") - val arguments = constructor.parameterTypes.map { parameter -> - when (parameter) { - String::class.java -> "placeholder" - List::class.java -> emptyList() - Long::class.javaPrimitiveType -> 0L - Int::class.javaPrimitiveType -> 0 - Boolean::class.javaPrimitiveType -> false - else -> null - } - } - return constructor.newInstance(*arguments.toTypedArray()) as AuthException - } } From 246d7c841d29e43694def6fb3f449faba575831c Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Fri, 11 Sep 2026 21:31:38 +0100 Subject: [PATCH 7/8] fix(auth): use the credentials hook for expired user tokens, not the account-generic one --- .../java/com/firebase/ui/auth/AuthException.kt | 2 +- .../com/firebase/ui/auth/AuthExceptionTest.kt | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/AuthException.kt b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt index f8153f33ba..eefefb00b6 100644 --- a/auth/src/main/java/com/firebase/ui/auth/AuthException.kt +++ b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt @@ -686,7 +686,7 @@ abstract class AuthException( "ERROR_INVALID_USER_TOKEN", "ERROR_USER_TOKEN_EXPIRED" -> InvalidCredentialsException( - message = stringProvider?.errorUserAccountGeneric.nonEmpty() + message = stringProvider?.errorInvalidCredentials.nonEmpty() ?: stringProvider?.errorMultiFactorSessionExpired.nonEmpty() ?: firebaseException.message ?: "Your sign-in session expired. Sign in again to continue.", diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt index 42bf0de611..47d4f07a13 100644 --- a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt @@ -578,4 +578,19 @@ class AuthExceptionTest { assertWithMessage(name).that(method.isDefault).isTrue() } } + @Test + fun `an expired user token honours the credentials hook, not the account-generic one`() { + // It produces an InvalidCredentialsException, so errorUserAccountGeneric — the hook the + // UserNotFoundException arm below it uses — must not win. + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorInvalidCredentials).thenReturn("Custom: credentials") + whenever(stringProvider.errorUserAccountGeneric).thenReturn("Custom: account generic") + val firebaseException = FirebaseAuthInvalidUserException("ERROR_USER_TOKEN_EXPIRED", "x") + + val result = AuthException.from(firebaseException, stringProvider) + + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(result.message).isEqualTo("Custom: credentials") + } + } \ No newline at end of file From 2e0a918cb8a9e195739c0ee2d212d9efc35b3708 Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Fri, 11 Sep 2026 22:17:02 +0100 Subject: [PATCH 8/8] docs(auth): document how auth error messages resolve --- auth/README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/auth/README.md b/auth/README.md index be8fb1fa7e..613569d90e 100644 --- a/auth/README.md +++ b/auth/README.md @@ -2223,6 +2223,33 @@ Or override individual strings in your `strings.xml`: ``` +### Error message resolution + +Error text is chosen when the exception is built, not when the dialog renders it, and it resolves in this order: + +1. **The type-level hook**, one `fui_error_*` resource per exception type. These ship **deliberately blank**, and a blank value means "skip me" rather than "show nothing". +2. **A per-code string**, selected from the Firebase error code, so a mistyped SMS code and a wrong password no longer produce the same sentence. +3. **The Firebase SDK's own message**, English only, reached only for codes the library does not map. + +Setting a type-level hook therefore overrides *every* code of that type at once. That is occasionally what you want — uniform copy resists account enumeration, since distinguishing "no such account" from "wrong password" tells an attacker which addresses are registered — but it costs you the specific per-code messages: + +```xml + + + Those sign-in details aren\'t correct. + +``` + +Developer misconfiguration is handled separately. `AuthException.MisconfigurationException` carries generic translated copy on `message`, and keeps Firebase's diagnostic on `cause` so it reaches your logs without reaching your users: + +```kotlin +is AuthException.MisconfigurationException -> { + Log.e(TAG, "Check the Firebase console", exception.cause) +} +``` + +One limit worth knowing: `FirebaseAuthUI.signOut`, `withReauth` and `delete` take a `Context` and no configuration, so their messages resolve against that `Context` rather than a `stringProvider` or `locale` you configured. Pass a locale-aware `Context` if that matters. + ## Error Handling FirebaseUI provides a comprehensive exception hierarchy: