diff --git a/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Views/SignInWithAppleButton.swift b/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Views/SignInWithAppleButton.swift index d3c5c2151a..95a51f6e27 100644 --- a/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Views/SignInWithAppleButton.swift +++ b/FirebaseSwiftUI/FirebaseAppleSwiftUI/Sources/Views/SignInWithAppleButton.swift @@ -29,7 +29,7 @@ public struct SignInWithAppleButton { extension SignInWithAppleButton: View { public var body: some View { AuthProviderButton( - label: "Sign in with Apple", + label: authService.string.appleLoginButtonLabel, style: .apple, accessibilityId: "sign-in-with-apple-button" ) { diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthConfiguration.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthConfiguration.swift index d760cc743f..822c3bff4d 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthConfiguration.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthConfiguration.swift @@ -14,8 +14,11 @@ import FirebaseAuth import Foundation +import SwiftUI public struct AuthConfiguration { + public let logo: ImageResource? + public let languageCode: String? public let shouldHideCancelButton: Bool public let interactiveDismissEnabled: Bool public let shouldAutoUpgradeAnonymousUsers: Bool @@ -31,21 +34,27 @@ public struct AuthConfiguration { public let allowedSecondFactors: Set public let mfaIssuer: String - public init(shouldHideCancelButton: Bool = false, - interactiveDismissEnabled: Bool = true, - shouldAutoUpgradeAnonymousUsers: Bool = false, - customStringsBundle: Bundle? = nil, - tosUrl: URL? = nil, - privacyPolicyUrl: URL? = nil, - emailLinkSignInActionCodeSettings: ActionCodeSettings? = nil, - verifyEmailActionCodeSettings: ActionCodeSettings? = nil, - mfaEnabled: Bool = false, - allowedSecondFactors: Set = [.sms, .totp], - mfaIssuer: String = "Firebase Auth") { + public init( + logo: ImageResource? = nil, + languageCode: String? = nil, + shouldHideCancelButton: Bool = false, + interactiveDismissEnabled: Bool = true, + shouldAutoUpgradeAnonymousUsers: Bool = false, + customStringsBundle: Bundle? = nil, + tosUrl: URL? = nil, + privacyPolicyUrl: URL? = nil, + emailLinkSignInActionCodeSettings: ActionCodeSettings? = nil, + verifyEmailActionCodeSettings: ActionCodeSettings? = nil, + mfaEnabled: Bool = false, + allowedSecondFactors: Set = [.sms, .totp], + mfaIssuer: String = "Firebase Auth" + ) { + self.logo = logo self.shouldHideCancelButton = shouldHideCancelButton self.interactiveDismissEnabled = interactiveDismissEnabled self.shouldAutoUpgradeAnonymousUsers = shouldAutoUpgradeAnonymousUsers self.customStringsBundle = customStringsBundle + self.languageCode = languageCode self.tosUrl = tosUrl self.privacyPolicyUrl = privacyPolicyUrl self.emailLinkSignInActionCodeSettings = emailLinkSignInActionCodeSettings diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 096eebeb04..8b19eea7d0 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -27,10 +27,9 @@ public protocol AuthProviderUI { var provider: AuthProviderSwift { get } } -public protocol PhoneAuthProviderSwift: AuthProviderSwift, AnyObject { - // Phone auth provider that presents its own UI flow in createAuthCredential() - // Internal use only: AuthService will be injected automatically by AuthService.signIn() - var authService: AuthService? { get set } +public protocol PhoneAuthProviderSwift: AuthProviderSwift { + @MainActor func verifyPhoneNumber(phoneNumber: String) async throws -> String + @MainActor func createAuthCredential(verificationId: String, verificationCode: String) async throws -> AuthCredential } public enum AuthenticationState { @@ -51,6 +50,8 @@ public enum AuthView: Hashable { case mfaEnrollment case mfaManagement case mfaResolution + case enterPhoneNumber + case enterVerificationCode(verificationID: String, fullPhoneNumber: String) } public enum SignInOutcome: @unchecked Sendable { @@ -108,7 +109,7 @@ public final class AuthService { public init(configuration: AuthConfiguration = AuthConfiguration(), auth: Auth = Auth.auth()) { self.auth = auth self.configuration = configuration - string = StringUtils(bundle: configuration.customStringsBundle ?? Bundle.module) + string = StringUtils(bundle: configuration.customStringsBundle ?? Bundle.module, languageCode: configuration.languageCode) listenerManager = AuthListenerManager(auth: auth, authEnvironment: self) FirebaseApp.registerLibrary("firebase-ui-ios", withVersion: FirebaseAuthSwiftUIVersion.version) } @@ -159,6 +160,10 @@ public final class AuthService { private var providers: [AuthProviderUI] = [] + public var currentPhoneProvider: PhoneAuthProviderSwift? { + providers.compactMap { $0.provider as? PhoneAuthProviderSwift }.first + } + public func registerProvider(providerWithButton: AuthProviderUI) { providers.append(providerWithButton) } @@ -182,11 +187,6 @@ public final class AuthService { public func signIn(_ provider: AuthProviderSwift) async throws -> SignInOutcome { do { - // Automatically inject AuthService for phone provider - if let phoneProvider = provider as? PhoneAuthProviderSwift { - phoneProvider.authService = self - } - let credential = try await provider.createAuthCredential() let result = try await signIn(credentials: credential) return result diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings index 7d686924a6..9f57074e39 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings @@ -1,1311 +1,4961 @@ { - "sourceLanguage" : "en", - "strings" : { - "%@" : { - + "sourceLanguage": "en", + "strings": { + "%@": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "%@" + } + } + } + }, + "••••••%@": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "••••••%@" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "••••••%@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "••••••%@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "••••••%@" + } + } + } + }, + "Account: %@": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Konto: %@" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Account: %@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cuenta: %@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Compte : %@" + } + } + } + }, + "AccountDisabledError": { + "comment": "Error message displayed when the account is disabled. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "migrated", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Diese E-Mail-Adresse gehört zu einem deaktivierten Konto." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "That email address is for an account that has been disabled." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Esa dirección de correo es de una cuenta que ha sido deshabilitada." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Cette adresse e-mail correspond à un compte qui a été désactivé." + } + } + } + }, + "ActionCantBeUndone": { + "comment": "Alert message shown before account deletion.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Diese Aktion kann nicht rückgängig gemacht werden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "This action can't be undone" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Esta acción no se puede deshacer" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Cette action ne peut pas être annulée" + } + } + } + }, + "Add an extra layer of security to your account": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Füge deinem Konto eine zusätzliche Sicherheitsebene hinzu" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Add an extra layer of security to your account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Agrega una capa adicional de seguridad a tu cuenta" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Ajoute une couche de sécurité supplémentaire à ton compte" + } + } + } + }, + "Add Another Method": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Weitere Methode hinzufügen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Add Another Method" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Agregar otro método" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Ajouter une autre méthode" + } + } + } + }, + "AddPasswordAlertMessage": { + "comment": "Alert message shown when adding account password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Um ein Passwort zu deinem Konto hinzuzufügen, musst du dich erneut anmelden." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "To add password to your account, you will need to sign in again." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Para agregar una contraseña a tu cuenta, deberás iniciar sesión nuevamente." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Pour ajouter un mot de passe à ton compte, tu devras te reconnecter." + } + } + } + }, + "AddPasswordTitle": { + "comment": "Controller title shown when adding password to account.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort hinzufügen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Add password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Agregar contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Ajouter un mot de passe" + } + } + } + }, + "Already have an account?": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Hast du bereits ein Konto?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Already have an account?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Ya tienes una cuenta?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Tu as déjà un compte ?" + } + } + } + }, + "AS_AddPassword": { + "comment": "Account Settings cell title Add Password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort hinzufügen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Add password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Agregar contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Ajouter un mot de passe" + } + } + } + }, + "AS_ChangePassword": { + "comment": "Account Settings cell title Change Password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort ändern" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Change password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cambiar contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Changer le mot de passe" + } + } + } + }, + "AS_DeleteAccount": { + "comment": "Account Settings cell title Delete Account.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Konto löschen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete Account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Eliminar cuenta" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Supprimer le compte" + } + } + } + }, + "AS_Email": { + "comment": "Account Settings cell title Email. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "E-Mail" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "E-mail" + } + } + } + }, + "AS_Name": { + "comment": "Account Settings cell title Name.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Name" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Name" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Nombre" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nom" + } + } + } + }, + "AS_SectionLinkedAccounts": { + "comment": "Account Settings section title Linked Accounts.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verknüpfte Konten" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Linked Accounts" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cuentas vinculadas" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Comptes liés" + } + } + } + }, + "AS_SectionProfile": { + "comment": "Account Settings section title Profile.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Profil" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Profile" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Perfil" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Profil" + } + } + } + }, + "AS_SectionSecurity": { + "comment": "Account Settings section title Security.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sicherheit" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Security" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Seguridad" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Sécurité" + } + } + } + }, + "AS_SignOut": { + "comment": "Account Settings cell title Sign Out.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Abmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign Out" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cerrar sesión" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se déconnecter" + } + } + } + }, + "Authenticating...": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Authentifizierung läuft..." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Authenticating..." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Autenticando..." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Authentification..." + } + } + } + }, + "Authentication Method": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Authentifizierungsmethode" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Authentication Method" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Método de autenticación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Méthode d'authentification" + } + } + } + }, + "Authenticator App": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Authentifizierungs-App" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Authenticator App" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Aplicación de autenticación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Application d'authentification" + } + } + } + }, + "AuthPickerTitle": { + "comment": "Title for auth picker screen.", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit Firebase anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with Firebase" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con Firebase" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec Firebase" + } + } + } + }, + "Back": { + "comment": "Back button title.", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zurück" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Back" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Atrás" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Retour" + } + } + } + }, + "Cancel": { + "comment": "Cancel button title.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Abbrechen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Cancel" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cancelar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Annuler" + } + } + } + }, + "CannotAuthenticateError": { + "comment": "Error message displayed when the app cannot authenticate user's account.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dieser Kontotyp wird von dieser App nicht unterstützt" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "This type of account isn't supported by this app" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Este tipo de cuenta no es compatible con esta aplicación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Ce type de compte n'est pas pris en charge par cette application" + } + } + } + }, + "CantFindProvider": { + "comment": "Error message displayed when FUIAuth is not configured with third party provider. Parameter is value of provider (e g Google, Facebook etc)", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anbieter für %@ kann nicht gefunden werden." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Can't find provider for %@." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "No se puede encontrar el proveedor para %@." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Impossible de trouver le fournisseur pour %@." + } + } + } + }, + "Change number": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nummer ändern" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Change number" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cambiar número" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Changer le numéro" + } + } + } + }, + "Choose Authentication Method": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Authentifizierungsmethode wählen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Choose Authentication Method" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elige el método de autenticación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Choisir la méthode d'authentification" + } + } + } + }, + "Choose verification method:": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verifizierungsmethode wählen:" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Choose verification method:" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elige el método de verificación:" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Choisis la méthode de vérification :" + } + } + } + }, + "ChoosePassword": { + "comment": "Placeholder for the password text field in a sign up form.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort wählen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Choose password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elegir contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Choisis un mot de passe" + } + } + } + }, + "Close": { + "comment": "Alert button title Close.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Schließen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Close" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cerrar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Fermer" + } + } + } + }, + "Complete Setup": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Einrichtung abschließen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Complete Setup" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Completar configuración" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Terminer la configuration" + } + } + } + }, + "Complete Sign-In": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmeldung abschließen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Complete Sign-In" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Completar inicio de sesión" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Terminer la connexion" + } + } + } + }, + "Complete sign-in with your second factor": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Schließe die Anmeldung mit deinem zweiten Faktor ab" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Complete sign-in with your second factor" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Completa el inicio de sesión con tu segundo factor" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Termine la connexion avec ton deuxième facteur" + } + } + } + }, + "ConfirmEmail": { + "comment": "Title of confirm email label.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "E-Mail bestätigen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Confirm Email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Confirmar correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Confirmer l'e-mail" + } + } + } + }, + "Confirm Password": { + "comment": "Field label for confirming password", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort bestätigen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Confirm Password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Confirmar contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Confirmer le mot de passe" + } + } + } + }, + "ConfirmPasswordInputLabel": { + "comment": "Input label for confirming password when signing up", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort bestätigen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Confirm Password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Confirmar contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Confirmer le mot de passe" + } + } + } + }, + "Copied to clipboard!": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "In die Zwischenablage kopiert!" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Copied to clipboard!" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¡Copiado al portapapeles!" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Copié dans le presse-papiers !" + } + } + } + }, + "Delete": { + "comment": "Text of Delete action button.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Löschen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Eliminar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Supprimer" + } + } + } + }, + "Delete Account": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Konto löschen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete Account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Eliminar cuenta" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Supprimer le compte" + } + } + } + }, + "Delete Account?": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Konto löschen?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete Account?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Eliminar cuenta?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Supprimer le compte ?" + } + } + } + }, + "DeleteAccountBody": { + "comment": "Alert message body shown to confirm account deletion action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dies löscht alle mit deinem Konto verbundenen Daten und kann nicht rückgängig gemacht werden. Du musst dich erneut anmelden, um diese Aktion abzuschließen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "This will erase all data associated with your account, and can't be undone You will need to sign in again to complete this action" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Esto borrará todos los datos asociados con tu cuenta y no se puede deshacer. Deberás iniciar sesión nuevamente para completar esta acción" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Cela effacera toutes les données associées à ton compte et ne peut pas être annulé. Tu devras te reconnecter pour effectuer cette action" + } + } + } + }, + "DeleteAccountConfirmationMessage": { + "comment": "Explanation message shown before deleting account.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dies löscht alle mit deinem Konto verbundenen Daten und kann nicht rückgängig gemacht werden. Bist du sicher, dass du dein Konto löschen möchtest?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "This will erase all data associated with your account, and can't be undone. Are you sure you want to delete your account?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Esto borrará todos los datos asociados con tu cuenta y no se puede deshacer. ¿Estás seguro de que deseas eliminar tu cuenta?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Cela effacera toutes les données associées à ton compte et ne peut pas être annulé. Es-tu sûr de vouloir supprimer ton compte ?" + } + } + } + }, + "DeleteAccountConfirmationTitle": { + "comment": "Alert message title shown to confirm account deletion action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Konto löschen?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete Account?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Eliminar cuenta?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Supprimer le compte ?" + } + } + } + }, + "DeleteAccountControllerTitle": { + "comment": "Title of Controller shown before deleting account", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Konto löschen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Eliminar cuenta" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Supprimer le compte" + } + } + } + }, + "Display Name": { + "comment": "Field label for display name", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anzeigename" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Display Name" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Nombre para mostrar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nom affiché" + } + } + } + }, + "Don't have an account yet?": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Hast du noch kein Konto?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Don't have an account yet?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Aún no tienes una cuenta?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Tu n'as pas encore de compte ?" + } + } + } + }, + "EditEmailTitle": { + "comment": "Controller title shown when editing account email. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "E-Mail bearbeiten" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Edit email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Editar correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Modifier l'e-mail" + } + } + } + }, + "EditNameTitle": { + "comment": "Controller title shown when editing account name.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Name bearbeiten" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Edit name" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Editar nombre" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Modifier le nom" + } + } + } + }, + "EditPasswordAlertMessage": { + "comment": "Alert message shown when editing account password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Um das Passwort deines Kontos zu ändern, musst du dich erneut anmelden." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "To change password to your account, you will need to sign in again." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Para cambiar la contraseña de tu cuenta, deberás iniciar sesión nuevamente." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Pour modifier le mot de passe de ton compte, tu devras te reconnecter." + } + } + } + }, + "EditPasswordTitle": { + "comment": "Controller title shown when editing password to account.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort ändern" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Change password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cambiar contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Changer le mot de passe" + } + } + } + }, + "Email": { + "comment": "Field label for email", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "E-Mail" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "E-mail" + } + } + } + }, + "EmailAlreadyInUseError": { + "comment": "Error message displayed when the email address is already in use. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "migrated", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die E-Mail-Adresse wird bereits von einem anderen Konto verwendet." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "The email address is already in use by another account." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "La dirección de correo ya está en uso por otra cuenta." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Cette adresse e-mail est déjà utilisée par un autre compte." + } + } + } + }, + "Enter code from app": { + "comment": "Prompt for entering code from authenticator app", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Code aus der App eingeben" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter code from app" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa el código de la aplicación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis le code de l'application" + } + } + } + }, + "Enter display name for this authenticator": { + "comment": "Prompt for entering display name for authenticator", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anzeigename für diesen Authentifikator eingeben" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter display name for this authenticator" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa el nombre para mostrar de este autenticador" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis le nom affiché pour cet authentificateur" + } + } + } + }, + "Enter display name for this device": { + "comment": "Prompt for entering display name for device", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anzeigename für dieses Gerät eingeben" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter display name for this device" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa el nombre para mostrar de este dispositivo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis le nom affiché pour cet appareil" + } + } + } }, - "••••••%@" : { - + "Enter phone number": { + "comment": "Prompt for entering phone number", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Telefonnummer eingeben" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter phone number" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa el número de teléfono" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis le numéro de téléphone" + } + } + } }, - "Account: %@" : { - + "EmailLinkSignInLabel": { + "comment": "Button label to push user to email link sign-in", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Lieber mit E-Mail-Link anmelden?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Prefer Email link sign-in?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Prefieres iniciar sesión con enlace por correo?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Préfères-tu la connexion par lien e-mail ?" + } + } + } }, - "AccountDisabledError" : { - "comment" : "Error message displayed when the account is disabled. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "migrated", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "That email address is for an account that has been disabled." + "EmailLinkSignInTitle": { + "comment": "Sign in with email link View title", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit E-Mail-Link anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with email link" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con enlace por correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec un lien e-mail" } } } }, - "ActionCantBeUndone" : { - "comment" : "Alert message shown before account deletion.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This action can't be undone" + "EmailsDontMatch": { + "comment": "Error message displayed when after re-authorization current user's email and re-authorized user's email doesn't match. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "E-Mails stimmen nicht überein" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Emails don't match" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Los correos no coinciden" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Les e-mails ne correspondent pas" } } } }, - "Add an extra layer of security to your account" : { - + "EmailSentConfirmationMessage": { + "comment": "Message displayed after email is sent. The placeholder is the email address that the email is sent to.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Eine Anmelde-E-Mail mit zusätzlichen Anweisungen wurde an %@ gesendet. Überprüfe deine E-Mails, um die Anmeldung abzuschließen." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "A sign-in email with additional instructions was sent to %@. Check your email to complete sign-in." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Se envió un correo de inicio de sesión con instrucciones adicionales a %@. Revisa tu correo para completar el inicio de sesión." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Un e-mail de connexion avec des instructions supplémentaires a été envoyé à %@. Vérifie tes e-mails pour terminer la connexion." + } + } + } }, - "Add Another Method" : { - + "Enrolled Methods": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Registrierte Methoden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enrolled Methods" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Métodos registrados" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Méthodes inscrites" + } + } + } }, - "AddPasswordAlertMessage" : { - "comment" : "Alert message shown when adding account password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "To add password to your account, you will need to sign in again." + "Enrolled: %@": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Registriert: %@" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enrolled: %@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Registrado: %@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Inscrit : %@" } } } }, - "AddPasswordTitle" : { - "comment" : "Controller title shown when adding password to account.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Add password" + "Enter 6-digit code": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "6-stelligen Code eingeben" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter 6-digit code" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa el código de 6 dígitos" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis le code à 6 chiffres" } } } }, - "Already have an account?" : { - + "Enter the 6-digit code from your authenticator app": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gib den 6-stelligen Code aus deiner Authentifizierungs-App ein" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter the 6-digit code from your authenticator app" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa el código de 6 dígitos de tu aplicación de autenticación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis le code à 6 chiffres de ton application d'authentification" + } + } + } }, - "AS_AddPassword" : { - "comment" : "Account Settings cell title Add Password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Add password" + "Enter Verification Code": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bestätigungscode eingeben" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter Verification Code" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa el código de verificación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis le code de vérification" } } } }, - "AS_ChangePassword" : { - "comment" : "Account Settings cell title Change Password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Change password" + "Enter Your Phone Number": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gib deine Telefonnummer ein" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter Your Phone Number" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa tu número de teléfono" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis ton numéro de téléphone" } } } }, - "AS_DeleteAccount" : { - "comment" : "Account Settings cell title Delete Account.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete Account" + "EnterYourEmail": { + "comment": "Title for email entry screen, email text field placeholder. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gib deine E-Mail ein" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter your email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa tu correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis ton e-mail" } } } }, - "AS_Email" : { - "comment" : "Account Settings cell title Email. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Email" + "EnterYourPassword": { + "comment": "Password text field placeholder.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gib dein Passwort ein" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter your password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa tu contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis ton mot de passe" } } } }, - "AS_Name" : { - "comment" : "Account Settings cell title Name.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Name" + "Error": { + "comment": "Alert title Error.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Fehler" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Error" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Error" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Erreur" } } } }, - "AS_SectionLinkedAccounts" : { - "comment" : "Account Settings section title Linked Accounts.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Linked Accounts" + "ExistingAccountTitle": { + "comment": "Title of an alert shown to an existing user coming back to the app.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Du hast bereits ein Konto" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "You already have an account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ya tienes una cuenta" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Tu as déjà un compte" } } } }, - "AS_SectionProfile" : { - "comment" : "Account Settings section title Profile.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Profile" + "FirstAndLastName": { + "comment": "Name text field placeholder.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Vor- und Nachname" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "First & last name" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Nombre y apellido" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Prénom et nom" } } } }, - "AS_SectionSecurity" : { - "comment" : "Account Settings section title Security.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Security" + "ForgotPassword": { + "comment": "Button text for 'Forgot Password' action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort-Wiederherstellungs-E-Mail senden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Send password recovery email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviar correo de recuperación de contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Envoyer un e-mail de récupération de mot de passe" } } } }, - "AS_SignOut" : { - "comment" : "Account Settings cell title Sign Out.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign Out" + "ForgotPasswordTitle": { + "comment": "Title of forgot password button.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Probleme beim Anmelden?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Trouble signing in?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Problemas para iniciar sesión?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Problème de connexion ?" } } } }, - "Authentication Method" : { - + "Get Started": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Loslegen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Get Started" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Comenzar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Commencer" + } + } + } }, - "Authenticator App" : { - + "InvalidEmailError": { + "comment": "Error message displayed when user enters an invalid email address. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "migrated", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Diese E-Mail-Adresse ist nicht korrekt." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "That email address isn't correct." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Esa dirección de correo no es correcta." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Cette adresse e-mail n'est pas correcte." + } + } + } }, - "AuthPickerTitle" : { - "comment" : "Title for auth picker screen.", - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign in with Firebase" + "Invalid OAuth Provider": { + "comment": "Error message displayed when OAuth provider configuration is invalid.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ungültiger OAuth-Anbieter" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Invalid OAuth Provider" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Proveedor OAuth inválido" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Fournisseur OAuth invalide" } } } }, - "Back" : { - "comment" : "Back button title." + "InvalidPasswordError": { + "comment": "Error message displayed when user enters an empty password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort darf nicht leer sein." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Password cannot be empty." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "La contraseña no puede estar vacía." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Le mot de passe ne peut pas être vide." + } + } + } }, - "Cancel" : { - "comment" : "Cancel button title.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cancel" + "Login": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Login" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Connexion" } } } }, - "CannotAuthenticateError" : { - "comment" : "Error message displayed when the app cannot authenticate user's account.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This type of account isn't supported by this app" + "Manage Two-Factor Authentication": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zwei-Faktor-Authentifizierung verwalten" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Manage Two-Factor Authentication" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Administrar autenticación de dos factores" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Gérer l'authentification à deux facteurs" } } } }, - "CantFindProvider" : { - "comment" : "Error message displayed when FUIAuth is not configured with third party provider. Parameter is value of provider (e g Google, Facebook etc)", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Can't find provider for %@." + "Manage your authentication methods": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verwalte deine Authentifizierungsmethoden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Manage your authentication methods" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Administra tus métodos de autenticación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Gère tes méthodes d'authentification" } } } }, - "Change number" : { - + "Manual Entry Key:": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Manueller Eingabeschlüssel:" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Manual Entry Key:" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Clave de entrada manual:" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Clé de saisie manuelle :" + } + } + } }, - "Choose Authentication Method" : { - + "MFA is not enabled in the current configuration. Please contact your administrator.": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "MFA ist in der aktuellen Konfiguration nicht aktiviert. Bitte kontaktiere deinen Administrator." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "MFA is not enabled in the current configuration. Please contact your administrator." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "MFA no está habilitado en la configuración actual. Por favor, contacta a tu administrador." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "L'authentification multifacteur n'est pas activée dans la configuration actuelle. Contacte ton administrateur." + } + } + } }, - "Choose verification method:" : { - + "Multi-Factor Authentication Disabled": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mehr-Faktor-Authentifizierung deaktiviert" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Multi-Factor Authentication Disabled" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Autenticación multifactor deshabilitada" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Authentification multifacteur désactivée" + } + } + } }, - "ChoosePassword" : { - "comment" : "Placeholder for the password text field in a sign up form.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Choose password" + "Name": { + "comment": "Label next to a name text field.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Name" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Name" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Nombre" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nom" } } } }, - "Close" : { - "comment" : "Alert button title Close.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Close" + "Next": { + "comment": "Next button title.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Weiter" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Next" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Siguiente" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Suivant" } } } }, - "Complete Setup" : { - + "No Authentication Methods": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Authentifizierungsmethoden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "No Authentication Methods" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Sin métodos de autenticación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Aucune méthode d'authentification" + } + } + } }, - "Complete Sign-In" : { - + "No Authentication Methods Available": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Authentifizierungsmethoden verfügbar" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "No Authentication Methods Available" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "No hay métodos de autenticación disponibles" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Aucune méthode d'authentification disponible" + } + } + } }, - "Complete sign-in with your second factor" : { - + "No MFA methods are configured as allowed. Please contact your administrator.": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine MFA-Methoden sind als erlaubt konfiguriert. Bitte kontaktiere deinen Administrator." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "No MFA methods are configured as allowed. Please contact your administrator." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "No hay métodos MFA configurados como permitidos. Por favor, contacta a tu administrador." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Aucune méthode d'authentification multifacteur n'est configurée comme autorisée. Contacte ton administrateur." + } + } + } }, - "ConfirmEmail" : { - "comment" : "Title of confirm email label.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirm Email" + "OK": { + "comment": "OK button title.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "OK" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "OK" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Aceptar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "OK" } } } }, - "ConfirmPasswordInputLabel" : { - "comment" : "Input label for confirming password when signing up", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirm Password" + "Password": { + "comment": "Field label for password", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Mot de passe" } } } }, - "Copied to clipboard!" : { - + "PasswordRecoveryEmailSentMessage": { + "comment": "Message displayed when the email for password recovery has been sent.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Folge den Anweisungen, die an %@ gesendet wurden, um dein Passwort wiederherzustellen." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Follow the instructions sent to %@ to recover your password." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Sigue las instrucciones enviadas a %@ para recuperar tu contraseña." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Suis les instructions envoyées à %@ pour récupérer ton mot de passe." + } + } + } }, - "Delete" : { - "comment" : "Text of Delete action button.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete" + "PasswordRecoveryEmailSentTitle": { + "comment": "Title of a message displayed when the email for password recovery has been sent. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Überprüfe deine E-Mails" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Check your email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Revisa tu correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Vérifie tes e-mails" } } } }, - "Delete Account" : { - + "PasswordRecoveryMessage": { + "comment": "Explanation on how the password of an account can be recovered. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Erhalte Anweisungen an diese E-Mail, die erklären, wie du dein Passwort zurücksetzen kannst." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Get instructions sent to this email that explain how to reset your password." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Recibe instrucciones enviadas a este correo que explican cómo restablecer tu contraseña." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Reçois des instructions envoyées à cet e-mail qui expliquent comment réinitialiser ton mot de passe." + } + } + } }, - "Delete Account?" : { - + "PasswordRecoveryTitle": { + "comment": "Title for password recovery screen.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort wiederherstellen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Recover password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Recuperar contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Récupérer le mot de passe" + } + } + } }, - "DeleteAccountBody" : { - "comment" : "Alert message body shown to confirm account deletion action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This will erase all data associated with your account, and can't be undone You will need to sign in again to complete this action" + "PasswordVerificationMessage": { + "comment": "Message to explain to the user that password is needed for an account with this email address.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Du hast bereits %@ verwendet, um dich anzumelden. Gib dein Passwort für dieses Konto ein." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "You've already used %@ to sign in. Enter your password for that account." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ya has usado %@ para iniciar sesión. Ingresa tu contraseña para esa cuenta." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Tu as déjà utilisé %@ pour te connecter. Saisis ton mot de passe pour ce compte." } } } }, - "DeleteAccountConfirmationMessage" : { - "comment" : "Explanation message shown before deleting account.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This will erase all data associated with your account, and can't be undone. Are you sure you want to delete your account?" + "Phone": { + "comment": "Field label for phone", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Telefon" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Phone" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Teléfono" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Téléphone" } } } }, - "DeleteAccountConfirmationTitle" : { - "comment" : "Alert message title shown to confirm account deletion action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete Account?" + "Phone Number": { + "comment": "Field label for phone number", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Telefonnummer" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Phone Number" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Número de teléfono" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Numéro de téléphone" } } } }, - "DeleteAccountControllerTitle" : { - "comment" : "Title of Controller shown before deleting account", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete account" + "PlaceholderChosePassword": { + "comment": "Placeholder of secret input cell when user changes password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort wählen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Choose password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Elegir contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Choisis un mot de passe" } } } }, - "Display Name" : { - - }, - "Don't have an account yet?" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Don't have an account yet?" + "PlaceholderEnterEmail": { + "comment": "Placeholder of input cell when user changes name. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gib deine E-Mail ein" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter your email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa tu correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis ton e-mail" } } } }, - "EditEmailTitle" : { - "comment" : "Controller title shown when editing account email. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Edit email" + "PlaceholderEnterName": { + "comment": "Placeholder of input cell when user changes name.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gib deinen Namen ein" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter your name" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa tu nombre" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis ton nom" } } } }, - "EditNameTitle" : { - "comment" : "Controller title shown when editing account name.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Edit name" + "PlaceholderEnterPassword": { + "comment": "Placeholder of secret input cell when user changes password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gib dein Passwort ein" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter your password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ingresa tu contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Saisis ton mot de passe" } } } }, - "EditPasswordAlertMessage" : { - "comment" : "Alert message shown when editing account password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "To change password to your account, you will need to sign in again." + "PlaceholderNewPassword": { + "comment": "Placeholder of secret input cell when user confirms password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Neues Passwort" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "New password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Nueva contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nouveau mot de passe" } } } }, - "EditPasswordTitle" : { - "comment" : "Controller title shown when editing password to account.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Change password" + "PrivacyPolicy": { + "comment": "Text linked to a web page with the Privacy Policy content.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Datenschutzrichtlinie" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Privacy Policy" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Política de privacidad" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Politique de confidentialité" } } } }, - "EmailAlreadyInUseError" : { - "comment" : "Error message displayed when the email address is already in use. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "migrated", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The email address is already in use by another account." + "ProviderTitleFacebook": { + "comment": "Title of Facebook provider", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Facebook" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Facebook" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Facebook" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Facebook" } } } }, - "EmailLinkSignInLabel" : { - "comment" : "Button label to push user to email link sign-in", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Prefer Email link sign-in?" + "ProviderTitleGoogle": { + "comment": "Title of Google provider", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Google" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Google" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Google" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Google" } } } }, - "EmailLinkSignInTitle" : { - "comment" : "Sign in with email link View title", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign in with email link" + "ProviderTitlePassword": { + "comment": "Title of Password/Email provider. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "E-Mail" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "E-mail" } } } }, - "EmailsDontMatch" : { - "comment" : "Error message displayed when after re-authorization current user's email and re-authorized user's email doesn't match. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Emails don't match" + "ProviderTitleTwitter": { + "comment": "Title of Twitter provider", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Twitter" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Twitter" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Twitter" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Twitter" } } } }, - "EmailSentConfirmationMessage" : { - "comment" : "Message displayed after email is sent. The placeholder is the email address that the email is sent to.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "A sign-in email with additional instructions was sent to %@. Check your email to complete sign-in." + "ProviderUsedPreviouslyMessage": { + "comment": "Alert message to let user know what identity provider (second placeholder, ex. Google) was used previously for the email address (first placeholder).", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Du hast bereits %@ verwendet. Melde dich mit %@ an, um fortzufahren." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "You've already used %@. Sign in with %@ to continue." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ya has usado %@. Inicia sesión con %@ para continuar." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Tu as déjà utilisé %@. Connecte-toi avec %@ pour continuer." } } } }, - "Enrolled Methods" : { - - }, - "Enrolled: %@" : { - - }, - "Enter 6-digit code" : { - - }, - "Enter the 6-digit code from your authenticator app" : { - - }, - "Enter Verification Code" : { - - }, - "Enter Your Phone Number" : { - - }, - "EnterYourEmail" : { - "comment" : "Title for email entry screen, email text field placeholder. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter your email" + "ReauthenticateEditPasswordAlertMessage": { + "comment": "Alert message shown when re-authenticating before editing account password.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Um dein Passwort zu ändern, musst du zuerst dein aktuelles Passwort eingeben." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "In order to change your password, you first need to enter your current password." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Para cambiar tu contraseña, primero debes ingresar tu contraseña actual." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Pour modifier ton mot de passe, tu dois d'abord saisir ton mot de passe actuel." } } } }, - "EnterYourPassword" : { - "comment" : "Password text field placeholder.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter your password" + "Remove": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Entfernen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Remove" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Eliminar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Retirer" } } } }, - "Error" : { - "comment" : "Alert title Error.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Error" + "Resend": { + "comment": "Resend button title.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Erneut senden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Resend" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Reenviar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Renvoyer" } } } }, - "ExistingAccountTitle" : { - "comment" : "Title of an alert shown to an existing user coming back to the app.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "You already have an account" + "Resend Code": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Code erneut senden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Resend Code" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Reenviar código" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Renvoyer le code" } } } }, - "FirstAndLastName" : { - "comment" : "Name text field placeholder.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "First & last name" + "Save": { + "comment": "Save button title.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Speichern" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Save" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Guardar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Enregistrer" } } } }, - "ForgotPassword" : { - "comment" : "Button text for 'Forgot Password' action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Send password recovery email" + "Scan QR Code": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "QR-Code scannen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Scan QR Code" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Escanear código QR" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Scanner le code QR" } } } }, - "ForgotPasswordTitle" : { - "comment" : "Title of forgot password button.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trouble signing in?" + "Scan with your authenticator app or tap to open directly": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Scanne mit deiner Authentifizierungs-App oder tippe, um direkt zu öffnen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Scan with your authenticator app or tap to open directly" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Escanea con tu aplicación de autenticación o toca para abrir directamente" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Scanne avec ton application d'authentification ou appuie pour ouvrir directement" } } } }, - "Get Started" : { - - }, - "InvalidEmailError" : { - "comment" : "Error message displayed when user enters an invalid email address. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "migrated", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "That email address isn't correct." + "Send": { + "comment": "Send button title.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Senden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Send" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviar" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Envoyer" } } } }, - "InvalidPasswordError" : { - "comment" : "Error message displayed when user enters an empty password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Password cannot be empty." + "Send Code": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Code senden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Send Code" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviar código" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Envoyer le code" } } } }, - "Login" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Login" + "SendEmailSignInLinkButtonLabel": { + "comment": "Button label for sending email sign-in link", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "E-Mail-Anmeldelink senden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Send email sign-in link" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviar enlace de inicio de sesión por correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Envoyer le lien de connexion par e-mail" } } } }, - "Manage Two-Factor Authentication" : { - - }, - "Manage your authentication methods" : { - - }, - "Manual Entry Key:" : { - - }, - "MFA is not enabled in the current configuration. Please contact your administrator." : { - - }, - "Multi-Factor Authentication Disabled" : { - - }, - "Name" : { - "comment" : "Label next to a name text field.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Name" + "Set Up Two-Factor Authentication": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zwei-Faktor-Authentifizierung einrichten" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Set Up Two-Factor Authentication" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Configurar autenticación de dos factores" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Configurer l'authentification à deux facteurs" } } } }, - "Next" : { - "comment" : "Next button title.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Next" + "Set up two-factor authentication to add an extra layer of security to your account.": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Richte die Zwei-Faktor-Authentifizierung ein, um deinem Konto eine zusätzliche Sicherheitsebene hinzuzufügen." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Set up two-factor authentication to add an extra layer of security to your account." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Configura la autenticación de dos factores para agregar una capa adicional de seguridad a tu cuenta." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Configure l'authentification à deux facteurs pour ajouter une couche de sécurité supplémentaire à ton compte." } } } }, - "No Authentication Methods" : { - - }, - "No Authentication Methods Available" : { - - }, - "No MFA methods are configured as allowed. Please contact your administrator." : { - - }, - "OK" : { - "comment" : "OK button title.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "OK" + "Sign up": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Registrieren" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign up" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Registrarse" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "S'inscrire" } } } }, - "PasswordRecoveryEmailSentMessage" : { - "comment" : "Message displayed when the email for password recovery has been sent.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Follow the instructions sent to %@ to recover your password." + "SignedIn": { + "comment": "Title of successfully signed in label.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Angemeldet!" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Signed in!" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¡Sesión iniciada!" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Connecté !" } } } }, - "PasswordRecoveryEmailSentTitle" : { - "comment" : "Title of a message displayed when the email for password recovery has been sent. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Check your email" + "SignInEmailSent": { + "comment": "Message displayed after the email of sign-in link is sent.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmelde-E-Mail gesendet" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign-in email Sent" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Correo de inicio de sesión enviado" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "E-mail de connexion envoyé" } } } }, - "PasswordRecoveryMessage" : { - "comment" : "Explanation on how the password of an account can be recovered. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Get instructions sent to this email that explain how to reset your password." + "SignInTitle": { + "comment": "Title for sign in screen and sign in button.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter" } } } }, - "PasswordRecoveryTitle" : { - "comment" : "Title for password recovery screen.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recover password" + "SignInTooManyTimesError": { + "comment": "Error message displayed after user trying to sign in too many times.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Du hast zu oft ein falsches Passwort eingegeben. Versuche es in ein paar Minuten erneut." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "You've entered an incorrect password too many times. Try again in a few minutes." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Has ingresado una contraseña incorrecta demasiadas veces. Intenta nuevamente en unos minutos." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Tu as saisi un mot de passe incorrect trop de fois. Réessaie dans quelques minutes." } } } }, - "PasswordVerificationMessage" : { - "comment" : "Message to explain to the user that password is needed for an account with this email address.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "You’ve already used %@ to sign in. Enter your password for that account." + "Sign in with Apple": { + "comment": "Sign in with Apple button label.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit Apple anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with Apple" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con Apple" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec Apple" } } } }, - "Phone Number" : { - - }, - "PlaceholderChosePassword" : { - "comment" : "Placeholder of secret input cell when user changes password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Choose password" + "Sign in with Facebook": { + "comment": "Sign in with Facebook button label.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit Facebook anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with Facebook" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con Facebook" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec Facebook" } } } }, - "PlaceholderEnterEmail" : { - "comment" : "Placeholder of input cell when user changes name. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter your email" + "Sign in with Google": { + "comment": "Sign in with Google button label.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit Google anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with Google" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con Google" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec Google" } } } }, - "PlaceholderEnterName" : { - "comment" : "Placeholder of input cell when user changes name.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter your name" + "Sign in with Phone": { + "comment": "Sign in with Phone button label.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit Telefon anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with Phone" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con teléfono" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec un téléphone" } } } }, - "PlaceholderEnterPassword" : { - "comment" : "Placeholder of secret input cell when user changes password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter your password" + "Sign in with X": { + "comment": "Sign in with X (Twitter) button label.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit X anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with X" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con X" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec X" } } } }, - "PlaceholderNewPassword" : { - "comment" : "Placeholder of secret input cell when user confirms password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "New password" + "SignInWithEmail": { + "comment": "Sign in with email button label. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit E-Mail anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec un e-mail" } } } }, - "PrivacyPolicy" : { - "comment" : "Text linked to a web page with the Privacy Policy content.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Privacy Policy" + "SignInWithProvider": { + "comment": "Sign in with provider button label.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit %@ anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with %@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Iniciar sesión con %@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Se connecter avec %@" } } } }, - "ProviderTitleFacebook" : { - "comment" : "Title of Facebook provider", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Facebook" + "SignUpTitle": { + "comment": "Title for sign up screen.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Konto erstellen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Create account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Crear cuenta" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Créer un compte" } } } }, - "ProviderTitleGoogle" : { - "comment" : "Title of Google provider", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Google" + "SignUpTooManyTimesError": { + "comment": "Error message displayed when many accounts have been created from same IP address.", + "extractionState": "migrated", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zu viele Kontoanfragen kommen von deiner IP-Adresse. Versuche es in ein paar Minuten erneut." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Too many account requests are coming from your IP address. Try again in a few minutes." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Demasiadas solicitudes de cuenta provienen de tu dirección IP. Intenta nuevamente en unos minutos." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Trop de demandes de compte proviennent de ton adresse IP. Réessaie dans quelques minutes." } } } }, - "ProviderTitlePassword" : { - "comment" : "Title of Password/Email provider. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Email" + "SMS Authentication": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "SMS-Authentifizierung" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "SMS Authentication" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Autenticación por SMS" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Authentification par SMS" } } } }, - "ProviderTitleTwitter" : { - "comment" : "Title of Twitter provider", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Twitter" + "SMS Verification": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "SMS-Verifizierung" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "SMS Verification" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Verificación por SMS" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Vérification par SMS" } } } }, - "ProviderUsedPreviouslyMessage" : { - "comment" : "Alert message to let user know what identity provider (second placeholder, ex. Google) was used previously for the email address (first placeholder).", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "You’ve already used %@. Sign in with %@ to continue." + "SMS: %@": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "SMS: %@" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "SMS: %@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "SMS: %@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "SMS : %@" } } } }, - "ReauthenticateEditPasswordAlertMessage" : { - "comment" : "Alert message shown when re-authenticating before editing account password.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "In order to change your password, you first need to enter your current password." + "Tap to open in authenticator app": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Tippe, um in der Authentifizierungs-App zu öffnen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Tap to open in authenticator app" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Toca para abrir en la aplicación de autenticación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Appuie pour ouvrir dans l'application d'authentification" } } } }, - "Remove" : { - - }, - "Resend" : { - "comment" : "Resend button title.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Resend" + "TermsOfService": { + "comment": "Text linked to a web page with the Terms of Service content.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nutzungsbedingungen" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Terms of Service" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Términos de servicio" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Conditions d'utilisation" } } } }, - "Resend Code" : { - - }, - "Save" : { - "comment" : "Save button title.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Save" + "TermsOfServiceMessage": { + "comment": "A message displayed when the first log in screen is displayed. The first placeholder is the terms of service agreement link, the second place holder is the privacy policy agreement link.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Indem du fortfährst, bestätigst du, dass du unsere %@ und %@ akzeptierst." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "By continuing, you are indicating that you accept our %@ and %@." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Al continuar, indicas que aceptas nuestros %@ y %@." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "En continuant, tu indiques que tu acceptes nos %@ et notre %@." } } } }, - "Scan QR Code" : { - - }, - "Scan with your authenticator app or tap to open directly" : { - - }, - "Send" : { - "comment" : "Send button title.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Send" + "This action cannot be undone. All your data will be permanently deleted. You may need to reauthenticate to complete this action.": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Diese Aktion kann nicht rückgängig gemacht werden. Alle deine Daten werden dauerhaft gelöscht. Du musst dich möglicherweise erneut authentifizieren, um diese Aktion abzuschließen." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "This action cannot be undone. All your data will be permanently deleted. You may need to reauthenticate to complete this action." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Esta acción no se puede deshacer. Todos tus datos se eliminarán permanentemente. Es posible que debas volver a autenticarte para completar esta acción." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Cette action ne peut pas être annulée. Toutes tes données seront définitivement supprimées. Tu devras peut-être te réauthentifier pour effectuer cette action." } } } }, - "Send Code" : { - - }, - "SendEmailSignInLinkButtonLabel" : { - "comment" : "Button label for sending email sign-in link", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Send email sign-in link" + "TroubleGettingEmailMessage": { + "comment": "Alert message displayed when user having trouble getting email.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Versuche diese gängigen Lösungen: \n - Überprüfe, ob die E-Mail als Spam markiert oder gefiltert wurde.\n - Überprüfe deine Internetverbindung.\n - Überprüfe, ob du deine E-Mail nicht falsch geschrieben hast.\n - Überprüfe, ob dein Postfach voll ist oder andere Probleme mit den Postfacheinstellungen vorliegen.\n Wenn die obigen Schritte nicht funktioniert haben, kannst du die E-Mail erneut senden. Beachte, dass dadurch der Link in der älteren E-Mail deaktiviert wird." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Try these common fixes: \n - Check if the email was marked as spam or filtered.\n - Check your internet connection.\n - Check that you did not misspell your email.\n - Check that your inbox space is not running out or other inbox settings related issues.\n If the steps above didn't work, you can resend the email. Note that this will deactivate the link in the older email." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Prueba estas soluciones comunes: \n - Verifica si el correo fue marcado como spam o filtrado.\n - Verifica tu conexión a internet.\n - Verifica que no hayas escrito mal tu correo.\n - Verifica que tu bandeja de entrada no esté llena u otros problemas relacionados con la configuración de la bandeja de entrada.\n Si los pasos anteriores no funcionaron, puedes reenviar el correo. Ten en cuenta que esto desactivará el enlace del correo anterior." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Essaie ces solutions courantes :\n - Vérifie si l'e-mail a été marqué comme spam ou filtré.\n - Vérifie ta connexion Internet.\n - Vérifie que tu n'as pas mal orthographié ton e-mail.\n - Vérifie que l'espace de ta boîte de réception n'est pas saturé ou qu'il n'y a pas d'autres problèmes de paramètres de boîte de réception.\n Si les étapes ci-dessus n'ont pas fonctionné, tu peux renvoyer l'e-mail. Note que cela désactivera le lien dans l'ancien e-mail." } } } }, - "Set Up Two-Factor Authentication" : { - - }, - "Set up two-factor authentication to add an extra layer of security to your account." : { - - }, - "Sign up" : { - - }, - "SignedIn" : { - "comment" : "Title of successfully signed in label.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Signed in!" + "TroubleGettingEmailTitle": { + "comment": "Title used in trouble getting email alert view.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Probleme beim Empfang von E-Mails?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Trouble getting emails?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Problemas para recibir correos?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Problème pour recevoir les e-mails ?" } } } }, - "SignInEmailSent" : { - "comment" : "Message displayed after the email of sign-in link is sent.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign-in email Sent" + "Two-Factor Authentication": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zwei-Faktor-Authentifizierung" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Two-Factor Authentication" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Autenticación de dos factores" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Authentification à deux facteurs" } } } }, - "SignInTitle" : { - "comment" : "Title for sign in screen and sign in button.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign in" + "Unable to generate QR Code": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "QR-Code kann nicht generiert werden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Unable to generate QR Code" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "No se puede generar el código QR" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Impossible de générer le code QR" } } } }, - "SignInTooManyTimesError" : { - "comment" : "Error message displayed after user trying to sign in too many times.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "You’ve entered an incorrect password too many times. Try again in a few minutes." + "UnlinkAction": { + "comment": "Button title for unlinking account action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verknüpfung aufheben" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Unlink" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Desvincular" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Délier" } } } }, - "SignInWithEmail" : { - "comment" : "Sign in with email button label. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign in with email" + "UnlinkConfirmationActionTitle": { + "comment": "Alert action title shown before unlinking action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Kontoverknüpfung aufheben" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Unlink account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Desvincular cuenta" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Délier le compte" } } } }, - "SignInWithProvider" : { - "comment" : "Sign in with provider button label.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign in with %@" + "UnlinkConfirmationMessage": { + "comment": "Alert message shown before unlinking action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Du kannst dich nicht mehr mit deinem Konto anmelden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "You will no longer be able to sign in using your account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Ya no podrás iniciar sesión usando tu cuenta" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Tu ne pourras plus te connecter en utilisant ton compte" } } } }, - "SignUpTitle" : { - "comment" : "Title for sign up screen.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Create account" + "UnlinkConfirmationTitle": { + "comment": "Alert title shown before unlinking action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Kontoverknüpfung aufheben?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Unlink account?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Desvincular cuenta?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Délier le compte ?" } } } }, - "SignUpTooManyTimesError" : { - "comment" : "Error message displayed when many accounts have been created from same IP address.", - "extractionState" : "migrated", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Too many account requests are coming from your IP address. Try again in a few minutes." + "UnlinkTitle": { + "comment": "Controller title shown for unlinking account action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verknüpftes Konto" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Linked account" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Cuenta vinculada" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Compte lié" } } } }, - "SMS Authentication" : { - - }, - "SMS Verification" : { - - }, - "SMS: %@" : { - - }, - "Tap to open in authenticator app" : { - - }, - "TermsOfService" : { - "comment" : "Text linked to a web page with the Terms of Service content.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Terms of Service" + "Update password": { + "comment": "Update password button label", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort aktualisieren" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Update password" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Actualizar contraseña" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Mettre à jour le mot de passe" } } } }, - "TermsOfServiceMessage" : { - "comment" : "A message displayed when the first log in screen is displayed. The first placeholder is the terms of service agreement link, the second place holder is the privacy policy agreement link.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "By continuing, you are indicating that you accept our %@ and %@." + "UpdateEmailAlertMessage": { + "comment": "Alert action message shown before updating email action. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Um die mit deinem Konto verknüpfte E-Mail-Adresse zu ändern, musst du dich erneut anmelden." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "To change email address associated with your account, you will need to sign in again." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Para cambiar la dirección de correo asociada con tu cuenta, deberás iniciar sesión nuevamente." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Pour modifier l'adresse e-mail associée à ton compte, tu devras te reconnecter." } } } }, - "This action cannot be undone. All your data will be permanently deleted. You may need to reauthenticate to complete this action." : { - - }, - "TroubleGettingEmailMessage" : { - "comment" : "Alert message displayed when user having trouble getting email.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Try these common fixes: \n - Check if the email was marked as spam or filtered.\n - Check your internet connection.\n - Check that you did not misspell your email.\n - Check that your inbox space is not running out or other inbox settings related issues.\n If the steps above didn't work, you can resend the email. Note that this will deactivate the link in the older email." + "UpdateEmailVerificationAlertMessage": { + "comment": "Alert action message shown before confirmation of updating email action.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Um dein Passwort zu ändern, musst du zuerst dein aktuelles Passwort eingeben." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "In order to change your password, you first need to enter your current password." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Para cambiar tu contraseña, primero debes ingresar tu contraseña actual." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Pour modifier ton mot de passe, tu dois d'abord saisir ton mot de passe actuel." } } } }, - "TroubleGettingEmailTitle" : { - "comment" : "Title used in trouble getting email alert view.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trouble getting emails?" + "Use an authenticator app like Google Authenticator or Authy to generate verification codes.": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verwende eine Authentifizierungs-App wie Google Authenticator oder Authy, um Bestätigungscodes zu generieren." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Use an authenticator app like Google Authenticator or Authy to generate verification codes." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Usa una aplicación de autenticación como Google Authenticator o Authy para generar códigos de verificación." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Utilise une application d'authentification comme Google Authenticator ou Authy pour générer des codes de vérification." } } } }, - "Two-Factor Authentication" : { - - }, - "Unable to generate QR Code" : { - - }, - "UnlinkAction" : { - "comment" : "Button title for unlinking account action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unlink" + "UserNotFoundError": { + "comment": "Error message displayed when there's no account matching the email address. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "migrated", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Diese E-Mail-Adresse stimmt mit keinem vorhandenen Konto überein." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "That email address doesn't match an existing account." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Esa dirección de correo no coincide con una cuenta existente." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Cette adresse e-mail ne correspond à aucun compte existant." } } } }, - "UnlinkConfirmationActionTitle" : { - "comment" : "Alert action title shown before unlinking action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unlink account" + "Send a password recovery link to your email": { + "comment": "Field label for password recovery email", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort-Wiederherstellungslink an deine E-Mail senden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Send a password recovery link to your email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviar un enlace de recuperación de contraseña a tu correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Envoyer un lien de récupération de mot de passe à ton e-mail" } } } }, - "UnlinkConfirmationMessage" : { - "comment" : "Alert message shown before unlinking action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "You will no longer be able to sign in using your account" + "Send a sign-in link to your email": { + "comment": "Field label for sign-in email link", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmeldelink an deine E-Mail senden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Send a sign-in link to your email" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviar un enlace de inicio de sesión a tu correo" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Envoyer un lien de connexion à ton e-mail" } } } }, - "UnlinkConfirmationTitle" : { - "comment" : "Alert title shown before unlinking action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unlink account?" + "Verification Code": { + "comment": "Field label for verification code", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bestätigungscode" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Verification Code" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Código de verificación" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Code de vérification" } } } }, - "UnlinkTitle" : { - "comment" : "Controller title shown for unlinking account action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Linked account" + "Verify email address?": { + "comment": "Label for sending email verification to user.", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "E-Mail-Adresse verifizieren?" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Verify email address?" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "¿Verificar dirección de correo?" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Vérifier l'adresse e-mail ?" } } } }, - "Update password" : { - "comment" : "Update password button label", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Update password" + "VerifyItsYou": { + "comment": "Alert message title show for re-authorization.", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bestätige, dass du es bist" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Verify it's you" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Verifica que eres tú" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Vérifie que c'est toi" } } } }, - "UpdateEmailAlertMessage" : { - "comment" : "Alert action message shown before updating email action. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "To change email address associated with your account, you will need to sign in again." + "We sent a code to %@": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wir haben einen Code an %@ gesendet" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "We sent a code to %@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviamos un código a %@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nous avons envoyé un code à %@" } } } }, - "UpdateEmailVerificationAlertMessage" : { - "comment" : "Alert action message shown before confirmation of updating email action.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "In order to change your password, you first need to enter your current password." + "We'll send a code to ••••••%@": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wir senden einen Code an ••••••%@" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "We'll send a code to ••••••%@" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviaremos un código a ••••••%@" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nous enverrons un code à ••••••%@" } } } }, - "Use an authenticator app like Google Authenticator or Authy to generate verification codes." : { - - }, - "UserNotFoundError" : { - "comment" : "Error message displayed when there's no account matching the email address. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "migrated", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "That email address doesn’t match an existing account." + "We'll send a verification code to this number": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wir senden einen Bestätigungscode an diese Nummer" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "We'll send a verification code to this number" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviaremos un código de verificación a este número" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nous enverrons un code de vérification à ce numéro" } } } }, - "Verification Code" : { - - }, - "Verify email address?" : { - "comment" : "Label for sending email verification to user.", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verify email address?" + "We'll send a verification code to your phone": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wir senden einen Bestätigungscode an dein Telefon" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "We'll send a verification code to your phone" + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviaremos un código de verificación a tu teléfono" + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nous enverrons un code de vérification à ton téléphone" } } } }, - "VerifyItsYou" : { - "comment" : "Alert message title show for re-authorization.", - "extractionState" : "manual", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verify it's you" + "We'll send a verification code to your phone number each time you sign in.": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wir senden jedes Mal einen Bestätigungscode an deine Telefonnummer, wenn du dich anmeldest." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "We'll send a verification code to your phone number each time you sign in." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "Enviaremos un código de verificación a tu número de teléfono cada vez que inicies sesión." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Nous enverrons un code de vérification à ton numéro de téléphone chaque fois que tu te connectes." } } } }, - "We sent a code to %@" : { - - }, - "We'll send a code to ••••••%@" : { - - }, - "We'll send a verification code to this number" : { - - }, - "We'll send a verification code to your phone" : { - - }, - "We'll send a verification code to your phone number each time you sign in." : { - - }, - "WeakPasswordError" : { - "comment" : "Error message displayed when the password is too weak.", - "extractionState" : "migrated", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Password must be at least 6 characters long." + "WeakPasswordError": { + "comment": "Error message displayed when the password is too weak.", + "extractionState": "migrated", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort muss mindestens 6 Zeichen lang sein." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "Password must be at least 6 characters long." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "La contraseña debe tener al menos 6 caracteres." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "Le mot de passe doit contenir au moins 6 caractères." } } } }, - "WrongPasswordError" : { - "comment" : "Error message displayed when the email and password don't match. Use short/abbreviated translation for 'email' which is less than 15 chars.", - "extractionState" : "migrated", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The email and password you entered don't match." + "WrongPasswordError": { + "comment": "Error message displayed when the email and password don't match. Use short/abbreviated translation for 'email' which is less than 15 chars.", + "extractionState": "migrated", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die E-Mail und das Passwort, die du eingegeben hast, stimmen nicht überein." + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "The email and password you entered don't match." + } + }, + "es": { + "stringUnit": { + "state": "translated", + "value": "El correo y la contraseña que ingresaste no coinciden." + } + }, + "fr": { + "stringUnit": { + "state": "translated", + "value": "L'e-mail et le mot de passe que tu as saisis ne correspondent pas." } } } } }, - "version" : "1.0" + "version": "1.0" } \ No newline at end of file diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift index 8b26f1f34c..01db61caa1 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift @@ -19,14 +19,22 @@ let kKeyNotFound = "Key not found" public class StringUtils { let bundle: Bundle - init(bundle: Bundle) { + let languageCode: String? + + init(bundle: Bundle, languageCode: String? = nil) { self.bundle = bundle + self.languageCode = languageCode } public func localizedString(for key: String) -> String { + // If a specific language code is set, load strings from that language bundle + if let languageCode, let path = bundle.path(forResource: languageCode, ofType: "lproj"), let localizedBundle = Bundle(path: path) { + return localizedBundle.localizedString(forKey: key, value: nil, table: "Localizable") + } + + // Use default localization let keyLocale = String.LocalizationValue(key) - let value = String(localized: keyLocale, bundle: bundle) - return value + return String(localized: keyLocale, bundle: bundle) } public func localizedErrorMessage(for error: Error) -> String { @@ -281,6 +289,34 @@ public class StringUtils { return localizedString(for: "Already have an account?") } + /// Google provider + /// found in: + /// - SignInWithGoogleButton + public var googleLoginButtonLabel: String { + return localizedString(for: "Sign in with Google") + } + + /// Apple provider + /// found in: + /// - SignInWithAppleButton + public var appleLoginButtonLabel: String { + return localizedString(for: "Sign in with Apple") + } + + /// Twitter/X provider + /// found in: + /// - SignInWithTwitterButton + public var twitterLoginButtonLabel: String { + return localizedString(for: "Sign in with X") + } + + /// Phone provider + /// found in: + /// - PhoneAuthButtonView + public var phoneLoginButtonLabel: String { + return localizedString(for: "Sign in with Phone") + } + /// Facebook provider /// found in: /// - SignInWithFacebookButton @@ -420,4 +456,491 @@ public class StringUtils { public var alertErrorTitle: String { return localizedString(for: "Error") } + + /// Authenticating overlay message + /// found in: + /// - AuthPickerView + public var authenticatingMessage: String { + return localizedString(for: "Authenticating...") + } + + /// Two-Factor Authentication + /// found in: + /// - MFAEnrolmentView + /// - MFAManagementView + public var twoFactorAuthenticationLabel: String { + return localizedString(for: "Two-Factor Authentication") + } + + /// Set Up Two-Factor Authentication + /// found in: + /// - MFAEnrolmentView + public var setUpTwoFactorAuthenticationLabel: String { + return localizedString(for: "Set Up Two-Factor Authentication") + } + + /// Manage Two-Factor Authentication + /// found in: + /// - MFAManagementView + public var manageTwoFactorAuthenticationLabel: String { + return localizedString(for: "Manage Two-Factor Authentication") + } + + /// Complete Sign-In + /// found in: + /// - MFAResolutionView + public var completeSignInLabel: String { + return localizedString(for: "Complete Sign-In") + } + + /// Complete Setup + /// found in: + /// - MFAEnrolmentView + public var completeSetupLabel: String { + return localizedString(for: "Complete Setup") + } + + /// Choose Authentication Method + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + public var chooseAuthenticationMethodLabel: String { + return localizedString(for: "Choose Authentication Method") + } + + /// SMS Authentication + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + public var smsAuthenticationLabel: String { + return localizedString(for: "SMS Authentication") + } + + /// Authenticator App + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + public var authenticatorAppLabel: String { + return localizedString(for: "Authenticator App") + } + + /// Enter Your Phone Number + /// found in: + /// - MFAEnrolmentView + /// - EnterPhoneNumberView + public var enterYourPhoneNumberLabel: String { + return localizedString(for: "Enter Your Phone Number") + } + + /// Phone Number + /// found in: + /// - MFAEnrolmentView + /// - EnterPhoneNumberView + public var phoneNumberLabel: String { + return localizedString(for: "Phone Number") + } + + /// Send Code + /// found in: + /// - MFAEnrolmentView + /// - EnterPhoneNumberView + public var sendCodeLabel: String { + return localizedString(for: "Send Code") + } + + /// Enter Verification Code + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + /// - EnterVerificationCodeView + public var enterVerificationCodeLabel: String { + return localizedString(for: "Enter Verification Code") + } + + /// Verification Code + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + /// - EnterVerificationCodeView + public var verificationCodeLabel: String { + return localizedString(for: "Verification Code") + } + + /// Scan QR Code + /// found in: + /// - MFAEnrolmentView + public var scanQRCodeLabel: String { + return localizedString(for: "Scan QR Code") + } + + /// Manual Entry Key: + /// found in: + /// - MFAEnrolmentView + public var manualEntryKeyLabel: String { + return localizedString(for: "Manual Entry Key:") + } + + /// Enter 6-digit code + /// found in: + /// - MFAEnrolmentView + public var enterSixDigitCodeLabel: String { + return localizedString(for: "Enter 6-digit code") + } + + /// Scan with your authenticator app or tap to open directly + /// found in: + /// - MFAEnrolmentView + public var scanWithAuthenticatorAppMessage: String { + return localizedString(for: "Scan with your authenticator app or tap to open directly") + } + + /// Tap to open in authenticator app + /// found in: + /// - MFAEnrolmentView + public var tapToOpenInAuthenticatorAppLabel: String { + return localizedString(for: "Tap to open in authenticator app") + } + + /// Use an authenticator app like Google Authenticator or Authy to generate verification codes. + /// found in: + /// - MFAEnrolmentView + public var authenticatorAppInstructionsMessage: String { + return localizedString(for: "Use an authenticator app like Google Authenticator or Authy to generate verification codes.") + } + + /// Set up two-factor authentication to add an extra layer of security to your account. + /// found in: + /// - MFAEnrolmentView + public var setUpTwoFactorAuthMessage: String { + return localizedString(for: "Set up two-factor authentication to add an extra layer of security to your account.") + } + + /// We'll send a verification code to this number + /// found in: + /// - MFAEnrolmentView + public var sendVerificationCodeToNumberMessage: String { + return localizedString(for: "We'll send a verification code to this number") + } + + /// We'll send a verification code to your phone + /// found in: + /// - MFAEnrolmentView + public var sendVerificationCodeToPhoneMessage: String { + return localizedString(for: "We'll send a verification code to your phone") + } + + /// We'll send a verification code to your phone number each time you sign in. + /// found in: + /// - MFAEnrolmentView + public var sendVerificationCodeEachSignInMessage: String { + return localizedString(for: "We'll send a verification code to your phone number each time you sign in.") + } + + /// Unable to generate QR Code + /// found in: + /// - MFAEnrolmentView + public var unableToGenerateQRCodeMessage: String { + return localizedString(for: "Unable to generate QR Code") + } + + /// Copied to clipboard! + /// found in: + /// - MFAEnrolmentView + public var copiedToClipboardMessage: String { + return localizedString(for: "Copied to clipboard!") + } + + /// Multi-Factor Authentication Disabled + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + public var mfaDisabledLabel: String { + return localizedString(for: "Multi-Factor Authentication Disabled") + } + + /// MFA is not enabled in the current configuration. Please contact your administrator. + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + public var mfaNotEnabledMessage: String { + return localizedString(for: "MFA is not enabled in the current configuration. Please contact your administrator.") + } + + /// No Authentication Methods Available + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + public var noAuthenticationMethodsAvailableLabel: String { + return localizedString(for: "No Authentication Methods Available") + } + + /// No MFA methods are configured as allowed. Please contact your administrator. + /// found in: + /// - MFAEnrolmentView + /// - MFAResolutionView + public var noMFAMethodsConfiguredMessage: String { + return localizedString(for: "No MFA methods are configured as allowed. Please contact your administrator.") + } + + /// Complete sign-in with your second factor + /// found in: + /// - MFAResolutionView + public var completeSignInWithSecondFactorMessage: String { + return localizedString(for: "Complete sign-in with your second factor") + } + + /// Choose verification method: + /// found in: + /// - MFAResolutionView + public var chooseVerificationMethodLabel: String { + return localizedString(for: "Choose verification method:") + } + + /// SMS Verification + /// found in: + /// - MFAResolutionView + public var smsVerificationLabel: String { + return localizedString(for: "SMS Verification") + } + + /// We sent a code to %@ + /// found in: + /// - MFAResolutionView + public var sentCodeToNumberMessage: String { + return localizedString(for: "We sent a code to %@") + } + + /// We'll send a code to ••••••%@ + /// found in: + /// - MFAResolutionView + public var sendCodeToMaskedNumberMessage: String { + return localizedString(for: "We'll send a code to ••••••%@") + } + + /// Enter the 6-digit code from your authenticator app + /// found in: + /// - MFAResolutionView + public var enterCodeFromAuthenticatorAppMessage: String { + return localizedString(for: "Enter the 6-digit code from your authenticator app") + } + + /// Resend Code + /// found in: + /// - MFAResolutionView + /// - EnterVerificationCodeView + public var resendCodeLabel: String { + return localizedString(for: "Resend Code") + } + + /// Change number + /// found in: + /// - EnterVerificationCodeView + public var changeNumberLabel: String { + return localizedString(for: "Change number") + } + + /// Manage your authentication methods + /// found in: + /// - MFAManagementView + public var manageAuthenticationMethodsMessage: String { + return localizedString(for: "Manage your authentication methods") + } + + /// Enrolled Methods + /// found in: + /// - MFAManagementView + public var enrolledMethodsLabel: String { + return localizedString(for: "Enrolled Methods") + } + + /// No Authentication Methods + /// found in: + /// - MFAManagementView + public var noAuthenticationMethodsLabel: String { + return localizedString(for: "No Authentication Methods") + } + + /// Add an extra layer of security to your account + /// found in: + /// - MFAManagementView + public var addExtraSecurityLayerMessage: String { + return localizedString(for: "Add an extra layer of security to your account") + } + + /// Add Another Method + /// found in: + /// - MFAManagementView + public var addAnotherMethodLabel: String { + return localizedString(for: "Add Another Method") + } + + /// Get Started + /// found in: + /// - MFAManagementView + public var getStartedLabel: String { + return localizedString(for: "Get Started") + } + + /// Remove + /// found in: + /// - MFAManagementView + public var removeLabel: String { + return localizedString(for: "Remove") + } + + /// Authentication Method + /// found in: + /// - MFAManagementView + public var authenticationMethodLabel: String { + return localizedString(for: "Authentication Method") + } + + /// Enrolled: %@ + /// found in: + /// - MFAManagementView + public var enrolledDateLabel: String { + return localizedString(for: "Enrolled: %@") + } + + /// SMS: %@ + /// found in: + /// - MFAManagementView + public var smsPhoneLabel: String { + return localizedString(for: "SMS: %@") + } + + /// Delete Account + /// found in: + /// - SignedInView + public var deleteAccountLabel: String { + return localizedString(for: "Delete Account") + } + + /// Delete Account? + /// found in: + /// - SignedInView + public var deleteAccountConfirmationLabel: String { + return localizedString(for: "Delete Account?") + } + + /// This action cannot be undone. All your data will be permanently deleted. You may need to reauthenticate to complete this action. + /// found in: + /// - SignedInView + public var deleteAccountWarningMessage: String { + return localizedString(for: "This action cannot be undone. All your data will be permanently deleted. You may need to reauthenticate to complete this action.") + } + + /// Invalid OAuth Provider error + /// found in: + /// - GenericOAuthButton + public var invalidOAuthProviderError: String { + return localizedString(for: "Invalid OAuth Provider") + } + + // MARK: - Field Labels + + /// Email field label + /// found in: + /// - EmailAuthView + public var emailFieldLabel: String { + return localizedString(for: "Email") + } + + /// Password field label + /// found in: + /// - EmailAuthView + public var passwordFieldLabel: String { + return localizedString(for: "Password") + } + + /// Confirm Password field label + /// found in: + /// - EmailAuthView + public var confirmPasswordFieldLabel: String { + return localizedString(for: "Confirm Password") + } + + /// Phone Number field label + /// found in: + /// - MFAEnrolmentView + public var phoneNumberFieldLabel: String { + return localizedString(for: "Phone Number") + } + + /// Display Name field label + /// found in: + /// - MFAEnrolmentView + public var displayNameFieldLabel: String { + return localizedString(for: "Display Name") + } + + /// Verification Code field label + /// found in: + /// - MFAEnrolmentView + public var verificationCodeFieldLabel: String { + return localizedString(for: "Verification Code") + } + + /// Send a password recovery link to your email field label + /// found in: + /// - PasswordRecoveryView + public var passwordRecoveryEmailFieldLabel: String { + return localizedString(for: "Send a password recovery link to your email") + } + + /// Send a sign-in link to your email field label + /// found in: + /// - EmailLinkView + public var signInLinkEmailFieldLabel: String { + return localizedString(for: "Send a sign-in link to your email") + } + + /// Enter phone number prompt + /// found in: + /// - MFAEnrolmentView + public var enterPhoneNumberPrompt: String { + return localizedString(for: "Enter phone number") + } + + /// Enter display name for this device prompt + /// found in: + /// - MFAEnrolmentView + public var enterDisplayNameForDevicePrompt: String { + return localizedString(for: "Enter display name for this device") + } + + /// Enter display name for this authenticator prompt + /// found in: + /// - MFAEnrolmentView + public var enterDisplayNameForAuthenticatorPrompt: String { + return localizedString(for: "Enter display name for this authenticator") + } + + /// Enter code from app prompt + /// found in: + /// - MFAEnrolmentView + public var enterCodeFromAppPrompt: String { + return localizedString(for: "Enter code from app") + } + + /// Phone field label + /// found in: + /// - EnterPhoneNumberView + public var phoneFieldLabel: String { + return localizedString(for: "Phone") + } + + /// We sent a code to number message + /// found in: + /// - EnterVerificationCodeView + public func sentCodeMessage(phoneNumber: String) -> String { + return String(format: localizedString(for: "We sent a code to %@"), phoneNumber) + } + + /// Change number label + /// found in: + /// - EnterVerificationCodeView + public var changeNumberButtonLabel: String { + return localizedString(for: "Change number") + } } diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift index bd310bc0b7..a4876f5b18 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift @@ -22,7 +22,7 @@ public struct AuthPickerView { public init(@ViewBuilder content: @escaping () -> Content = { EmptyView() }) { self.content = content } - + @Environment(AuthService.self) private var authService private let content: () -> Content @@ -58,6 +58,10 @@ extension AuthPickerView: View { MFAManagementView() case AuthView.mfaResolution: MFAResolutionView() + case AuthView.enterPhoneNumber: + EnterPhoneNumberView() + case let .enterVerificationCode(verificationID, fullPhoneNumber): + EnterVerificationCodeView(verificationID: verificationID, fullPhoneNumber: fullPhoneNumber) } } } @@ -119,7 +123,7 @@ extension AuthPickerView: View { } } } - + @ToolbarContentBuilder var toolbar: some ToolbarContent { ToolbarItem(placement: .topBarTrailing) { @@ -128,11 +132,12 @@ extension AuthPickerView: View { authService.isPresented = false } label: { Image(systemName: "xmark") + .foregroundStyle(Color(UIColor.label)) } } } } - + @ViewBuilder var authPickerViewInternal: some View { @Bindable var authService = authService @@ -144,18 +149,31 @@ extension AuthPickerView: View { .safeAreaPadding() } } + .overlay { + if authService.authenticationState == .authenticating { + VStack(spacing: 24) { + ProgressView() + .scaleEffect(1.25) + .tint(.white) + Text("Authenticating...") + .foregroundStyle(.white) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(.black.opacity(0.7)) + } + } .errorAlert( error: authService.currentError, okButtonLabel: authService.string.okButtonLabel ) } - + @ViewBuilder var authMethodPicker: some View { GeometryReader { proxy in ScrollView { VStack(spacing: 24) { - Image(Assets.firebaseAuthLogo) + Image(authService.configuration.logo ?? Assets.firebaseAuthLogo) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 100, height: 100) @@ -169,7 +187,7 @@ extension AuthPickerView: View { } } } - + @ViewBuilder func otherSignInOptions(_ proxy: GeometryProxy) -> some View { VStack { diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift index 58c88e89de..3a3977e2f1 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift @@ -32,15 +32,15 @@ private enum FocusableField: Hashable { @MainActor public struct EmailAuthView { @Environment(AuthService.self) private var authService - + @State private var email = "" @State private var password = "" @State private var confirmPassword = "" - + @FocusState private var focus: FocusableField? - + public init() {} - + private var isValid: Bool { return if authService.authenticationFlow == .signIn { !email.isEmpty && !password.isEmpty @@ -48,11 +48,11 @@ public struct EmailAuthView { !email.isEmpty && !password.isEmpty && password == confirmPassword } } - + private func signInWithEmailPassword() async { try? await authService.signIn(email: email, password: password) } - + private func createUserWithEmailPassword() async { try? await authService.createUser(email: email, password: password) } @@ -63,7 +63,7 @@ extension EmailAuthView: View { VStack(spacing: 16) { AuthTextField( text: $email, - localizedTitle: "Email", + label: authService.string.emailFieldLabel, prompt: authService.string.emailInputLabel, keyboardType: .emailAddress, contentType: .emailAddress, @@ -78,7 +78,7 @@ extension EmailAuthView: View { .accessibilityIdentifier("email-field") AuthTextField( text: $password, - localizedTitle: "Password", + label: authService.string.passwordFieldLabel, prompt: authService.string.passwordInputLabel, contentType: .password, sensitive: true, @@ -101,11 +101,11 @@ extension EmailAuthView: View { } .accessibilityIdentifier("password-recovery-button") } - + if authService.authenticationFlow == .signUp { AuthTextField( text: $confirmPassword, - localizedTitle: "Confirm Password", + label: authService.string.confirmPasswordFieldLabel, prompt: authService.string.confirmPasswordInputLabel, contentType: .password, sensitive: true, @@ -120,7 +120,7 @@ extension EmailAuthView: View { .focused($focus, equals: .confirmPassword) .accessibilityIdentifier("confirm-password-field") } - + Button(action: { Task { if authService.authenticationFlow == .signIn { @@ -133,9 +133,8 @@ extension EmailAuthView: View { if authService.authenticationState != .authenticating { Text( authService.authenticationFlow == .signIn - ? authService.string - .signInWithEmailButtonLabel - : authService.string.signUpWithEmailButtonLabel + ? authService.string.signInWithEmailButtonLabel + : authService.string.signUpWithEmailButtonLabel ) .padding(.vertical, 8) .frame(maxWidth: .infinity) @@ -152,31 +151,31 @@ extension EmailAuthView: View { .buttonStyle(.borderedProminent) .accessibilityIdentifier("sign-in-button") } - HStack { - Text( + Button(action: { + withAnimation { + authService.authenticationFlow = authService - .authenticationFlow == .signIn + .authenticationFlow == .signIn ? .signUp : .signIn + } + }) { + HStack(spacing: 4) { + Text( + authService + .authenticationFlow == .signIn ? authService.string.dontHaveAnAccountYetLabel : authService.string.alreadyHaveAnAccountLabel - ) - Button(action: { - withAnimation { - authService.authenticationFlow = - authService - .authenticationFlow == .signIn ? .signUp : .signIn - } - }) { + ) + .foregroundStyle(Color(.label)) Text( authService.authenticationFlow == .signUp - ? authService.string - .emailLoginFlowLabel - : authService.string.emailSignUpFlowLabel + ? authService.string.emailLoginFlowLabel + : authService.string.emailSignUpFlowLabel ) .fontWeight(.semibold) .foregroundColor(.blue) } - .accessibilityIdentifier("switch-auth-flow") } + .accessibilityIdentifier("switch-auth-flow") } } diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailLinkView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailLinkView.swift index c86c33d64f..14690cda82 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailLinkView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailLinkView.swift @@ -39,7 +39,7 @@ extension EmailLinkView: View { VStack(spacing: 24) { AuthTextField( text: $email, - localizedTitle: "Send a sign-in link to your email", + label: authService.string.signInLinkEmailFieldLabel, prompt: authService.string.emailInputLabel, keyboardType: .emailAddress, contentType: .emailAddress, diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EnterPhoneNumberView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EnterPhoneNumberView.swift new file mode 100644 index 0000000000..0abf0c057a --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EnterPhoneNumberView.swift @@ -0,0 +1,91 @@ +// Copyright 2025 Google LLC +// +// 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. + +import FirebaseAuth +import FirebaseAuthUIComponents +import FirebaseCore +import SwiftUI + +struct EnterPhoneNumberView: View { + @Environment(AuthService.self) private var authService + @State private var phoneNumber: String = "" + @State private var selectedCountry: CountryData = .default + + var body: some View { + VStack(spacing: 16) { + Text(authService.string.enterPhoneNumberPlaceholder) + .font(.subheadline) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.top) + + AuthTextField( + text: $phoneNumber, + label: authService.string.phoneFieldLabel, + prompt: authService.string.enterPhoneNumberPlaceholder, + keyboardType: .phonePad, + contentType: .telephoneNumber, + onChange: { _ in } + ) { + CountrySelector( + selectedCountry: $selectedCountry, + enabled: !(authService.authenticationState == .authenticating) + ) + } + + Button(action: { + Task { + do { + guard let provider = authService.currentPhoneProvider else { + fatalError("No phone provider found") + } + let fullPhoneNumber = selectedCountry.dialCode + phoneNumber + let id = try await provider.verifyPhoneNumber(phoneNumber: fullPhoneNumber) + authService.navigator.push(.enterVerificationCode( + verificationID: id, + fullPhoneNumber: fullPhoneNumber + )) + } catch { + } + } + }) { + if authService.authenticationState == .authenticating { + ProgressView() + .frame(height: 32) + .frame(maxWidth: .infinity) + } else { + Text(authService.string.sendCodeButtonLabel) + .frame(height: 32) + .frame(maxWidth: .infinity) + } + } + .buttonStyle(.borderedProminent) + .disabled(authService.authenticationState == .authenticating || phoneNumber.isEmpty) + .padding(.top, 8) + + Spacer() + } + .navigationTitle(authService.string.phoneSignInTitle) + .padding(.horizontal) + .errorAlert(error: authService.currentError, okButtonLabel: authService.string.okButtonLabel) + } +} + +#Preview { + FirebaseOptions.dummyConfigurationForPreview() + + return EnterPhoneNumberView() + .environment(AuthService()) +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EnterVerificationCodeView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EnterVerificationCodeView.swift new file mode 100644 index 0000000000..484a8c37ed --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EnterVerificationCodeView.swift @@ -0,0 +1,100 @@ +// Copyright 2025 Google LLC +// +// 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. + +import FirebaseAuth +import FirebaseAuthUIComponents +import FirebaseCore +import SwiftUI + +@MainActor +struct EnterVerificationCodeView: View { + @Environment(AuthService.self) private var authService + @State private var verificationCode: String = "" + + let verificationID: String + let fullPhoneNumber: String + + var body: some View { + @Bindable var authService = authService + VStack(spacing: 32) { + VStack(spacing: 16) { + VStack(spacing: 8) { + Text(authService.string.sentCodeMessage(phoneNumber: fullPhoneNumber)) + .font(.subheadline) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity, alignment: .leading) + + Button { + authService.navigator.pop() + } label: { + Text(authService.string.changeNumberButtonLabel) + .font(.caption) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .padding(.bottom) + .frame(maxWidth: .infinity, alignment: .leading) + + VerificationCodeInputField(code: $verificationCode) + + Button(action: { + Task { + do { + guard let provider = authService.currentPhoneProvider else { + fatalError("No phone provider found") + } + let credential = try await provider.createAuthCredential(verificationId: verificationID, verificationCode: verificationCode) + + _ = try await authService.signIn(credentials: credential) + authService.navigator.clear() + } catch { + + } + } + }) { + if authService.authenticationState == .authenticating { + ProgressView() + .frame(height: 32) + .frame(maxWidth: .infinity) + } else { + Text(authService.string.verifyAndSignInButtonLabel) + .frame(height: 32) + .frame(maxWidth: .infinity) + } + } + .buttonStyle(.borderedProminent) + .disabled(authService.authenticationState == .authenticating || verificationCode.count != 6) + } + + Spacer() + } + .navigationTitle(authService.string.enterVerificationCodeTitle) + .navigationBarTitleDisplayMode(.large) + .padding(.horizontal) + .errorAlert(error: authService.currentError, okButtonLabel: authService.string.okButtonLabel) + } +} + +#Preview { + FirebaseOptions.dummyConfigurationForPreview() + + return NavigationStack { + EnterVerificationCodeView( + verificationID: "mock-id", + fullPhoneNumber: "+1 5551234567", + ) + .environment(AuthService()) + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/MFAEnrolmentView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/MFAEnrolmentView.swift index f53da9da72..8f0185b99f 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/MFAEnrolmentView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/MFAEnrolmentView.swift @@ -370,8 +370,8 @@ extension MFAEnrolmentView: View { AuthTextField( text: $phoneNumber, - localizedTitle: "Phone Number", - prompt: "Enter phone number", + label: authService.string.phoneNumberFieldLabel, + prompt: authService.string.enterPhoneNumberPrompt, keyboardType: .phonePad, contentType: .telephoneNumber, onChange: { _ in } @@ -386,8 +386,8 @@ extension MFAEnrolmentView: View { AuthTextField( text: $displayName, - localizedTitle: "Display Name", - prompt: "Enter display name for this device", + label: authService.string.displayNameFieldLabel, + prompt: authService.string.enterDisplayNameForDevicePrompt, leading: { Image(systemName: "person") } @@ -432,7 +432,7 @@ extension MFAEnrolmentView: View { AuthTextField( text: $verificationCode, - localizedTitle: "Verification Code", + label: authService.string.verificationCodeFieldLabel, prompt: "Enter 6-digit code", keyboardType: .numberPad, contentType: .oneTimeCode, @@ -577,8 +577,8 @@ extension MFAEnrolmentView: View { AuthTextField( text: $displayName, - localizedTitle: "Display Name", - prompt: "Enter display name for this authenticator", + label: authService.string.displayNameFieldLabel, + prompt: authService.string.enterDisplayNameForAuthenticatorPrompt, leading: { Image(systemName: "person") } @@ -587,8 +587,8 @@ extension MFAEnrolmentView: View { AuthTextField( text: $totpCode, - localizedTitle: "Verification Code", - prompt: "Enter code from app", + label: authService.string.verificationCodeFieldLabel, + prompt: authService.string.enterCodeFromAppPrompt, keyboardType: .numberPad, contentType: .oneTimeCode, leading: { diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/PasswordRecoveryView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/PasswordRecoveryView.swift index 5c8296ed1a..f14346d4d2 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/PasswordRecoveryView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/PasswordRecoveryView.swift @@ -40,7 +40,7 @@ extension PasswordRecoveryView: View { VStack(spacing: 24) { AuthTextField( text: $email, - localizedTitle: "Send a password recovery link to your email", + label: authService.string.passwordRecoveryEmailFieldLabel, prompt: authService.string.emailInputLabel, keyboardType: .emailAddress, contentType: .emailAddress, diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/UpdatePasswordView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/UpdatePasswordView.swift index b2e7921797..1e54cfe4d6 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/UpdatePasswordView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/UpdatePasswordView.swift @@ -45,7 +45,7 @@ extension UpdatePasswordView: View { VStack(spacing: 24) { AuthTextField( text: $password, - localizedTitle: "Type new password", + label: "Type new password", prompt: authService.string.passwordInputLabel, contentType: .password, sensitive: true, @@ -58,7 +58,7 @@ extension UpdatePasswordView: View { AuthTextField( text: $confirmPassword, - localizedTitle: "Retype new password", + label: "Retype new password", prompt: authService.string.confirmPasswordInputLabel, contentType: .password, sensitive: true, diff --git a/FirebaseSwiftUI/FirebaseAuthUIComponents/Sources/Components/AuthTextField.swift b/FirebaseSwiftUI/FirebaseAuthUIComponents/Sources/Components/AuthTextField.swift index 9c3247c6c7..628dd15462 100644 --- a/FirebaseSwiftUI/FirebaseAuthUIComponents/Sources/Components/AuthTextField.swift +++ b/FirebaseSwiftUI/FirebaseAuthUIComponents/Sources/Components/AuthTextField.swift @@ -31,7 +31,7 @@ public struct AuthTextField: View { @State var obscured: Bool = true @Binding var text: String - let localizedTitle: String + let label: String let prompt: String var textAlignment: TextAlignment = .leading var keyboardType: UIKeyboardType = .default @@ -44,7 +44,7 @@ public struct AuthTextField: View { private let leading: () -> Leading? public init(text: Binding, - localizedTitle: String, + label: String, prompt: String, textAlignment: TextAlignment = .leading, keyboardType: UIKeyboardType = .default, @@ -56,7 +56,7 @@ public struct AuthTextField: View { onChange: ((String) -> Void)? = nil, @ViewBuilder leading: @escaping () -> Leading? = { EmptyView() }) { _text = text - self.localizedTitle = localizedTitle + self.label = label self.prompt = prompt self.textAlignment = textAlignment self.keyboardType = keyboardType @@ -75,17 +75,17 @@ public struct AuthTextField: View { public var body: some View { VStack(alignment: .leading) { - Text(localizedTitle) + Text(LocalizedStringResource(stringLiteral: label)) HStack(spacing: 8) { leading() Group { if isSecureTextField { ZStack(alignment: .trailing) { - SecureField(localizedTitle, text: $text, prompt: Text(prompt)) + SecureField(label, text: $text, prompt: Text(prompt)) .opacity(obscured ? 1 : 0) .focused($isFocused) .frame(height: 24) - TextField(localizedTitle, text: $text, prompt: Text(prompt)) + TextField(label, text: $text, prompt: Text(prompt)) .opacity(obscured ? 0 : 1) .focused($isFocused) .frame(height: 24) @@ -106,7 +106,7 @@ public struct AuthTextField: View { } } else { TextField( - localizedTitle, + label, text: $text, prompt: Text(prompt) ) diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/SignInWithGoogleButton.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/SignInWithGoogleButton.swift index dffb56123d..e967466813 100644 --- a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/SignInWithGoogleButton.swift +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/SignInWithGoogleButton.swift @@ -36,7 +36,7 @@ public struct SignInWithGoogleButton { extension SignInWithGoogleButton: View { public var body: some View { AuthProviderButton( - label: "Sign in with Google", + label: authService.string.googleLoginButtonLabel, style: .google, accessibilityId: "sign-in-with-google-button" ) { diff --git a/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Views/GenericOAuthButton.swift b/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Views/GenericOAuthButton.swift index 6cd3d17ab8..2ad48792b9 100644 --- a/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Views/GenericOAuthButton.swift +++ b/FirebaseSwiftUI/FirebaseOAuthSwiftUI/Sources/Views/GenericOAuthButton.swift @@ -30,7 +30,7 @@ extension GenericOAuthButton: View { public var body: some View { guard let oauthProvider = provider as? OAuthProviderSwift else { return AnyView( - Text("Invalid OAuth Provider") + Text(authService.string.invalidOAuthProviderError) .foregroundColor(.red) ) } diff --git a/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderAuthUI.swift b/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderAuthUI.swift index 2f5f05bd02..c2e3aa2dd3 100644 --- a/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderAuthUI.swift +++ b/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderAuthUI.swift @@ -12,289 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. -import Combine @preconcurrency import FirebaseAuth import FirebaseAuthSwiftUI -import FirebaseAuthUIComponents import SwiftUI -import UIKit public typealias VerificationID = String -// MARK: - Phone Auth Coordinator - -@MainActor -private class PhoneAuthCoordinator: ObservableObject { - @Published var isPresented = true - @Published var currentStep: Step = .enterPhoneNumber - @Published var phoneNumber = "" - @Published var selectedCountry: CountryData = .default - @Published var verificationID = "" - @Published var fullPhoneNumber = "" - @Published var verificationCode = "" - @Published var currentError: AlertError? - @Published var isProcessing = false - - var continuation: CheckedContinuation? - - enum Step { - case enterPhoneNumber - case enterVerificationCode - } - - func sendVerificationCode() async { - isProcessing = true - do { - fullPhoneNumber = selectedCountry.dialCode + phoneNumber - verificationID = try await withCheckedThrowingContinuation { continuation in - PhoneAuthProvider.provider() - .verifyPhoneNumber(fullPhoneNumber, uiDelegate: nil) { verificationID, error in - if let error = error { - continuation.resume(throwing: error) - return - } - continuation.resume(returning: verificationID!) - } - } - currentStep = .enterVerificationCode - currentError = nil - } catch { - currentError = AlertError(message: error.localizedDescription) - } - isProcessing = false - } - - func verifyCodeAndComplete() async { - isProcessing = true - do { - let credential = PhoneAuthProvider.provider() - .credential(withVerificationID: verificationID, verificationCode: verificationCode) - - isPresented = false - continuation?.resume(returning: credential) - continuation = nil - } catch { - currentError = AlertError(message: error.localizedDescription) - isProcessing = false - } - } - - func cancel() { - isPresented = false - - // Only throw error if user has started the flow (sent verification code) - // If they cancel before entering/sending phone number, dismiss silently - if !verificationID.isEmpty { - continuation? - .resume(throwing: AuthServiceError.signInCancelled("Phone authentication was cancelled")) - } else { - continuation?.resume(throwing: CancellationError()) - } - - continuation = nil - } -} - -// MARK: - Phone Auth Flow View - -@MainActor -private struct PhoneAuthFlowView: View { - @StateObject var coordinator: PhoneAuthCoordinator - @Environment(AuthService.self) private var authService - - var body: some View { - NavigationStack { - Group { - switch coordinator.currentStep { - case .enterPhoneNumber: - phoneNumberView - case .enterVerificationCode: - verificationCodeView - } - } - .toolbar { - toolbar - } - } - .interactiveDismissDisabled(authService.configuration.interactiveDismissEnabled) - } - - @ToolbarContentBuilder - var toolbar: some ToolbarContent { - ToolbarItem(placement: .topBarTrailing) { - if !authService.configuration.shouldHideCancelButton { - Button { - coordinator.cancel() - } label: { - Image(systemName: "xmark") - } - } - } - } - - // MARK: - Phone Number View - - var phoneNumberView: some View { - VStack(spacing: 16) { - Text(authService.string.enterPhoneNumberPlaceholder) - .font(.subheadline) - .foregroundStyle(.secondary) - .multilineTextAlignment(.center) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.top) - - AuthTextField( - text: $coordinator.phoneNumber, - localizedTitle: "Phone", - prompt: authService.string.enterPhoneNumberPlaceholder, - keyboardType: .phonePad, - contentType: .telephoneNumber, - onChange: { _ in } - ) { - CountrySelector( - selectedCountry: $coordinator.selectedCountry, - enabled: !coordinator.isProcessing - ) - } - - Button(action: { - Task { - await coordinator.sendVerificationCode() - } - }) { - if coordinator.isProcessing { - ProgressView() - .frame(height: 32) - .frame(maxWidth: .infinity) - } else { - Text(authService.string.sendCodeButtonLabel) - .frame(height: 32) - .frame(maxWidth: .infinity) - } - } - .buttonStyle(.borderedProminent) - .disabled(coordinator.isProcessing || coordinator.phoneNumber.isEmpty) - .padding(.top, 8) - - Spacer() - } - .navigationTitle(authService.string.phoneSignInTitle) - .padding(.horizontal) - .errorAlert(error: $coordinator.currentError, okButtonLabel: authService.string.okButtonLabel) - } - - // MARK: - Verification Code View - - var verificationCodeView: some View { - VStack(spacing: 32) { - VStack(spacing: 16) { - VStack(spacing: 8) { - Text("We sent a code to \(coordinator.fullPhoneNumber)") - .font(.subheadline) - .foregroundStyle(.secondary) - .multilineTextAlignment(.center) - .frame(maxWidth: .infinity, alignment: .leading) - - Button { - coordinator.currentStep = .enterPhoneNumber - coordinator.verificationCode = "" - } label: { - Text("Change number") - .font(.caption) - .frame(maxWidth: .infinity, alignment: .leading) - } - } - .padding(.bottom) - .frame(maxWidth: .infinity, alignment: .leading) - - VerificationCodeInputField( - code: $coordinator.verificationCode, - isError: coordinator.currentError != nil, - errorMessage: coordinator.currentError?.message - ) +public class PhoneProviderSwift: PhoneAuthProviderSwift { + public init() {} - Button(action: { - Task { - await coordinator.verifyCodeAndComplete() - } - }) { - if coordinator.isProcessing { - ProgressView() - .frame(height: 32) - .frame(maxWidth: .infinity) - } else { - Text(authService.string.verifyAndSignInButtonLabel) - .frame(height: 32) - .frame(maxWidth: .infinity) + @MainActor public func verifyPhoneNumber(phoneNumber: String) async throws -> VerificationID { + return try await withCheckedThrowingContinuation { continuation in + PhoneAuthProvider.provider() + .verifyPhoneNumber(phoneNumber, uiDelegate: nil) { verificationID, error in + if let error = error { + continuation.resume(throwing: error) + return } + continuation.resume(returning: verificationID!) } - .buttonStyle(.borderedProminent) - .disabled(coordinator.isProcessing || coordinator.verificationCode.count != 6) - } - - Spacer() } - .navigationTitle(authService.string.enterVerificationCodeTitle) - .navigationBarTitleDisplayMode(.inline) - .padding(.horizontal) - .errorAlert(error: $coordinator.currentError, okButtonLabel: authService.string.okButtonLabel) } -} - -// MARK: - Phone Provider Swift - -public class PhoneProviderSwift: PhoneAuthProviderSwift { - private var cancellables = Set() - - // Internal use only: Injected automatically by AuthService.signIn() - public weak var authService: AuthService? - - public init() {} @MainActor public func createAuthCredential() async throws -> AuthCredential { - guard let authService = authService else { - throw AuthServiceError.providerAuthenticationFailed( - "AuthService not injected. This should be set automatically by AuthService.signIn()." - ) - } - - // Get the root view controller to present from - guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let rootViewController = windowScene.windows.first?.rootViewController else { - throw AuthServiceError.rootViewControllerNotFound( - "Root view controller not available to present phone auth flow" - ) - } - - // Find the topmost view controller - var topViewController = rootViewController - while let presented = topViewController.presentedViewController { - topViewController = presented - } - - // Create coordinator - let coordinator = PhoneAuthCoordinator() - - // Present the flow and wait for result - return try await withCheckedThrowingContinuation { continuation in - coordinator.continuation = continuation - - // Create SwiftUI view with environment - let flowView = PhoneAuthFlowView(coordinator: coordinator) - .environment(authService) - - let hostingController = UIHostingController(rootView: flowView) - - // Dismiss handler - watch for presentation state changes - coordinator.$isPresented.sink { [weak hostingController] isPresented in - if !isPresented { - hostingController?.dismiss(animated: true) - } - }.store(in: &cancellables) - - // Present modally - topViewController.present(hostingController, animated: true) - } + fatalError("Not implemented") + } + + @MainActor public func createAuthCredential(verificationId: String, verificationCode: String) async throws -> AuthCredential { + return PhoneAuthProvider.provider() + .credential(withVerificationID: verificationId, verificationCode: verificationCode) } } diff --git a/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Views/PhoneAuthButtonView.swift b/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Views/PhoneAuthButtonView.swift index 9774a0154e..5f24d7cf35 100644 --- a/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Views/PhoneAuthButtonView.swift +++ b/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Views/PhoneAuthButtonView.swift @@ -30,13 +30,11 @@ public struct PhoneAuthButtonView { extension PhoneAuthButtonView: View { public var body: some View { AuthProviderButton( - label: "Sign in with Phone", + label: authService.string.phoneLoginButtonLabel, style: .phone, accessibilityId: "sign-in-with-phone-button" ) { - Task { - try? await authService.signIn(phoneProvider) - } + authService.navigator.push(.enterPhoneNumber) } } } diff --git a/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Views/SignInWithTwitterButton.swift b/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Views/SignInWithTwitterButton.swift index a874819f02..28d67e9c60 100644 --- a/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Views/SignInWithTwitterButton.swift +++ b/FirebaseSwiftUI/FirebaseTwitterSwiftUI/Sources/Views/SignInWithTwitterButton.swift @@ -29,7 +29,7 @@ public struct SignInWithTwitterButton { extension SignInWithTwitterButton: View { public var body: some View { AuthProviderButton( - label: "Sign in with X", + label: authService.string.twitterLoginButtonLabel, style: .twitter, accessibilityId: "sign-in-with-twitter-button" ) { diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj index 7b274a794d..4e93b41944 100644 --- a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj @@ -462,7 +462,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"FirebaseSwiftUIExample/Preview Content\""; - DEVELOPMENT_TEAM = 3G33A99C47; + DEVELOPMENT_TEAM = YYX2P3XVJ7; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = FirebaseSwiftUIExample/Info.plist; @@ -478,7 +478,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = aob.flutter.plugins.firebase.auth.example; + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebase.auth.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 6.0; @@ -511,7 +511,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = aob.flutter.plugins.firebase.auth.example; + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebase.auth.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 6.0; diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/xcshareddata/xcschemes/FirebaseSwiftUIExample.xcscheme b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/xcshareddata/xcschemes/FirebaseSwiftUIExample.xcscheme index bbe3e1cad5..30faacec20 100644 --- a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/xcshareddata/xcschemes/FirebaseSwiftUIExample.xcscheme +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/xcshareddata/xcschemes/FirebaseSwiftUIExample.xcscheme @@ -31,7 +31,7 @@ shouldAutocreateTestPlan = "YES"> + CFBundleLocalizations + + en + es + de + fr + CFBundleURLTypes