diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a6d5269d33..ca5a5bb3b6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,8 +31,8 @@ jobs: java-version: '21' distribution: 'temurin' - # lintAll gates :proguard-tests, which applies the google-services plugin and - # will not configure without this file. Mirrors what scripts/build.sh copies. + # lintAll gates :app and :proguard-tests, both of which apply the google-services + # plugin and will not configure without this file. Mirrors what build.sh copies. - name: Copy google-services.json run: | cp library/google-services.json app/google-services.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index de3ba2f5ba..1a98334baf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,6 +46,7 @@ This will: - Copy the necessary `google-services.json` files - Download all dependencies - Build all modules +- Run the R8/ProGuard gate - Run checkstyle - Run unit tests @@ -145,6 +146,7 @@ commands can be run locally to highlight any issues before committing your code: This script runs: - `./gradlew clean` - `./gradlew assembleDebug` - Build all modules +- `./gradlew proguard-tests:build` - Check the libraries' consumer ProGuard rules against R8 - `./gradlew checkstyle` - Run code style checks - `./gradlew testDebugUnitTest -x :e2eTest:testDebugUnitTest` - Run unit tests diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c7870c5001..ba8fded8fc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,8 +6,7 @@ plugins { id("org.jetbrains.kotlin.plugin.compose") id("com.google.gms.google-services") id("kotlin-kapt") - // The slot demos host the auth screens on their own Navigation 3 back stacks, and a - // rememberNavBackStack key has to be @Serializable to survive process death. + // Nav3 back-stack keys must be @Serializable to survive process death. alias(libs.plugins.kotlin.serialization) } @@ -39,6 +38,26 @@ android { } } } + + lint { + // Module specific + disable += mutableSetOf( + // Reads the root wrapper, but only the application module analyses it, so it + // belongs here rather than in the shared policy. For reproducible builds. + "AndroidGradlePluginVersion", + // The demos log their auth callbacks unconditionally on purpose — watching + // logcat is how you see one fire. Unlike :auth's, none of these log user data. + "LogConditional", + // Glide's KSP processor does not generate GlideApp, which the storage demo and + // storage/README.md are both written around. Migration tracked separately. + "KaptUsageInsteadOfKsp", + // A themed icon needs a flat silhouette drawn for the purpose. The only + // candidate here is ic_launcher_foreground, whose opaque region is a solid + // plate, so it tints to a featureless block — worse than no monochrome layer. + "MonochromeLauncherIcon" + ) + } + compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -71,6 +90,7 @@ dependencies { implementation(libs.compose.ui.graphics) implementation(libs.compose.ui.tooling.preview) implementation(libs.compose.material3) + implementation(libs.compose.material.icons.extended) // Facebook implementation(libs.facebook.login) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 77abc57765..9e310ab7cc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,6 @@ android:usesCleartextTraffic="true"> @@ -22,7 +21,13 @@ - + + @@ -88,6 +93,12 @@ android:exported="false" android:theme="@style/Theme.FirebaseUIAndroid" /> + + + FullCustomizationDemoActivity::class.java + // Untagged links predate this routing, so they belong to the demo that had them. + else -> HighLevelApiDemoActivity::class.java + } + val demoIntent = Intent(this, target).apply { pendingEmailLink?.let { link -> putExtra(EmailLinkConstants.EXTRA_EMAIL_LINK, link) pendingEmailLink = null @@ -96,7 +104,7 @@ class MainActivity : ComponentActivity() { } if (savedInstanceState == null && !pendingEmailLink.isNullOrEmpty()) { - launchHighLevelDemo() + launchDemoForEmailLink() finish() return } diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/CustomMethodPickerDemoActivity.kt b/app/src/main/java/com/firebaseui/android/demo/auth/CustomMethodPickerDemoActivity.kt index 5228927d9f..dbe3a27271 100644 --- a/app/src/main/java/com/firebaseui/android/demo/auth/CustomMethodPickerDemoActivity.kt +++ b/app/src/main/java/com/firebaseui/android/demo/auth/CustomMethodPickerDemoActivity.kt @@ -142,6 +142,7 @@ class CustomMethodPickerDemoActivity : ComponentActivity() { Log.d("CustomMethodPickerDemo", "Auth cancelled") }, customMethodPickerLayout = { providers, onProviderSelected -> + // Owns the whole screen now, so the terms checkbox is inline here. SpotlightMethodPicker( providers = providers, onProviderSelected = onProviderSelected, @@ -181,6 +182,7 @@ fun SpotlightMethodPicker( val anonymous = groups["anonymous"]?.firstOrNull() LazyColumn( + // Owns the whole screen, so it handles its own insets. modifier = Modifier .fillMaxSize() .safeDrawingPadding(), @@ -298,7 +300,7 @@ fun SpotlightMethodPicker( } @Composable -private fun ProviderIconButton( +fun ProviderIconButton( style: AuthUITheme.ProviderStyle, contentDescription: String, onClick: () -> Unit, @@ -335,12 +337,12 @@ private fun ProviderIconButton( } @Composable -private fun AuthUIAsset.asPainter(): Painter = when (this) { +fun AuthUIAsset.asPainter(): Painter = when (this) { is AuthUIAsset.Resource -> painterResource(resId) is AuthUIAsset.Vector -> rememberVectorPainter(image) } -private fun styleForProvider(provider: AuthProvider): AuthUITheme.ProviderStyle = when (provider) { +fun styleForProvider(provider: AuthProvider): AuthUITheme.ProviderStyle = when (provider) { is AuthProvider.Facebook -> ProviderStyleDefaults.Facebook is AuthProvider.Twitter -> ProviderStyleDefaults.Twitter is AuthProvider.Github -> ProviderStyleDefaults.Github diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/CustomSlotsThemingDemoActivity.kt b/app/src/main/java/com/firebaseui/android/demo/auth/CustomSlotsThemingDemoActivity.kt index df069091f9..d270beac55 100644 --- a/app/src/main/java/com/firebaseui/android/demo/auth/CustomSlotsThemingDemoActivity.kt +++ b/app/src/main/java/com/firebaseui/android/demo/auth/CustomSlotsThemingDemoActivity.kt @@ -22,6 +22,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.firebaseui.android.demo.auth.fullcustomization.FullCustomizationDemoActivity class CustomSlotsThemingDemoActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -46,6 +47,9 @@ class CustomSlotsThemingDemoActivity : ComponentActivity() { }, onCustomMethodPickerClick = { startActivity(Intent(this, CustomMethodPickerDemoActivity::class.java)) + }, + onFullCustomizationClick = { + startActivity(Intent(this, FullCustomizationDemoActivity::class.java)) } ) } @@ -60,6 +64,7 @@ fun CustomSlotsDemoChooser( onPhoneAuthSlotClick: () -> Unit, onShapeCustomizationClick: () -> Unit, onCustomMethodPickerClick: () -> Unit, + onFullCustomizationClick: () -> Unit, ) { Column( modifier = Modifier @@ -106,6 +111,12 @@ fun CustomSlotsDemoChooser( description = "Replace the default provider list with a custom layout, and swap the 'By continuing...' footer with a checkbox using customMethodPickerLayout and customMethodPickerTermsConfiguration on FirebaseAuthScreen.", onClick = onCustomMethodPickerClick ) + + DemoCard( + title = "Full Customization", + description = "customMethodPickerLayout renders as the entire screen, so this layers a full-bleed background image and scrim behind the custom method picker.", + onClick = onFullCustomizationClick + ) } } diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/HighLevelApiDemoActivity.kt b/app/src/main/java/com/firebaseui/android/demo/auth/HighLevelApiDemoActivity.kt index aa8216c53f..589bc54d9e 100644 --- a/app/src/main/java/com/firebaseui/android/demo/auth/HighLevelApiDemoActivity.kt +++ b/app/src/main/java/com/firebaseui/android/demo/auth/HighLevelApiDemoActivity.kt @@ -67,9 +67,15 @@ import com.firebase.ui.auth.util.EmailLinkConstants import com.firebase.ui.auth.util.displayIdentifier import com.firebase.ui.auth.util.getDisplayEmail import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.utils.EMAIL_LINK_ORIGIN_PARAM import com.google.firebase.auth.actionCodeSettings class HighLevelApiDemoActivity : ComponentActivity() { + companion object { + /** Marks this demo's email links so MainActivity can route the return trip back here. */ + const val EMAIL_LINK_ORIGIN = "highlevel" + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -131,7 +137,9 @@ class HighLevelApiDemoActivity : ComponentActivity() { isEmailLinkForceSameDeviceEnabled = false, isEmailLinkSignInEnabled = true, emailLinkActionCodeSettings = actionCodeSettings { - url = "https://flutterfire-e2e-tests.firebaseapp.com" + // This tag is what MainActivity routes the return trip on. + url = "https://flutterfire-e2e-tests.firebaseapp.com" + + "?$EMAIL_LINK_ORIGIN_PARAM=$EMAIL_LINK_ORIGIN" handleCodeInApp = true setAndroidPackageName( "com.firebaseui.android.demo", diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/FullCustomizationDemoActivity.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/FullCustomizationDemoActivity.kt new file mode 100644 index 0000000000..d3cccd059b --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/FullCustomizationDemoActivity.kt @@ -0,0 +1,182 @@ +package com.firebaseui.android.demo.auth.fullcustomization + +import android.os.Bundle +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import com.firebase.ui.auth.AuthException +import com.firebase.ui.auth.FirebaseAuthUI +import com.firebase.ui.auth.configuration.AuthUIConfiguration +import com.firebase.ui.auth.configuration.MfaConfiguration +import com.firebase.ui.auth.configuration.MfaFactor +import com.firebase.ui.auth.configuration.authUIConfiguration +import com.firebase.ui.auth.configuration.auth_provider.AuthProvider +import com.firebase.ui.auth.configuration.theme.AuthUIAsset +import com.firebase.ui.auth.ui.screens.FirebaseAuthScreen +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.utils.EMAIL_LINK_ORIGIN_PARAM +import com.firebaseui.android.demo.auth.fullcustomization.screens.AuthMethodPickerUI +import com.firebaseui.android.demo.auth.fullcustomization.screens.AuthenticatedUI +import com.firebaseui.android.demo.auth.fullcustomization.screens.email.EmailAuthUI +import com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.MfaChallengeUI +import com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.MfaEnrollmentUI +import com.firebaseui.android.demo.auth.fullcustomization.screens.phone.PhoneSignInUI +import com.firebaseui.android.demo.auth.fullcustomization.screens.reauth.ReauthUI +import com.firebaseui.android.demo.auth.fullcustomization.theme.FullCustomizationTheme +import com.firebase.ui.auth.util.EmailLinkConstants +import com.google.firebase.auth.actionCodeSettings + +class FullCustomizationDemoActivity : ComponentActivity() { + companion object { + /** Marks this demo's email links so MainActivity can route the return trip back here. */ + const val EMAIL_LINK_ORIGIN = "fullcustomization" + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + + val authUI = FirebaseAuthUI.getInstance() + // MainActivity owns the deep link and hands it on once it works out which demo sent it. + val emailLink = intent.getStringExtra(EmailLinkConstants.EXTRA_EMAIL_LINK) + val configuration = authUIConfiguration { + context = applicationContext + logo = AuthUIAsset.Resource(R.drawable.firebase_auth) + tosUrl = "https://policies.google.com/terms" + privacyPolicyUrl = "https://policies.google.com/privacy" + providers { + provider( + AuthProvider.Google( + scopes = listOf("email"), + serverClientId = "406099696497-a12gakvts4epfk5pkio7dphc1anjiggc.apps.googleusercontent.com", + ) + ) + provider(AuthProvider.Apple(customParameters = emptyMap(), locale = null)) + provider(AuthProvider.Facebook()) + provider(AuthProvider.Twitter(customParameters = emptyMap())) + provider(AuthProvider.Github(customParameters = emptyMap())) + provider(AuthProvider.Microsoft(tenant = null, customParameters = emptyMap())) + provider(AuthProvider.Yahoo(customParameters = emptyMap())) + provider( + AuthProvider.Email( + isEmailLinkSignInEnabled = true, + emailLinkActionCodeSettings = actionCodeSettings { + // This tag is what MainActivity routes the return trip on. + url = "https://flutterfire-e2e-tests.firebaseapp.com" + + "?$EMAIL_LINK_ORIGIN_PARAM=$EMAIL_LINK_ORIGIN" + handleCodeInApp = true + setAndroidPackageName( + "com.firebaseui.android.demo", + true, + null + ) + }, + passwordValidationRules = emptyList() + ) + ) + provider( + AuthProvider.Phone( + defaultNumber = null, + defaultCountryCode = null, + allowedCountries = null + ) + ) + provider(AuthProvider.Anonymous) + } + } + + setContent { + FullCustomizationTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + FirebaseAuthScreen( + configuration = configuration, + authUI = authUI, + emailLink = emailLink, + onSignInSuccess = { result -> + Log.d("FullCustomizationDemo", "Auth success: ${result.user?.uid}") + }, + onSignInFailure = { exception: AuthException -> + Log.e("FullCustomizationDemo", "Auth failed", exception) + }, + onSignInCancelled = { + Log.d("FullCustomizationDemo", "Auth cancelled") + }, + mfaConfiguration = MfaConfiguration( + allowedFactors = listOf(MfaFactor.Sms, MfaFactor.Totp), + requireEnrollment = false, + ), + customMethodPickerLayout = { providers, onProviderSelected -> + MainUI( + authUI = authUI, + configuration = configuration, + providers = providers, + onProviderSelected = onProviderSelected, + ) + }, + // Covers the email flows the library navigates to itself. + emailContent = { state -> EmailAuthUI(state) }, + phoneContent = { state -> PhoneSignInUI(state) }, + mfaEnrollmentContent = { state -> MfaEnrollmentUI(state) }, + mfaChallengeContent = { state -> MfaChallengeUI(state) }, + reauthContent = { state -> ReauthUI(state) }, + authenticatedContent = { state, uiContext -> + AuthenticatedUI(state = state, uiContext = uiContext) + }, + ) + } + } + } + } +} + +@Composable +private fun MainUI( + authUI: FirebaseAuthUI, + configuration: AuthUIConfiguration, + providers: List, + onProviderSelected: (AuthProvider) -> Unit, +) { + val context = LocalContext.current + Box(modifier = Modifier.fillMaxSize()) { + Image( + painter = painterResource(id = R.drawable.custom_background), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + Column(modifier = Modifier.fillMaxSize()) { + // AuthMethodPickerUI builds its own EmailAuthScreen per step, so none is needed here. + AuthMethodPickerUI( + context = context, + configuration = configuration, + authUI = authUI, + otherProviders = providers.filterNot { it is AuthProvider.Email }, + onProviderSelected = onProviderSelected, + onSuccess = { result -> + Log.d("FullCustomizationDemo", "Auth success: ${result.user?.uid}") + }, + onError = { exception -> + Log.e("FullCustomizationDemo", "Auth failed", exception) + }, + onCancel = { + Log.d("FullCustomizationDemo", "Auth cancelled") + }, + ) + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/AuthPage.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/AuthPage.kt new file mode 100644 index 0000000000..8bb588dfdb --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/AuthPage.kt @@ -0,0 +1,114 @@ +package com.firebaseui.android.demo.auth.fullcustomization.common + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import com.firebaseui.android.demo.R + +/** + * The page frame shared by the MFA and reauthentication screens: mascot, headline, a single + * elevated card, and bottom-anchored actions. + * + * The email and phone steps predate this and inline the same structure themselves. + * + * verticalScroll measures content with infinite max height, and Column distributes weights + * against the MIN height when max is infinite (RowColumnMeasurePolicy.kt) — so + * heightIn(min = viewport) makes the weighted spacers expand (centering content, anchoring the + * actions to the bottom) when everything fits, and collapse to zero (plain scrolling) when it + * doesn't. + */ +@Composable +fun AuthPage( + @DrawableRes mascot: Int, + mascotDescription: String, + title: String, + cardContentDescription: String, + actions: @Composable ColumnScope.() -> Unit, + card: @Composable ColumnScope.() -> Unit, +) { + Box(modifier = Modifier.fillMaxSize()) { + // Outside safeDrawingPadding so it runs edge to edge under the system bars. + Image( + painter = painterResource(id = R.drawable.custom_background), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize(), + ) + + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + .safeDrawingPadding(), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .heightIn(min = maxHeight) + .padding(horizontal = 40.dp, vertical = 24.dp), + ) { + Spacer(modifier = Modifier.weight(1f)) + + Column(modifier = Modifier.fillMaxWidth()) { + Image( + painter = painterResource(id = mascot), + contentDescription = mascotDescription, + modifier = Modifier.size(72.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = title, + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(24.dp)) + + HardOffsetShadow(shape = AuthFieldShape, modifier = Modifier.fillMaxWidth()) { + Surface( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = cardContentDescription }, + color = MaterialTheme.colorScheme.surface, + shape = AuthFieldShape, + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + content = card, + ) + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.height(24.dp)) + + Column(modifier = Modifier.fillMaxWidth(), content = actions) + } + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/AuthTextFieldStyle.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/AuthTextFieldStyle.kt new file mode 100644 index 0000000000..ca66ee7bc2 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/AuthTextFieldStyle.kt @@ -0,0 +1,75 @@ +package com.firebaseui.android.demo.auth.fullcustomization.common + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import com.firebaseui.android.demo.R + +val AuthFieldShape = RoundedCornerShape(24.dp) + +@Composable +fun authTextFieldColors(): TextFieldColors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = Color.White, + focusedContainerColor = Color.White, + disabledContainerColor = Color.White, + unfocusedBorderColor = MaterialTheme.colorScheme.outlineVariant, + focusedBorderColor = MaterialTheme.colorScheme.secondary, +) + +@Composable +fun FullCustomizationTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + label: String? = null, + placeholder: String? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + enabled: Boolean = true, + isError: Boolean = false, + supportingText: String? = null, + singleLine: Boolean = true, + visualTransformation: VisualTransformation = VisualTransformation.None, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + shape: Shape = AuthFieldShape, +) { + OutlinedTextField( + value = value, + onValueChange = onValueChange, + modifier = modifier, + label = label?.let { { Text(it) } }, + placeholder = placeholder?.let { { Text(it) } }, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + enabled = enabled, + isError = isError, + supportingText = supportingText?.let { { Text(it) } }, + singleLine = singleLine, + visualTransformation = visualTransformation, + keyboardOptions = keyboardOptions, + shape = shape, + colors = authTextFieldColors(), + ) +} + +@Composable +fun EmailFieldIcon() { + Image( + painter = painterResource(R.drawable.email_at_sign), + contentDescription = null, + modifier = Modifier.size(24.dp), + ) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/CtaButton.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/CtaButton.kt new file mode 100644 index 0000000000..edb251c936 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/CtaButton.kt @@ -0,0 +1,55 @@ +package com.firebaseui.android.demo.auth.fullcustomization.common + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.firebaseui.android.demo.auth.fullcustomization.theme.ButtonShape + +private val CtaShadowColor = Color(0xFF5D0B47) + +@Composable +fun CtaButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + isLoading: Boolean = false, + colors: ButtonColors = ButtonDefaults.buttonColors(), +) { + HardOffsetShadow( + shape = ButtonShape, + offsetX = 2.dp, + offsetY = 4.dp, + color = if (enabled) CtaShadowColor else Color.Transparent, + modifier = modifier.fillMaxWidth(), + ) { + Button( + onClick = onClick, + enabled = enabled, + shape = ButtonShape, + colors = colors, + contentPadding = PaddingValues(horizontal = 24.dp, vertical = 10.dp), + modifier = Modifier + .fillMaxWidth() + .height(80.dp), + ) { + if (isLoading) { + // M3 default (primary) reads on the disabled fill; LocalContentColor would wash it out. + CircularProgressIndicator(modifier = Modifier.size(20.dp)) + } else { + Text(text = text, style = MaterialTheme.typography.titleMedium) + } + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/HardOffsetShadow.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/HardOffsetShadow.kt new file mode 100644 index 0000000000..628bffafd2 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/HardOffsetShadow.kt @@ -0,0 +1,32 @@ +package com.firebaseui.android.demo.auth.fullcustomization.common + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.offset +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +@Composable +fun HardOffsetShadow( + shape: Shape, + modifier: Modifier = Modifier, + offsetX: Dp = 3.dp, + offsetY: Dp = 6.dp, + color: Color = MaterialTheme.colorScheme.primaryContainer, + content: @Composable () -> Unit, +) { + Box(modifier = modifier) { + Box( + modifier = Modifier + .matchParentSize() + .offset(x = offsetX, y = offsetY) + .background(color = color, shape = shape), + ) + content() + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/OtherSignInMethodsSheet.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/OtherSignInMethodsSheet.kt new file mode 100644 index 0000000000..c5919e1699 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/OtherSignInMethodsSheet.kt @@ -0,0 +1,74 @@ +package com.firebaseui.android.demo.auth.fullcustomization.common + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.configuration.auth_provider.AuthProvider +import com.firebase.ui.auth.ui.components.TermsAndPrivacyForm + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun OtherSignInMethodsSheet( + otherProviders: List, + onProviderSelected: (AuthProvider) -> Unit, + onDismissRequest: () -> Unit, + tosUrl: String?, + ppUrl: String?, +) { + ModalBottomSheet( + onDismissRequest = onDismissRequest, + containerColor = MaterialTheme.colorScheme.primaryContainer, + ) { + // Scrollable: nine providers plus the ToS footer overflow a sheet on shorter screens. + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(horizontal = 64.dp), + ) { + Text( + text = "Other sign in methods", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp) + .semantics { contentDescription = "Other sign-in methods sheet title" }, + ) + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier.fillMaxWidth(), + ) { + otherProviders.forEach { provider -> + SheetProviderButton( + provider = provider, + onClick = { + onDismissRequest() + onProviderSelected(provider) + }, + modifier = Modifier.fillMaxWidth(), + ) + } + } + Spacer(modifier = Modifier.height(16.dp)) + TermsAndPrivacyForm(tosUrl = tosUrl, ppUrl = ppUrl) + Spacer(modifier = Modifier.height(24.dp)) + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/SheetProviderButton.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/SheetProviderButton.kt new file mode 100644 index 0000000000..905774fe0e --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/common/SheetProviderButton.kt @@ -0,0 +1,123 @@ +package com.firebaseui.android.demo.auth.fullcustomization.common + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.configuration.auth_provider.AuthProvider +import com.firebase.ui.auth.configuration.theme.AuthUIAsset +import com.firebase.ui.auth.configuration.theme.ProviderStyleDefaults +import com.firebaseui.android.demo.auth.fullcustomization.theme.ProviderButtonShape + +@Composable +fun SheetProviderButton( + provider: AuthProvider, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val label = providerSheetLabel(provider) + val style = when (provider) { + is AuthProvider.Google -> ProviderStyleDefaults.Google + is AuthProvider.Facebook -> ProviderStyleDefaults.Facebook + is AuthProvider.Twitter -> ProviderStyleDefaults.Twitter + is AuthProvider.Github -> ProviderStyleDefaults.Github + is AuthProvider.Microsoft -> ProviderStyleDefaults.Microsoft + is AuthProvider.Yahoo -> ProviderStyleDefaults.Yahoo + is AuthProvider.Apple -> ProviderStyleDefaults.Apple + is AuthProvider.Anonymous -> ProviderStyleDefaults.Anonymous + else -> ProviderStyleDefaults.Email + } + val backgroundColor = if (provider is AuthProvider.Phone) { + MaterialTheme.colorScheme.primary + } else { + style.backgroundColor + } + val contentColor = if (provider is AuthProvider.Google) Color.Black else style.contentColor + val hasWhiteBackground = backgroundColor == Color.White + + Button( + onClick = onClick, + shape = ProviderButtonShape, + colors = ButtonDefaults.buttonColors( + containerColor = backgroundColor, + contentColor = contentColor, + ), + border = if (hasWhiteBackground) BorderStroke(1.dp, Color.Black) else null, + contentPadding = PaddingValues(horizontal = 36.dp, vertical = 12.dp), + modifier = modifier, + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + ) { + if (provider is AuthProvider.Phone) { + Icon( + imageVector = Icons.Default.Phone, + contentDescription = null, + modifier = Modifier.size(20.dp), + ) + } else { + style.icon?.let { icon -> + Image( + painter = icon.asPainter(), + contentDescription = null, + modifier = Modifier.size(20.dp), + ) + } + } + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = label, + modifier = Modifier + .weight(1f) + .padding(end = 8.dp), + maxLines = 1, + overflow = TextOverflow.MiddleEllipsis, + style = MaterialTheme.typography.labelLarge, + ) + } + } +} + +private fun providerSheetLabel(provider: AuthProvider): String = when (provider) { + is AuthProvider.Google -> "Sign in with Google" + is AuthProvider.Facebook -> "Sign in with Facebook" + is AuthProvider.Twitter -> "Sign in with X" + is AuthProvider.Github -> "Sign in with GitHub" + is AuthProvider.Microsoft -> "Sign in with Microsoft" + is AuthProvider.Yahoo -> "Sign in with Yahoo" + is AuthProvider.Apple -> "Sign in with Apple" + is AuthProvider.Phone -> "Sign in with phone" + is AuthProvider.Anonymous -> "Continue as guest" + // Email only reaches this button during reauthentication; the sign-in sheet filters it out. + is AuthProvider.Email -> "Continue with email" + else -> "Continue" +} + +@Composable +private fun AuthUIAsset.asPainter() = when (this) { + is AuthUIAsset.Resource -> painterResource(resId) + is AuthUIAsset.Vector -> rememberVectorPainter(image) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/AuthMethodPickerUI.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/AuthMethodPickerUI.kt new file mode 100644 index 0000000000..9592a31203 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/AuthMethodPickerUI.kt @@ -0,0 +1,207 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens + +import android.content.Context +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.ContentTransform +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.dropUnlessResumed +import androidx.navigation3.runtime.NavKey +import androidx.navigation3.runtime.entryProvider +import androidx.navigation3.runtime.rememberNavBackStack +import androidx.navigation3.scene.Scene +import androidx.navigation3.ui.NavDisplay +import com.firebase.ui.auth.AuthException +import com.firebase.ui.auth.FirebaseAuthUI +import com.firebase.ui.auth.configuration.AuthUIConfiguration +import com.firebase.ui.auth.configuration.auth_provider.AuthProvider +import com.firebase.ui.auth.ui.screens.AuthRoute +import com.firebase.ui.auth.ui.screens.email.EmailAuthMode +import com.firebase.ui.auth.ui.screens.email.EmailAuthScreen +import com.firebaseui.android.demo.auth.fullcustomization.common.OtherSignInMethodsSheet +import com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages.EmailEntryStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages.LoginStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages.SignUpStep +import com.google.firebase.auth.AuthResult +import kotlinx.serialization.Serializable + +/** + * The demo's own pre-step, entered before any [AuthRoute.Email.Step]: type an address, then choose + * to sign in or create an account. Not part of the library's [AuthRoute] — that sealed hierarchy is + * closed outside the auth module — so this is a plain [NavKey] of the demo's own, sharing the same + * back stack as the library's per-mode destinations. + */ +@Serializable +private data object EmailEntryKey : NavKey + +private fun AuthRoute.Email.Step.toMode(): EmailAuthMode = when (this) { + is AuthRoute.Email.SignIn -> EmailAuthMode.SignIn + is AuthRoute.Email.SignUp -> EmailAuthMode.SignUp + is AuthRoute.Email.ResetPassword -> EmailAuthMode.ResetPassword + is AuthRoute.Email.EmailLinkSignIn -> EmailAuthMode.EmailLinkSignIn +} + +private fun stepFor(mode: EmailAuthMode, email: String?): AuthRoute.Email.Step = when (mode) { + EmailAuthMode.SignIn -> AuthRoute.Email.SignIn(email) + EmailAuthMode.SignUp -> AuthRoute.Email.SignUp(email) + EmailAuthMode.ResetPassword -> AuthRoute.Email.ResetPassword(email) + EmailAuthMode.EmailLinkSignIn -> AuthRoute.Email.EmailLinkSignIn(email) +} + +/** + * Navigates to [target], replacing any existing entry of the same step *type* rather than stacking + * a duplicate — matching [com.firebase.ui.auth.ui.screens.email.navigateToEmailStep], which the + * library keeps `internal` to its own module. Adds before removing, so no single write leaves the + * stack without the chooser at its base. + */ +private fun MutableList.navigateToStep(target: AuthRoute.Email.Step) { + val existing = indexOfFirst { it is AuthRoute.Email.Step && it::class == target::class } + add(target) + if (existing >= 0) { + while (size > existing + 1) removeAt(existing) + } +} + +/** Matches the 700ms cross-fade [FirebaseAuthScreen][com.firebase.ui.auth.ui.screens.FirebaseAuthScreen] + * itself falls back to, so a step switch here looks the same as one at the top level. */ +private val EmailStepTransform: AnimatedContentTransitionScope>.() -> ContentTransform = { + fadeIn(animationSpec = tween(700)) togetherWith fadeOut(animationSpec = tween(700)) +} + +/** + * [NavDisplay]'s predictive-back default scales the outgoing step down to 70% while the incoming one + * springs in at full size, so a swipe back drew both steps superimposed at different scales. Reuse + * the cross-fade above so a gesture back looks like a tapped one. + */ +private val EmailStepPredictivePopTransform: + AnimatedContentTransitionScope>.(Int) -> ContentTransform = + { EmailStepTransform(this) } + +/** + * Custom UI for `customMethodPickerLayout`'s email path. + * + * Hosts its own [NavDisplay] over [AuthRoute.Email]'s public per-mode destinations, the same + * mechanism [com.firebase.ui.auth.ui.screens.FirebaseAuthScreen] uses for its own hosted + * destinations — so switching between sign-in, sign-up, password reset and email-link sign-in + * animates, gets a real back-stack entry, and never loses the address the user already typed, + * because the address travels as a field on the step's own key rather than in state a switch could + * clear. + */ +@Composable +fun AuthMethodPickerUI( + context: Context, + configuration: AuthUIConfiguration, + authUI: FirebaseAuthUI, + otherProviders: List, + onProviderSelected: (AuthProvider) -> Unit, + onSuccess: (AuthResult) -> Unit, + onError: (AuthException) -> Unit, + onCancel: () -> Unit, +) { + var showOtherMethods by remember { mutableStateOf(false) } + val backStack = rememberNavBackStack(EmailEntryKey) + + Box(modifier = Modifier.fillMaxSize()) { + NavDisplay( + backStack = backStack, + transitionSpec = EmailStepTransform, + popTransitionSpec = EmailStepTransform, + predictivePopTransitionSpec = EmailStepPredictivePopTransform, + entryProvider = entryProvider { + entry { + // No auth operation here, so there is nothing for EmailAuthContentState to own. + var email by rememberSaveable { mutableStateOf("") } + EmailEntryStep( + email = email, + onEmailChange = { email = it }, + isLoading = false, + onSignIn = dropUnlessResumed { + backStack.navigateToStep(AuthRoute.Email.SignIn(email)) + }, + onCreateAccount = dropUnlessResumed { + backStack.navigateToStep(AuthRoute.Email.SignUp(email)) + }, + onShowOtherMethods = { showOtherMethods = true }, + ) + } + + entry { step -> + EmailStep(step, backStack, context, configuration, authUI, onSuccess, onError, onCancel) + } + entry { step -> + EmailStep(step, backStack, context, configuration, authUI, onSuccess, onError, onCancel) + } + entry { step -> + EmailStep(step, backStack, context, configuration, authUI, onSuccess, onError, onCancel) + } + entry { step -> + EmailStep(step, backStack, context, configuration, authUI, onSuccess, onError, onCancel) + } + }, + ) + } + + if (showOtherMethods) { + OtherSignInMethodsSheet( + otherProviders = otherProviders, + onProviderSelected = onProviderSelected, + onDismissRequest = { showOtherMethods = false }, + tosUrl = configuration.tosUrl, + ppUrl = configuration.privacyPolicyUrl, + ) + } +} + +/** + * One [AuthRoute.Email.Step] destination: a single [EmailAuthScreen] instance pinned to [step]'s + * mode, seeded with the address [step] carries. [EmailAuthScreen.onNavigateToMode] is what makes + * a mode switch push (or replace) an entry on [backStack] instead of mutating local state. + */ +@Composable +private fun EmailStep( + step: AuthRoute.Email.Step, + backStack: MutableList, + context: Context, + configuration: AuthUIConfiguration, + authUI: FirebaseAuthUI, + onSuccess: (AuthResult) -> Unit, + onError: (AuthException) -> Unit, + onCancel: () -> Unit, +) { + // Resets rather than pops: from ResetPassword the stack is [chooser, SignIn, ResetPassword]. + val onUseDifferentEmail: () -> Unit = dropUnlessResumed { + backStack.clear() + backStack.add(EmailEntryKey) + } + + EmailAuthScreen( + context = context, + configuration = configuration, + authUI = authUI, + prefillEmail = step.email, + mode = step.toMode(), + onNavigateToMode = { mode, email -> backStack.navigateToStep(stepFor(mode, email)) }, + onSuccess = onSuccess, + onError = onError, + onCancel = onCancel, + ) { state -> + when (state.mode) { + EmailAuthMode.SignUp -> SignUpStep(state, onUseDifferentEmail) + // Reset-password and email-link are inline on the login form, so every mode has a screen. + EmailAuthMode.SignIn, + EmailAuthMode.ResetPassword, + EmailAuthMode.EmailLinkSignIn -> LoginStep(state, onUseDifferentEmail) + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/AuthenticatedUI.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/AuthenticatedUI.kt new file mode 100644 index 0000000000..8fc00b68c3 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/AuthenticatedUI.kt @@ -0,0 +1,287 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens + +import android.util.Log +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.lifecycleScope +import com.firebase.ui.auth.AuthException +import com.firebase.ui.auth.AuthState +import com.firebase.ui.auth.ui.screens.AuthSuccessUiContext +import com.firebase.ui.auth.util.displayIdentifier +import com.firebase.ui.auth.util.getDisplayEmail +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthPage +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.firebaseui.android.demo.auth.fullcustomization.common.FullCustomizationTextField +import kotlinx.coroutines.launch +import kotlinx.coroutines.tasks.await + +private const val TAG = "FullCustomizationDemo" + +/** + * Custom UI for `FirebaseAuthScreen.authenticatedContent`. + * + * Its main job in this demo is making the other slots reachable: the two-factor button navigates to + * the flow that `mfaEnrollmentContent` renders, and changing the password is a sensitive operation, + * so wrapping it in [com.firebase.ui.auth.FirebaseAuthUI.withReauth] is what provokes + * `reauthContent`. + * + * This slot also receives the email-verification and profile-completion states, which the library + * would otherwise render itself — so they are handled here too rather than falling through to a + * blank screen. + */ +@Composable +fun AuthenticatedUI(state: AuthState, uiContext: AuthSuccessUiContext) { + when (state) { + is AuthState.RequiresEmailVerification -> VerifyEmailPage(uiContext) + is AuthState.RequiresProfileCompletion -> ProfileCompletionPage(state, uiContext) + else -> SignedInPage(uiContext) + } +} + +@Composable +private fun SignedInPage(uiContext: AuthSuccessUiContext) { + val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current + val authUI = uiContext.authUI + // Not remembered: the identifier changes across sign-out and reauth. + val identifier = authUI.getCurrentUser().displayIdentifier() + + // Refreshes the cached factor list on return from MFA; keyed on Unit so it cannot loop. + LaunchedEffect(Unit) { uiContext.onReloadUser() } + val enrolledFactors = authUI.getCurrentUser()?.multiFactor?.enrolledFactors.orEmpty() + + var newPassword by remember { mutableStateOf("") } + var passwordVisible by remember { mutableStateOf(false) } + var isUpdating by remember { mutableStateOf(false) } + var statusMessage by remember { mutableStateOf(null) } + var isError by remember { mutableStateOf(false) } + + AuthPage( + mascot = R.drawable.full_customization_mascot, + mascotDescription = "doggo - cute welcome mascot", + title = "You're in", + cardContentDescription = "authenticated - account card", + card = { + Text( + text = if (identifier.isNotBlank()) "Signed in as $identifier" else "Signed in", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, + ) + + Text( + text = "Changing your password needs a recent sign-in, so it triggers the custom " + + "reauth screen.", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + FullCustomizationTextField( + value = newPassword, + onValueChange = { + newPassword = it + statusMessage = null + }, + label = "New password", + enabled = !isUpdating, + isError = isError, + supportingText = statusMessage, + visualTransformation = if (passwordVisible) { + VisualTransformation.None + } else { + PasswordVisualTransformation() + }, + trailingIcon = { + IconButton(onClick = { passwordVisible = !passwordVisible }) { + Icon( + imageVector = if (passwordVisible) { + Icons.Default.VisibilityOff + } else { + Icons.Default.Visibility + }, + contentDescription = null, + ) + } + }, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - new password secure input" }, + ) + }, + actions = { + CtaButton( + text = "Change password", + onClick = { + // lifecycleScope: the reauth overlay replaces this screen; the retry outlives it. + lifecycleOwner.lifecycleScope.launch { + isUpdating = true + statusMessage = null + isError = false + try { + authUI.withReauth( + context, + reason = "Verify your identity to change your password", + ) { + authUI.getCurrentUser()?.updatePassword(newPassword)?.await() + Log.d(TAG, "Password changed successfully") + } + } catch (e: AuthException.AuthCancelledException) { + // Declined, not failed: the password is unchanged, so report neither. + Log.d(TAG, "Reauthentication declined", e) + } catch (e: Exception) { + Log.e(TAG, "Password change failed", e) + isError = true + statusMessage = "Couldn't change the password. Try again." + } finally { + isUpdating = false + } + } + }, + enabled = newPassword.length >= 6 && !isUpdating, + isLoading = isUpdating, + modifier = Modifier.semantics { contentDescription = "button - change password" }, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + CtaButton( + // Relabelled, not disabled: SelectFactorStep is the only place a factor can be removed. + text = if (enrolledFactors.isEmpty()) "Set up two-factor" else "Manage two-factor", + onClick = uiContext.onManageMfa, + enabled = !isUpdating, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + ), + modifier = Modifier.semantics { contentDescription = "button - manage mfa" }, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = uiContext.onSignOut, + enabled = !isUpdating, + modifier = Modifier.fillMaxWidth(), + ) { + Text(uiContext.stringProvider.signOutAction) + } + }, + ) +} + +@Composable +private fun VerifyEmailPage(uiContext: AuthSuccessUiContext) { + val stringProvider = uiContext.stringProvider + val user = uiContext.authUI.getCurrentUser() + val emailLabel = user.getDisplayEmail(stringProvider.emailProvider) + + AuthPage( + mascot = R.drawable.full_customization_mascot, + mascotDescription = "doggo - cute welcome mascot", + title = "Check your inbox", + cardContentDescription = "authenticated - verify email card", + card = { + Text( + text = stringProvider.verifyEmailInstruction(emailLabel), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + actions = { + CtaButton( + text = stringProvider.verifiedEmailAction, + onClick = uiContext.onReloadUser, + modifier = Modifier.semantics { + contentDescription = "button - recheck email verification" + }, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + CtaButton( + text = stringProvider.resendVerificationEmailAction, + onClick = { user?.sendEmailVerification() }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + ), + modifier = Modifier.semantics { + contentDescription = "button - resend verification email" + }, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = uiContext.onSignOut, + modifier = Modifier.fillMaxWidth(), + ) { + Text(stringProvider.signOutAction) + } + }, + ) +} + +@Composable +private fun ProfileCompletionPage( + state: AuthState.RequiresProfileCompletion, + uiContext: AuthSuccessUiContext, +) { + val stringProvider = uiContext.stringProvider + + AuthPage( + mascot = R.drawable.full_customization_mascot, + mascotDescription = "doggo - cute welcome mascot", + title = "Almost there", + cardContentDescription = "authenticated - profile completion card", + card = { + Text( + text = stringProvider.profileCompletionMessage, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + if (state.missingFields.isNotEmpty()) { + Text( + text = stringProvider.profileMissingFieldsMessage( + state.missingFields.joinToString() + ), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.error, + ) + } + }, + actions = { + TextButton( + onClick = uiContext.onSignOut, + modifier = Modifier.fillMaxWidth(), + ) { + Text(stringProvider.signOutAction) + } + }, + ) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/EmailAuthUI.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/EmailAuthUI.kt new file mode 100644 index 0000000000..06bcecbbe4 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/EmailAuthUI.kt @@ -0,0 +1,83 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.email + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import com.firebase.ui.auth.ui.screens.email.EmailAuthContentState +import com.firebase.ui.auth.ui.screens.email.EmailAuthMode +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages.EmailEntryStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages.LoginStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages.SignUpStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.reauth.ReauthEmailStep + +/** + * Custom UI for `FirebaseAuthScreen.emailContent`. + * + * The method picker hosts its own email entry, so this slot only renders for email flows the + * *library* navigates to: reauthentication, account linking, and email-already-in-use recovery. + * Without it those flows fall back to the library's stock email screen, which is jarring inside a + * demo whose whole premise is that nothing looks stock. + * + * An address supplied by the library (as reauthentication does) skips the choice entirely — the + * caller already knows who is signing in. + */ +@Composable +fun EmailAuthUI(state: EmailAuthContentState) { + // A fixed address leaves nothing to choose, so render the compact confirm form. + if (state.isEmailLocked) { + ReauthEmailStep(state) + return + } + + var chosen by rememberSaveable { mutableStateOf(state.email.isNotBlank()) } + + val onUseDifferentEmail: () -> Unit = { + state.onPasswordChange("") + state.onConfirmPasswordChange("") + chosen = false + } + + Box(modifier = Modifier.fillMaxSize()) { + // The email pages don't paint their own background, so this slot must. + Image( + painter = painterResource(id = R.drawable.custom_background), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize(), + ) + + if (!chosen) { + EmailEntryStep( + email = state.email, + onEmailChange = state.onEmailChange, + isLoading = state.isLoading, + onSignIn = { + state.onGoToSignIn() + chosen = true + }, + onCreateAccount = { + state.onGoToSignUp() + chosen = true + }, + // No provider sheet in this slot — the caller already committed to email. + onShowOtherMethods = {}, + ) + } else { + when (state.mode) { + EmailAuthMode.SignUp -> SignUpStep(state, onUseDifferentEmail) + EmailAuthMode.SignIn, + EmailAuthMode.ResetPassword, + EmailAuthMode.EmailLinkSignIn -> LoginStep(state, onUseDifferentEmail) + } + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/EmailEntryStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/EmailEntryStep.kt new file mode 100644 index 0000000000..4b8945ffb8 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/EmailEntryStep.kt @@ -0,0 +1,197 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.Login +import androidx.compose.material3.Icon +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthFieldShape +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.firebaseui.android.demo.auth.fullcustomization.common.EmailFieldIcon +import com.firebaseui.android.demo.auth.fullcustomization.common.FullCustomizationTextField +import com.firebaseui.android.demo.auth.fullcustomization.common.HardOffsetShadow +import com.firebaseui.android.demo.auth.fullcustomization.theme.IntroShape + +@Composable +fun EmailEntryStep( + email: String, + onEmailChange: (String) -> Unit, + isLoading: Boolean, + onSignIn: () -> Unit, + onCreateAccount: () -> Unit, + onShowOtherMethods: () -> Unit, +) { + val isEmailValid = remember(email) { + android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() + } + val showEmailError = email.isNotBlank() && !isEmailValid + + // heightIn(min = maxHeight) centres content when it fits and plain-scrolls when it doesn't. + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + .safeDrawingPadding(), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .heightIn(min = maxHeight) + .padding(horizontal = 48.dp, vertical = 24.dp), + ) { + Spacer(modifier = Modifier.weight(1f)) + + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + painter = painterResource(id = R.drawable.full_customization_mascot), + contentDescription = "doggo - cute welcome mascot", + modifier = Modifier + .size(96.dp) + .offset(y = 12.dp) + .zIndex(1f), + ) + + Surface( + color = MaterialTheme.colorScheme.secondary, + shape = IntroShape, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "intro - welcome headline bubble" }, + ) { + Text( + text = "Hey there,\nWelcome", + style = MaterialTheme.typography.headlineMedium.copy( + textAlign = TextAlign.Center, + brush = Brush.radialGradient( + colors = listOf( + Color(0xFFFFF8F8), + Color(0xFFFFDDB4), + Color(0xFFFFD8EB), + ), + ), + ), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 16.dp), + ) + } + + HardOffsetShadow( + shape = AuthFieldShape, + modifier = Modifier.fillMaxWidth(), + ) { + Surface( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "email - sign in card" }, + color = MaterialTheme.colorScheme.surface, + shape = AuthFieldShape, + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Text( + text = "Enter your email address to continue.", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth(), + ) + + FullCustomizationTextField( + value = email, + onValueChange = onEmailChange, + label = "Email address", + leadingIcon = { EmailFieldIcon() }, + enabled = !isLoading, + isError = showEmailError, + supportingText = if (showEmailError) "Enter a valid email address" else null, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - email address input" }, + ) + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // Two explicit choices: enumeration protection withholds whether an address is registered. + CtaButton( + text = "Sign in", + onClick = onSignIn, + enabled = isEmailValid && !isLoading, + isLoading = isLoading, + modifier = Modifier.semantics { contentDescription = "button - sign in" }, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + CtaButton( + text = "Create account", + onClick = onCreateAccount, + // Not gated on the address: the sign-up form collects and confirms it. + enabled = !isLoading, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + ), + modifier = Modifier.semantics { contentDescription = "button - create account" }, + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + TextButton( + onClick = onShowOtherMethods, + modifier = Modifier + .align(Alignment.CenterHorizontally) + .semantics { contentDescription = "Other sign-in methods button" }, + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.Login, + contentDescription = null, + modifier = Modifier.size(18.dp), + ) + Spacer(modifier = Modifier.width(8.dp)) + Text("Use other sign-in methods") + } + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/LoginStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/LoginStep.kt new file mode 100644 index 0000000000..769e3063af --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/LoginStep.kt @@ -0,0 +1,189 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.ui.screens.email.EmailAuthContentState +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthFieldShape +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.firebaseui.android.demo.auth.fullcustomization.common.EmailFieldIcon +import com.firebaseui.android.demo.auth.fullcustomization.common.FullCustomizationTextField +import com.firebaseui.android.demo.auth.fullcustomization.common.HardOffsetShadow + +@Composable +fun LoginStep( + state: EmailAuthContentState, + onUseDifferentEmail: () -> Unit, +) { + var passwordVisible by remember { mutableStateOf(false) } + + // heightIn(min = maxHeight) centres content when it fits and plain-scrolls when it doesn't. + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + .safeDrawingPadding(), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .heightIn(min = maxHeight) + .padding(horizontal = 40.dp, vertical = 24.dp), + ) { + Spacer(modifier = Modifier.weight(1f)) + + Column(modifier = Modifier.fillMaxWidth()) { + Image( + painter = painterResource(id = R.drawable.full_customization_mascot), + contentDescription = "doggo - cute welcome mascot", + modifier = Modifier.size(72.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Login", + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(24.dp)) + + HardOffsetShadow(shape = AuthFieldShape, modifier = Modifier.fillMaxWidth()) { + Surface( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "email - login card" }, + color = MaterialTheme.colorScheme.surface, + shape = AuthFieldShape, + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + FullCustomizationTextField( + value = state.email, + onValueChange = {}, + enabled = false, + leadingIcon = { EmailFieldIcon() }, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - email address display" }, + ) + + FullCustomizationTextField( + value = state.password, + onValueChange = state.onPasswordChange, + label = "Password", + enabled = !state.isLoading, + visualTransformation = if (passwordVisible) { + VisualTransformation.None + } else { + PasswordVisualTransformation() + }, + trailingIcon = { + IconButton(onClick = { passwordVisible = !passwordVisible }) { + Icon( + imageVector = if (passwordVisible) { + Icons.Default.VisibilityOff + } else { + Icons.Default.Visibility + }, + contentDescription = null, + ) + } + }, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - password secure input" }, + ) + + Text( + text = if (state.resetLinkSent) "Reset link sent!" else "Forgot password?", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, + textDecoration = TextDecoration.Underline, + textAlign = TextAlign.End, + modifier = Modifier + .fillMaxWidth() + .clickable(enabled = !state.resetLinkSent) { + state.onSendResetLinkClick() + }, + ) + } + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.height(24.dp)) + + Column(modifier = Modifier.fillMaxWidth()) { + CtaButton( + text = "Login", + onClick = state.onSignInClick, + enabled = state.password.isNotBlank() && !state.isLoading, + isLoading = state.isLoading, + modifier = Modifier.semantics { contentDescription = "button - login" }, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + CtaButton( + text = if (state.emailSignInLinkSent) "Login link sent!" else "Send login link", + onClick = state.onSignInEmailLinkClick, + enabled = !state.isLoading, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + ), + modifier = Modifier.semantics { contentDescription = "button - send login link" }, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = onUseDifferentEmail, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Use a different email") + } + } + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/SignUpStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/SignUpStep.kt new file mode 100644 index 0000000000..7f7dd2dff5 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/email/pages/SignUpStep.kt @@ -0,0 +1,237 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.email.pages + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.ui.screens.email.EmailAuthContentState +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthFieldShape +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.firebaseui.android.demo.auth.fullcustomization.common.EmailFieldIcon +import com.firebaseui.android.demo.auth.fullcustomization.common.FullCustomizationTextField +import com.firebaseui.android.demo.auth.fullcustomization.common.HardOffsetShadow + +private val NameFieldStartShape = RoundedCornerShape( + topStart = 16.dp, + bottomStart = 16.dp, + topEnd = 0.dp, + bottomEnd = 0.dp, +) +private val NameFieldEndShape = RoundedCornerShape( + topStart = 0.dp, + bottomStart = 0.dp, + topEnd = 16.dp, + bottomEnd = 16.dp, +) + +@Composable +fun SignUpStep( + state: EmailAuthContentState, + onUseDifferentEmail: () -> Unit, +) { + var firstName by remember { mutableStateOf("") } + var lastName by remember { mutableStateOf("") } + var confirmEmail by remember { mutableStateOf("") } + + // Trimmed and case-insensitive: the default keyboard auto-capitalises on many IMEs. + val emailsMatch = confirmEmail.isNotBlank() && + confirmEmail.trim().equals(state.email.trim(), ignoreCase = true) + val passwordsMatch = state.confirmPassword.isNotBlank() && state.confirmPassword == state.password + val canSignUp = firstName.isNotBlank() && + lastName.isNotBlank() && + emailsMatch && + state.password.isNotBlank() && + passwordsMatch && + !state.isLoading + + // heightIn(min = maxHeight) centres content when it fits and plain-scrolls when it doesn't. + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + .safeDrawingPadding(), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .heightIn(min = maxHeight) + .padding(horizontal = 40.dp, vertical = 24.dp), + ) { + Spacer(modifier = Modifier.weight(1f)) + + Column(modifier = Modifier.fillMaxWidth()) { + Image( + painter = painterResource(id = R.drawable.full_customization_mascot), + contentDescription = "doggo - cute welcome mascot", + modifier = Modifier.size(72.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Sign up", + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(24.dp)) + + HardOffsetShadow(shape = AuthFieldShape, modifier = Modifier.fillMaxWidth()) { + Surface( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "sign up card" }, + color = MaterialTheme.colorScheme.surface, + shape = AuthFieldShape, + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + Row(modifier = Modifier.fillMaxWidth()) { + FullCustomizationTextField( + value = firstName, + onValueChange = { firstName = it }, + label = "First name", + enabled = !state.isLoading, + shape = NameFieldStartShape, + modifier = Modifier + .weight(1f) + .semantics { contentDescription = "text-field - first name" }, + ) + FullCustomizationTextField( + value = lastName, + onValueChange = { lastName = it }, + label = "Last name", + enabled = !state.isLoading, + shape = NameFieldEndShape, + modifier = Modifier + .weight(1f) + .semantics { contentDescription = "text-field - last name" }, + ) + } + + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + FullCustomizationTextField( + value = state.email, + // Editable unlike the login form: the account does not exist yet. + onValueChange = state.onEmailChange, + label = "Email", + enabled = !state.isLoading, + leadingIcon = { EmailFieldIcon() }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Email, + ), + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - email address display" }, + ) + FullCustomizationTextField( + value = confirmEmail, + onValueChange = { confirmEmail = it }, + label = "Confirm Email", + enabled = !state.isLoading, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Email, + ), + isError = confirmEmail.isNotBlank() && !emailsMatch, + supportingText = if (confirmEmail.isNotBlank() && !emailsMatch) { + "Emails don't match" + } else { + null + }, + leadingIcon = { EmailFieldIcon() }, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - confirm email" }, + ) + } + + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + FullCustomizationTextField( + value = state.password, + onValueChange = state.onPasswordChange, + label = "Password", + enabled = !state.isLoading, + visualTransformation = PasswordVisualTransformation(), + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - password" }, + ) + FullCustomizationTextField( + value = state.confirmPassword, + onValueChange = state.onConfirmPasswordChange, + label = "Confirm Password", + enabled = !state.isLoading, + visualTransformation = PasswordVisualTransformation(), + isError = state.confirmPassword.isNotBlank() && !passwordsMatch, + supportingText = if (state.confirmPassword.isNotBlank() && !passwordsMatch) { + "Passwords don't match" + } else { + null + }, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - confirm password" }, + ) + } + } + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.height(24.dp)) + + Column(modifier = Modifier.fillMaxWidth()) { + CtaButton( + text = "Sign up", + onClick = { + state.onDisplayNameChange("$firstName $lastName".trim()) + state.onSignUpClick() + }, + enabled = canSignUp, + isLoading = state.isLoading, + modifier = Modifier.semantics { contentDescription = "button - sign up" }, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = onUseDifferentEmail, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Use a different email") + } + } + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/MfaChallengeUI.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/MfaChallengeUI.kt new file mode 100644 index 0000000000..741f41882d --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/MfaChallengeUI.kt @@ -0,0 +1,101 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.mfa + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.configuration.MfaFactor +import com.firebase.ui.auth.mfa.MfaChallengeContentState +import com.firebase.ui.auth.ui.components.VerificationCodeInputField +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthPage +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton + +/** + * Custom UI for `FirebaseAuthScreen.mfaChallengeContent` — the second-factor prompt shown during + * sign-in when the account has MFA enrolled. + */ +@Composable +fun MfaChallengeUI(state: MfaChallengeContentState) { + val isSms = state.factorType == MfaFactor.Sms + + AuthPage( + mascot = if (isSms) { + R.drawable.full_customization_phone_mascot + } else { + R.drawable.full_customization_mascot + }, + mascotDescription = "doggo - cute two-factor mascot", + title = "One more step", + cardContentDescription = "mfa - challenge card", + card = { + Text( + text = if (isSms) { + "We sent a code to ${state.maskedPhoneNumber ?: "your phone"}." + } else { + "Open your authenticator app and enter the 6-digit code for this account." + }, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + VerificationCodeInputField( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - mfa challenge code input" }, + isError = state.hasError, + errorMessage = state.error, + onCodeChange = state.onVerificationCodeChange, + ) + + // canResend already covers "SMS factor and a resend callback exists". + if (state.canResend) { + Text( + text = if (state.resendTimer > 0) { + "Resend code in ${state.resendTimer}s" + } else { + "Resend code" + }, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, + textAlign = TextAlign.End, + modifier = Modifier + .fillMaxWidth() + .clickable(enabled = state.resendTimer == 0 && !state.isLoading) { + state.onResendCodeClick?.invoke() + }, + ) + } + }, + actions = { + CtaButton( + text = "Verify", + onClick = state.onVerifyClick, + enabled = state.isValid && !state.isLoading, + isLoading = state.isLoading, + modifier = Modifier.semantics { + contentDescription = "button - verify mfa challenge" + }, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = state.onCancelClick, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Cancel") + } + }, + ) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/MfaEnrollmentUI.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/MfaEnrollmentUI.kt new file mode 100644 index 0000000000..f104c1bb00 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/MfaEnrollmentUI.kt @@ -0,0 +1,25 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.mfa + +import androidx.compose.runtime.Composable +import com.firebase.ui.auth.mfa.MfaEnrollmentContentState +import com.firebase.ui.auth.mfa.MfaEnrollmentStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.pages.ConfigureSmsStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.pages.ConfigureTotpStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.pages.SelectFactorStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.pages.VerifyFactorStep + +/** + * Custom UI for `FirebaseAuthScreen.mfaEnrollmentContent`. + * + * A single state object drives every enrollment step, so this only dispatches on + * [MfaEnrollmentContentState.step] — the library owns the step transitions. + */ +@Composable +fun MfaEnrollmentUI(state: MfaEnrollmentContentState) { + when (state.step) { + MfaEnrollmentStep.SelectFactor -> SelectFactorStep(state) + MfaEnrollmentStep.ConfigureSms -> ConfigureSmsStep(state) + MfaEnrollmentStep.ConfigureTotp -> ConfigureTotpStep(state) + MfaEnrollmentStep.VerifyFactor -> VerifyFactorStep(state) + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/ConfigureSmsStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/ConfigureSmsStep.kt new file mode 100644 index 0000000000..d73284fb9c --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/ConfigureSmsStep.kt @@ -0,0 +1,123 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.pages + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.requiredHeight +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.mfa.MfaEnrollmentContentState +import com.firebase.ui.auth.ui.components.CountrySelector +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthFieldShape +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthPage +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.firebaseui.android.demo.auth.fullcustomization.common.FullCustomizationTextField + +@Composable +fun ConfigureSmsStep(state: MfaEnrollmentContentState) { + AuthPage( + mascot = R.drawable.full_customization_phone_mascot, + mascotDescription = "doggo - cute phone sign-in mascot", + title = "Add your number", + cardContentDescription = "mfa - sms setup card", + card = { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth(), + ) { + // CountrySelector needs a non-null country, so skip the step while it resolves. + state.selectedCountry?.let { country -> + Surface( + color = Color.White, + shape = AuthFieldShape, + modifier = Modifier + .requiredHeight(56.dp) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = AuthFieldShape, + ) + .semantics { contentDescription = "country code selector" }, + ) { + CountrySelector( + selectedCountry = country, + onCountrySelected = state.onCountrySelected, + enabled = !state.isLoading, + ) + } + } + + FullCustomizationTextField( + value = state.phoneNumber, + onValueChange = state.onPhoneNumberChange, + placeholder = "Phone number", + leadingIcon = { + Icon( + imageVector = Icons.Default.Phone, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + enabled = !state.isLoading, + isError = state.hasError, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), + modifier = Modifier + .weight(1f) + .semantics { contentDescription = "text-field - mfa phone number input" }, + ) + } + + Text( + text = state.error + ?: "We'll text a code to this number whenever you sign in. " + + "Message & data rates may apply.", + style = MaterialTheme.typography.bodyLarge, + color = if (state.hasError) { + MaterialTheme.colorScheme.error + } else { + MaterialTheme.colorScheme.onSurfaceVariant + }, + ) + }, + actions = { + CtaButton( + text = "Send code", + onClick = state.onSendSmsCodeClick, + enabled = state.isValid && !state.isLoading, + isLoading = state.isLoading, + modifier = Modifier.semantics { + contentDescription = "button - send mfa sms code" + }, + ) + + if (state.canGoBack) { + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = state.onBackClick, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Pick a different method") + } + } + }, + ) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/ConfigureTotpStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/ConfigureTotpStep.kt new file mode 100644 index 0000000000..562fe7919a --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/ConfigureTotpStep.kt @@ -0,0 +1,102 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.pages + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.mfa.MfaEnrollmentContentState +import com.firebase.ui.auth.ui.components.QrCodeImage +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthFieldShape +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthPage +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton + +@Composable +fun ConfigureTotpStep(state: MfaEnrollmentContentState) { + AuthPage( + mascot = R.drawable.full_customization_mascot, + mascotDescription = "doggo - cute security mascot", + title = "Scan to set up", + cardContentDescription = "mfa - totp setup card", + card = { + Text( + text = "Scan this with your authenticator app, or type the key in by hand.", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + state.totpQrCodeUrl?.let { url -> + QrCodeImage( + content = url, + size = 200.dp, + modifier = Modifier + .align(Alignment.CenterHorizontally) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = AuthFieldShape, + ) + .padding(12.dp) + .semantics { contentDescription = "mfa - totp qr code" }, + ) + } + + state.totpSecret?.sharedSecretKey?.let { key -> + SelectionContainer { + Text( + text = key, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "mfa - totp shared secret key" }, + ) + } + } + + state.error?.let { error -> + Text( + text = error, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.error, + ) + } + }, + actions = { + CtaButton( + text = "I've added it", + onClick = state.onContinueToVerifyClick, + enabled = state.isValid && !state.isLoading, + isLoading = state.isLoading, + modifier = Modifier.semantics { + contentDescription = "button - continue to mfa verification" + }, + ) + + if (state.canGoBack) { + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = state.onBackClick, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Pick a different method") + } + } + }, + ) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/SelectFactorStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/SelectFactorStep.kt new file mode 100644 index 0000000000..70995a799e --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/SelectFactorStep.kt @@ -0,0 +1,141 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.pages + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.configuration.MfaFactor +import com.firebase.ui.auth.mfa.MfaEnrollmentContentState +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthPage +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.google.firebase.auth.MultiFactorInfo +import com.google.firebase.auth.PhoneMultiFactorGenerator +import com.google.firebase.auth.TotpMultiFactorGenerator + +@Composable +fun SelectFactorStep(state: MfaEnrollmentContentState) { + AuthPage( + mascot = R.drawable.full_customization_mascot, + mascotDescription = "doggo - cute security mascot", + title = "Secure your account", + cardContentDescription = "mfa - factor selection card", + card = { + Text( + text = "Add a second step to sign-in, so a password on its own isn't enough.", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + state.error?.let { error -> + Text( + text = error, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.error, + ) + } + + if (state.enrolledFactors.isNotEmpty()) { + HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) + + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + text = "Already on this account", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, + ) + + state.enrolledFactors.forEach { info -> + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = enrolledFactorLabel(info), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.weight(1f), + ) + TextButton( + onClick = { state.onUnenrollFactor(info) }, + enabled = !state.isLoading, + modifier = Modifier.semantics { + contentDescription = + "button - remove factor ${enrolledFactorLabel(info)}" + }, + ) { + Text("Remove") + } + } + } + } + } + }, + actions = { + state.availableFactors.forEachIndexed { index, factor -> + if (index > 0) Spacer(modifier = Modifier.height(16.dp)) + + CtaButton( + text = factorCtaLabel(factor), + onClick = { state.onFactorSelected(factor) }, + enabled = !state.isLoading, + // First factor takes the primary CTA colour; the rest read as alternatives. + colors = if (index == 0) { + ButtonDefaults.buttonColors() + } else { + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer, + ) + }, + modifier = Modifier.semantics { + contentDescription = "button - enroll ${factorCtaLabel(factor)}" + }, + ) + } + + state.onSkipClick?.let { onSkip -> + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = onSkip, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Not now") + } + } + }, + ) +} + +private fun factorCtaLabel(factor: MfaFactor): String = when (factor) { + MfaFactor.Sms -> "Use text message" + MfaFactor.Totp -> "Use an authenticator app" +} + +/** + * SMS factors carry the phone number as their display name; TOTP factors are often unnamed, so + * fall back to the factor id. + */ +private fun enrolledFactorLabel(info: MultiFactorInfo): String { + val fallback = when (info.factorId) { + PhoneMultiFactorGenerator.FACTOR_ID -> "Text message" + TotpMultiFactorGenerator.FACTOR_ID -> "Authenticator app" + else -> info.factorId + } + return info.displayName?.takeIf { it.isNotBlank() } ?: fallback +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/VerifyFactorStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/VerifyFactorStep.kt new file mode 100644 index 0000000000..351c73cfc5 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/mfa/pages/VerifyFactorStep.kt @@ -0,0 +1,100 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.mfa.pages + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.configuration.MfaFactor +import com.firebase.ui.auth.mfa.MfaEnrollmentContentState +import com.firebase.ui.auth.ui.components.VerificationCodeInputField +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthPage +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton + +@Composable +fun VerifyFactorStep(state: MfaEnrollmentContentState) { + val isSms = state.selectedFactor == MfaFactor.Sms + val fullPhoneNumber = "${state.selectedCountry?.dialCode ?: ""}${state.phoneNumber}" + + AuthPage( + mascot = if (isSms) { + R.drawable.full_customization_phone_mascot + } else { + R.drawable.full_customization_mascot + }, + mascotDescription = "doggo - cute two-factor mascot", + title = "Confirm the code", + cardContentDescription = "mfa - enrollment verification card", + card = { + Text( + text = if (isSms) { + "We sent a code to $fullPhoneNumber." + } else { + "Enter the 6-digit code your authenticator app is showing right now." + }, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + VerificationCodeInputField( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - mfa enrollment code input" }, + isError = state.hasError, + errorMessage = state.error, + onCodeChange = state.onVerificationCodeChange, + ) + + // onResendCodeClick is null for TOTP, where there is nothing to resend. + state.onResendCodeClick?.let { onResend -> + Text( + text = if (state.resendTimer > 0) { + "Resend code in ${state.resendTimer}s" + } else { + "Resend code" + }, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, + textAlign = TextAlign.End, + modifier = Modifier + .fillMaxWidth() + .clickable(enabled = state.resendTimer == 0 && !state.isLoading) { + onResend() + }, + ) + } + }, + actions = { + CtaButton( + text = "Verify", + onClick = state.onVerifyClick, + enabled = state.isValid && !state.isLoading, + isLoading = state.isLoading, + modifier = Modifier.semantics { + contentDescription = "button - verify mfa enrollment" + }, + ) + + if (state.canGoBack) { + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = state.onBackClick, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Back") + } + } + }, + ) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/PhoneSignInUI.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/PhoneSignInUI.kt new file mode 100644 index 0000000000..ecf4bc41a1 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/PhoneSignInUI.kt @@ -0,0 +1,30 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.phone + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import com.firebase.ui.auth.ui.screens.phone.PhoneAuthContentState +import com.firebase.ui.auth.ui.screens.phone.PhoneAuthStep +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.screens.phone.pages.PhoneEntryStep +import com.firebaseui.android.demo.auth.fullcustomization.screens.phone.pages.PhoneVerificationStep + +@Composable +fun PhoneSignInUI(state: PhoneAuthContentState) { + Box(modifier = Modifier.fillMaxSize()) { + Image( + painter = painterResource(id = R.drawable.custom_background), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize(), + ) + when (state.step) { + PhoneAuthStep.EnterPhoneNumber -> PhoneEntryStep(state) + PhoneAuthStep.EnterVerificationCode -> PhoneVerificationStep(state) + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/pages/PhoneEntryStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/pages/PhoneEntryStep.kt new file mode 100644 index 0000000000..87e7d8ed45 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/pages/PhoneEntryStep.kt @@ -0,0 +1,162 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.phone.pages + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredHeight +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.ui.components.CountrySelector +import com.firebase.ui.auth.ui.screens.phone.PhoneAuthContentState +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthFieldShape +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.firebaseui.android.demo.auth.fullcustomization.common.FullCustomizationTextField +import com.firebaseui.android.demo.auth.fullcustomization.common.HardOffsetShadow + +@Composable +fun PhoneEntryStep(state: PhoneAuthContentState) { + val isPhoneValid = remember(state.phoneNumber) { + android.util.Patterns.PHONE.matcher(state.phoneNumber).matches() + } + + // heightIn(min = maxHeight) centres content when it fits and plain-scrolls when it doesn't. + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + .safeDrawingPadding(), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .heightIn(min = maxHeight) + .padding(horizontal = 40.dp, vertical = 24.dp), + ) { + Spacer(modifier = Modifier.weight(1f)) + + Column(modifier = Modifier.fillMaxWidth()) { + Image( + painter = painterResource(id = R.drawable.full_customization_phone_mascot), + contentDescription = "doggo - cute phone sign-in mascot", + modifier = Modifier.size(72.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Login by phone number", + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(24.dp)) + + HardOffsetShadow(shape = AuthFieldShape, modifier = Modifier.fillMaxWidth()) { + Surface( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "phone - sign in card" }, + color = MaterialTheme.colorScheme.surface, + shape = AuthFieldShape, + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth(), + ) { + Surface( + color = Color.White, + shape = AuthFieldShape, + modifier = Modifier + .requiredHeight(56.dp) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = AuthFieldShape, + ) + .semantics { contentDescription = "country code selector" }, + ) { + CountrySelector( + selectedCountry = state.selectedCountry, + onCountrySelected = state.onCountrySelected, + enabled = !state.isLoading, + ) + } + + FullCustomizationTextField( + value = state.phoneNumber, + onValueChange = state.onPhoneNumberChange, + placeholder = "Phone number", + leadingIcon = { + Icon( + imageVector = Icons.Default.Phone, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + enabled = !state.isLoading, + isError = state.error != null, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), + modifier = Modifier + .weight(1f) + .semantics { contentDescription = "text-field - phone number input" }, + ) + } + + Text( + text = state.error + ?: "By signing in with phone number, an SMS may be sent. " + + "Message & data rates may apply.", + style = MaterialTheme.typography.bodyLarge, + color = if (state.error != null) { + MaterialTheme.colorScheme.error + } else { + MaterialTheme.colorScheme.onSurfaceVariant + }, + ) + } + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.height(24.dp)) + + CtaButton( + text = "Send code", + onClick = state.onSendCodeClick, + enabled = isPhoneValid && !state.isLoading, + isLoading = state.isLoading, + modifier = Modifier.semantics { contentDescription = "button - send verification code" }, + ) + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/pages/PhoneVerificationStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/pages/PhoneVerificationStep.kt new file mode 100644 index 0000000000..8e9a153182 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/phone/pages/PhoneVerificationStep.kt @@ -0,0 +1,137 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.phone.pages + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.ui.components.VerificationCodeInputField +import com.firebase.ui.auth.ui.screens.phone.PhoneAuthContentState +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthFieldShape +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.firebaseui.android.demo.auth.fullcustomization.common.HardOffsetShadow + +@Composable +fun PhoneVerificationStep(state: PhoneAuthContentState) { + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + .safeDrawingPadding(), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .heightIn(min = maxHeight) + .padding(horizontal = 40.dp, vertical = 24.dp), + ) { + Spacer(modifier = Modifier.weight(1f)) + + Column(modifier = Modifier.fillMaxWidth()) { + Image( + painter = painterResource(id = R.drawable.full_customization_phone_mascot), + contentDescription = "doggo - cute phone sign-in mascot", + modifier = Modifier.size(72.dp), + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Enter your code", + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(24.dp)) + + HardOffsetShadow(shape = AuthFieldShape, modifier = Modifier.fillMaxWidth()) { + Surface( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "phone - verification card" }, + color = MaterialTheme.colorScheme.surface, + shape = AuthFieldShape, + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Text( + text = "We sent a code to ${state.fullPhoneNumber}.", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + VerificationCodeInputField( + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - verification code input" }, + isError = state.error != null, + errorMessage = state.error, + onCodeChange = state.onVerificationCodeChange, + ) + + Text( + text = if (state.resendTimer > 0) { + "Resend code in ${state.resendTimer}s" + } else { + "Resend code" + }, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, + textAlign = TextAlign.End, + modifier = Modifier + .fillMaxWidth() + .clickable(enabled = state.resendTimer == 0) { + state.onResendCodeClick() + }, + ) + } + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.height(24.dp)) + + Column(modifier = Modifier.fillMaxWidth()) { + CtaButton( + text = "Verify", + onClick = state.onVerifyCodeClick, + enabled = state.verificationCode.isNotBlank() && !state.isLoading, + isLoading = state.isLoading, + modifier = Modifier.semantics { contentDescription = "button - verify code" }, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = state.onChangeNumberClick, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Use a different number") + } + } + } + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/reauth/ReauthEmailStep.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/reauth/ReauthEmailStep.kt new file mode 100644 index 0000000000..d22ee432be --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/reauth/ReauthEmailStep.kt @@ -0,0 +1,143 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.reauth + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.ui.screens.email.EmailAuthContentState +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.CtaButton +import com.firebaseui.android.demo.auth.fullcustomization.common.FullCustomizationTextField + +/** + * The email step of reauthentication, for `FirebaseAuthScreen.emailContent` when the library has + * locked the address. + * + * Sign-in's own page is the wrong screen here twice over. The library composes the reauth email + * step inside a modal bottom sheet, so a full-bleed background and a viewport-height layout fight + * the sheet rather than sit in it; and reauthentication turns off sign-up and email-link sign-in + * (`isEmailSignUpOffered`/`isEmailLinkSignInOffered` both return false in that mode), so the + * affordances that page offers alongside the password are dead. This is the one thing the user can + * actually do: confirm the password for an address they cannot change. + * + * Password reset stays, because it still works — the link is sent while the sheet is up. + */ +@Composable +fun ReauthEmailStep(state: EmailAuthContentState) { + var passwordVisible by remember { mutableStateOf(false) } + + Column( + modifier = Modifier + .fillMaxWidth() + .navigationBarsPadding() + .imePadding() + .padding(horizontal = 24.dp) + .padding(bottom = 24.dp) + .semantics { contentDescription = "reauth - email password card" }, + ) { + Image( + painter = painterResource(id = R.drawable.full_customization_mascot), + contentDescription = "doggo - cute welcome mascot", + modifier = Modifier.size(56.dp), + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Confirm it's you", + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.primary, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Enter the password for ${state.email}.", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + Spacer(modifier = Modifier.height(24.dp)) + + FullCustomizationTextField( + value = state.password, + onValueChange = state.onPasswordChange, + label = "Password", + enabled = !state.isLoading, + isError = state.error != null, + supportingText = state.error, + visualTransformation = if (passwordVisible) { + VisualTransformation.None + } else { + PasswordVisualTransformation() + }, + trailingIcon = { + IconButton(onClick = { passwordVisible = !passwordVisible }) { + Icon( + imageVector = if (passwordVisible) { + Icons.Default.VisibilityOff + } else { + Icons.Default.Visibility + }, + contentDescription = null, + ) + } + }, + modifier = Modifier + .fillMaxWidth() + .semantics { contentDescription = "text-field - reauth password secure input" }, + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = if (state.resetLinkSent) "Reset link sent!" else "Forgot password?", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, + textDecoration = TextDecoration.Underline, + textAlign = TextAlign.End, + modifier = Modifier + .fillMaxWidth() + .clickable(enabled = !state.resetLinkSent && !state.isLoading) { + state.onSendResetLinkClick() + }, + ) + + Spacer(modifier = Modifier.height(24.dp)) + + CtaButton( + text = "Confirm", + onClick = state.onSignInClick, + enabled = state.password.isNotBlank() && !state.isLoading, + isLoading = state.isLoading, + modifier = Modifier.semantics { contentDescription = "button - confirm reauth" }, + ) + } +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/reauth/ReauthUI.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/reauth/ReauthUI.kt new file mode 100644 index 0000000000..557cc79c53 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/screens/reauth/ReauthUI.kt @@ -0,0 +1,92 @@ +package com.firebaseui.android.demo.auth.fullcustomization.screens.reauth + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import com.firebase.ui.auth.ui.screens.reauth.ReauthContentState +import com.firebaseui.android.demo.R +import com.firebaseui.android.demo.auth.fullcustomization.common.AuthPage +import com.firebaseui.android.demo.auth.fullcustomization.common.SheetProviderButton + +/** + * Custom UI for `FirebaseAuthScreen.reauthContent`. + * + * [ReauthContentState.providers] arrives already filtered to the providers linked to this user, and + * [ReauthContentState.onProviderSelected] performs the credential exchange, so this is purely a + * chooser: the library owns the reauthentication itself and the dismiss/retry sequencing that + * follows it. Picking email or phone hands off to the library's own sub-flow. + */ +@Composable +fun ReauthUI(state: ReauthContentState) { + // Overlay outside the NavHost: without this, back would finish the Activity mid-reauth. + BackHandler(enabled = !state.isLoading) { state.onDismiss() } + + AuthPage( + mascot = R.drawable.full_customization_mascot, + mascotDescription = "doggo - cute welcome mascot", + title = "Is that you?", + cardContentDescription = "reauth - provider chooser card", + card = { + Text( + text = state.reason + ?: "Confirm it's you to continue with ${state.user.email ?: "this account"}.", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + + state.error?.let { error -> + Text( + text = error, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.error, + ) + } + + if (state.isLoading) { + CircularProgressIndicator( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .semantics { contentDescription = "reauth - in progress" }, + ) + } + }, + actions = { + Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { + state.providers.forEach { provider -> + SheetProviderButton( + provider = provider, + onClick = { state.onProviderSelected(provider) }, + modifier = Modifier + .fillMaxWidth() + .semantics { + contentDescription = "button - reauth with ${provider.providerName}" + }, + ) + } + } + + Spacer(modifier = Modifier.height(8.dp)) + + TextButton( + onClick = state.onDismiss, + enabled = !state.isLoading, + modifier = Modifier.fillMaxWidth(), + ) { + Text("Cancel") + } + }, + ) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationShapes.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationShapes.kt new file mode 100644 index 0000000000..483b0b741d --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationShapes.kt @@ -0,0 +1,8 @@ +package com.firebaseui.android.demo.auth.fullcustomization.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.ui.unit.dp + +val IntroShape = RoundedCornerShape(80.dp) +val ButtonShape = RoundedCornerShape(36.dp) +val ProviderButtonShape = RoundedCornerShape(percent = 50) diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationTheme.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationTheme.kt new file mode 100644 index 0000000000..53b9161191 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationTheme.kt @@ -0,0 +1,135 @@ +package com.firebaseui.android.demo.auth.fullcustomization.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import com.firebase.ui.auth.configuration.theme.AuthUITheme +import kotlin.math.max +import kotlin.math.min + +private val LightPrimary = Color(0xFF864B6F) +private val LightOnPrimary = Color(0xFFFFFFFF) +private val LightPrimaryContainer = Color(0xFFFFD8EB) +private val LightOnPrimaryContainer = Color(0xFF7B3B73) +private val LightInversePrimary = Color(0xFFFAB1DA) +private val LightSecondary = Color(0xFF4C8BFF) +private val LightOnSecondary = Color(0xFFFFFFFF) +private val LightSecondaryContainer = Color(0xFFCCE5FF) +private val LightTertiaryContainer = Color(0xFFFFDDB4) +private val LightSurface = Color(0xFFFFF8F8) +private val LightSurfaceBright = Color(0xFFFFF8F8) +private val LightOnSurface = Color(0xFF211A1D) +private val LightOnSurfaceVariant = Color(0xFFA08B95) +private val LightSurfaceContainer = Color(0xFFF9EAEF) +private val LightSurfaceContainerLow = Color(0xFFFDF0F6) +private val LightOutline = Color(0xFF81737A) +private val LightOutlineVariant = Color(0xFFD3C2C9) +private val LightInverseSurface = Color(0xFF322F35) +private val LightInverseOnSurface = Color(0xFFF5EFF7) + +val FullCustomizationLightColorScheme = lightColorScheme( + primary = LightPrimary, + onPrimary = LightOnPrimary, + primaryContainer = LightPrimaryContainer, + onPrimaryContainer = LightOnPrimaryContainer, + inversePrimary = LightInversePrimary, + secondary = LightSecondary, + onSecondary = LightOnSecondary, + secondaryContainer = LightSecondaryContainer, + tertiaryContainer = LightTertiaryContainer, + surface = LightSurface, + surfaceBright = LightSurfaceBright, + onSurface = LightOnSurface, + onSurfaceVariant = LightOnSurfaceVariant, + surfaceContainer = LightSurfaceContainer, + surfaceContainerLow = LightSurfaceContainerLow, + outline = LightOutline, + outlineVariant = LightOutlineVariant, + inverseSurface = LightInverseSurface, + inverseOnSurface = LightInverseOnSurface, +) + +val FullCustomizationDarkColorScheme = darkColorScheme( + primary = LightPrimary.withLightness(0.78f), + onPrimary = LightOnPrimary.withLightness(0.18f), + primaryContainer = LightPrimaryContainer.withLightness(0.28f), + onPrimaryContainer = LightOnPrimaryContainer.withLightness(0.88f), + inversePrimary = LightPrimary, + secondary = LightSecondary.withLightness(0.78f), + onSecondary = LightOnSecondary.withLightness(0.18f), + secondaryContainer = LightSecondaryContainer.withLightness(0.28f), + tertiaryContainer = LightTertiaryContainer.withLightness(0.28f), + surface = LightSurface.withLightness(0.10f), + surfaceBright = LightSurfaceBright.withLightness(0.20f), + onSurface = LightOnSurface.withLightness(0.88f), + onSurfaceVariant = LightOnSurfaceVariant.withLightness(0.75f), + surfaceContainer = LightSurfaceContainer.withLightness(0.13f), + surfaceContainerLow = LightSurfaceContainerLow.withLightness(0.11f), + outline = LightOutline.withLightness(0.55f), + outlineVariant = LightOutlineVariant.withLightness(0.30f), + inverseSurface = LightSurface.withLightness(0.90f), + inverseOnSurface = LightOnSurface.withLightness(0.15f), +) + +@Composable +fun FullCustomizationTheme(content: @Composable () -> Unit) { + val colorScheme = if (isSystemInDarkTheme()) { + FullCustomizationDarkColorScheme + } else { + FullCustomizationLightColorScheme + } + AuthUITheme( + theme = AuthUITheme.Default.copy( + colorScheme = colorScheme, + typography = FullCustomizationTypography, + providerButtonShape = ProviderButtonShape, + ), + content = content, + ) +} + +private fun Color.withLightness(newLightness: Float): Color { + val (h, s, _) = toHsl() + return hslToColor(h, s, newLightness.coerceIn(0f, 1f), alpha) +} + +private fun Color.toHsl(): Triple { + val r = red + val g = green + val b = blue + val maxC = max(r, max(g, b)) + val minC = min(r, min(g, b)) + val l = (maxC + minC) / 2f + if (maxC == minC) return Triple(0f, 0f, l) + val d = maxC - minC + val s = if (l > 0.5f) d / (2f - maxC - minC) else d / (maxC + minC) + val h = when (maxC) { + r -> ((g - b) / d + (if (g < b) 6f else 0f)) + g -> ((b - r) / d + 2f) + else -> ((r - g) / d + 4f) + } / 6f + return Triple(h, s, l) +} + +private fun hslToColor(h: Float, s: Float, l: Float, alpha: Float): Color { + if (s == 0f) return Color(l, l, l, alpha) + fun hueToRgb(p: Float, q: Float, tIn: Float): Float { + var t = tIn + if (t < 0f) t += 1f + if (t > 1f) t -= 1f + return when { + t < 1f / 6f -> p + (q - p) * 6f * t + t < 1f / 2f -> q + t < 2f / 3f -> p + (q - p) * (2f / 3f - t) * 6f + else -> p + } + } + val q = if (l < 0.5f) l * (1f + s) else l + s - l * s + val p = 2f * l - q + val r = hueToRgb(p, q, h + 1f / 3f) + val g = hueToRgb(p, q, h) + val b = hueToRgb(p, q, h - 1f / 3f) + return Color(r, g, b, alpha) +} diff --git a/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationTypography.kt b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationTypography.kt new file mode 100644 index 0000000000..da31b8bc23 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/auth/fullcustomization/theme/FullCustomizationTypography.kt @@ -0,0 +1,59 @@ +package com.firebaseui.android.demo.auth.fullcustomization.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import com.firebaseui.android.demo.R + +val BagelFatOne = FontFamily(Font(R.font.bagel_fat_one_regular, FontWeight.Normal)) + +val Onest = FontFamily( + Font(R.font.onest_regular, FontWeight.Normal), + Font(R.font.onest_medium, FontWeight.Medium), + Font(R.font.onest_semibold, FontWeight.SemiBold), + Font(R.font.onest_bold, FontWeight.Bold), +) + +val Roboto = FontFamily( + Font(R.font.roboto_regular, FontWeight.Normal), + Font(R.font.roboto_medium, FontWeight.Medium), + Font(R.font.roboto_semibold, FontWeight.SemiBold), + Font(R.font.roboto_bold, FontWeight.Bold), +) + +val FullCustomizationTypography = Typography( + headlineSmall = TextStyle( + fontFamily = BagelFatOne, + fontWeight = FontWeight.Normal, + fontSize = 28.sp, + lineHeight = 36.sp, + ), + headlineMedium = TextStyle( + fontFamily = BagelFatOne, + fontWeight = FontWeight.Normal, + fontSize = 36.sp, + lineHeight = 44.sp, + ), + bodyLarge = TextStyle( + fontFamily = Onest, + fontWeight = FontWeight.Medium, + fontSize = 16.sp, + lineHeight = 24.sp, + ), + labelLarge = TextStyle( + fontFamily = Roboto, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.1.sp, + ), + titleMedium = TextStyle( + fontFamily = Onest, + fontWeight = FontWeight.Bold, + fontSize = 20.sp, + lineHeight = 20.sp, + ), +) diff --git a/app/src/main/java/com/firebaseui/android/demo/database/DatabaseDemoActivity.kt b/app/src/main/java/com/firebaseui/android/demo/database/DatabaseDemoActivity.kt index 4e99cbfd33..b756c2276f 100644 --- a/app/src/main/java/com/firebaseui/android/demo/database/DatabaseDemoActivity.kt +++ b/app/src/main/java/com/firebaseui/android/demo/database/DatabaseDemoActivity.kt @@ -33,6 +33,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.firebase.ui.database.paging.DatabasePagingOptions import com.firebase.ui.database.paging.FirebaseRecyclerPagingAdapter +import com.firebaseui.android.demo.R import com.google.firebase.auth.FirebaseAuth import com.google.firebase.database.DatabaseReference import com.google.firebase.database.FirebaseDatabase @@ -122,7 +123,8 @@ class ScoreAdapter(options: DatabasePagingOptions) : override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ScoreViewHolder(parent) override fun onBindViewHolder(holder: ScoreViewHolder, position: Int, model: ScoreItem) { - (holder.itemView as TextView).text = "${model.name} — score: ${model.score}" + val row = holder.itemView as TextView + row.text = row.context.getString(R.string.demo_score_row, model.name, model.score) } } diff --git a/app/src/main/java/com/firebaseui/android/demo/firestore/FirestoreDemoActivity.kt b/app/src/main/java/com/firebaseui/android/demo/firestore/FirestoreDemoActivity.kt index 91d07abb91..3fb4e44d5b 100644 --- a/app/src/main/java/com/firebaseui/android/demo/firestore/FirestoreDemoActivity.kt +++ b/app/src/main/java/com/firebaseui/android/demo/firestore/FirestoreDemoActivity.kt @@ -33,6 +33,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.firebase.ui.firestore.paging.FirestorePagingAdapter import com.firebase.ui.firestore.paging.FirestorePagingOptions +import com.firebaseui.android.demo.R import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.CollectionReference import com.google.firebase.firestore.FirebaseFirestore @@ -129,7 +130,8 @@ class ScoreAdapter(options: FirestorePagingOptions) : override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ScoreViewHolder(parent) override fun onBindViewHolder(holder: ScoreViewHolder, position: Int, model: ScoreItem) { - (holder.itemView as TextView).text = "${model.name} — score: ${model.score}" + val row = holder.itemView as TextView + row.text = row.context.getString(R.string.demo_score_row, model.name, model.score) } } diff --git a/app/src/main/java/com/firebaseui/android/demo/utils/EmailLinkRouting.kt b/app/src/main/java/com/firebaseui/android/demo/utils/EmailLinkRouting.kt new file mode 100644 index 0000000000..d881baad43 --- /dev/null +++ b/app/src/main/java/com/firebaseui/android/demo/utils/EmailLinkRouting.kt @@ -0,0 +1,33 @@ +package com.firebaseui.android.demo.utils + +import android.net.Uri +import androidx.core.net.toUri + +/** + * Works out which auth demo a returning email link came from. + * + * Demo plumbing, not an example of library usage: this app has several auth demos behind one deep + * link, so it has to tag its own links and route the return trip. An app with a single sign-in + * screen needs none of this — it passes the link straight to `FirebaseAuthScreen(emailLink = …)`. + */ + +/** The query parameter each demo tags its own continue URL with. */ +const val EMAIL_LINK_ORIGIN_PARAM = "demo" + +/** The continue URL, which sits either directly on [uri] or nested inside its `link`. */ +private fun continueUrlOf(uri: Uri): Uri? { + uri.getQueryParameter("continueUrl")?.let { return it.toUri() } + uri.getQueryParameter("link")?.let { return continueUrlOf(it.toUri()) } + return null +} + +/** + * Which demo sent [link], or null when it says nothing about where it came from. + * + * Read off the continue URL rather than the outer link, so the library's own `ui_` parameters and + * any the action handler adds cannot be mistaken for the tag. + */ +internal fun emailLinkOrigin(link: String?): String? = + link?.takeIf { it.isNotEmpty() } + ?.let { runCatching { continueUrlOf(it.toUri()) }.getOrNull() } + ?.getQueryParameter(EMAIL_LINK_ORIGIN_PARAM) diff --git a/app/src/main/res/drawable-hdpi/firebase_auth.png b/app/src/main/res/drawable-hdpi/firebase_auth.png deleted file mode 100644 index fecbcb6dd4..0000000000 Binary files a/app/src/main/res/drawable-hdpi/firebase_auth.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/firebase_auth.webp b/app/src/main/res/drawable-hdpi/firebase_auth.webp new file mode 100644 index 0000000000..ecdf81adb5 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/firebase_auth.webp differ diff --git a/app/src/main/res/drawable-mdpi/firebase_auth.png b/app/src/main/res/drawable-mdpi/firebase_auth.png deleted file mode 100644 index bc9af3cc0c..0000000000 Binary files a/app/src/main/res/drawable-mdpi/firebase_auth.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/firebase_auth.webp b/app/src/main/res/drawable-mdpi/firebase_auth.webp new file mode 100644 index 0000000000..cb5675df38 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/firebase_auth.webp differ diff --git a/app/src/main/res/drawable-nodpi/custom_background.webp b/app/src/main/res/drawable-nodpi/custom_background.webp new file mode 100644 index 0000000000..c92b00b9ce Binary files /dev/null and b/app/src/main/res/drawable-nodpi/custom_background.webp differ diff --git a/app/src/main/res/drawable-nodpi/email_at_sign.webp b/app/src/main/res/drawable-nodpi/email_at_sign.webp new file mode 100644 index 0000000000..dd61129ec7 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/email_at_sign.webp differ diff --git a/app/src/main/res/drawable-nodpi/full_customization_mascot.webp b/app/src/main/res/drawable-nodpi/full_customization_mascot.webp new file mode 100644 index 0000000000..4655b76464 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/full_customization_mascot.webp differ diff --git a/app/src/main/res/drawable-nodpi/full_customization_phone_mascot.webp b/app/src/main/res/drawable-nodpi/full_customization_phone_mascot.webp new file mode 100644 index 0000000000..c9efa6c9ee Binary files /dev/null and b/app/src/main/res/drawable-nodpi/full_customization_phone_mascot.webp differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index fde1368fc1..0000000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/firebase_auth.png b/app/src/main/res/drawable-xhdpi/firebase_auth.png deleted file mode 100644 index 8a93e39a6a..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/firebase_auth.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/firebase_auth.webp b/app/src/main/res/drawable-xhdpi/firebase_auth.webp new file mode 100644 index 0000000000..1bfda9b100 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/firebase_auth.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/firebase_auth.png b/app/src/main/res/drawable-xxhdpi/firebase_auth.png deleted file mode 100644 index c01b18b144..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/firebase_auth.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/firebase_auth.webp b/app/src/main/res/drawable-xxhdpi/firebase_auth.webp new file mode 100644 index 0000000000..600f00a5d1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/firebase_auth.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/firebase_auth.png b/app/src/main/res/drawable-xxxhdpi/firebase_auth.png deleted file mode 100644 index 221da4d3ae..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/firebase_auth.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/firebase_auth.webp b/app/src/main/res/drawable-xxxhdpi/firebase_auth.webp new file mode 100644 index 0000000000..0336659595 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/firebase_auth.webp differ diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 1e4408cae4..0000000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/font/bagel_fat_one_regular.ttf b/app/src/main/res/font/bagel_fat_one_regular.ttf new file mode 100644 index 0000000000..9de4a2f786 Binary files /dev/null and b/app/src/main/res/font/bagel_fat_one_regular.ttf differ diff --git a/app/src/main/res/font/onest_bold.ttf b/app/src/main/res/font/onest_bold.ttf new file mode 100644 index 0000000000..b0a3dd939e Binary files /dev/null and b/app/src/main/res/font/onest_bold.ttf differ diff --git a/app/src/main/res/font/onest_medium.ttf b/app/src/main/res/font/onest_medium.ttf new file mode 100644 index 0000000000..2ff600481c Binary files /dev/null and b/app/src/main/res/font/onest_medium.ttf differ diff --git a/app/src/main/res/font/onest_regular.ttf b/app/src/main/res/font/onest_regular.ttf new file mode 100644 index 0000000000..dec9f7a23b Binary files /dev/null and b/app/src/main/res/font/onest_regular.ttf differ diff --git a/app/src/main/res/font/onest_semibold.ttf b/app/src/main/res/font/onest_semibold.ttf new file mode 100644 index 0000000000..c7e8a3d2e9 Binary files /dev/null and b/app/src/main/res/font/onest_semibold.ttf differ diff --git a/app/src/main/res/font/roboto_bold.ttf b/app/src/main/res/font/roboto_bold.ttf new file mode 100644 index 0000000000..651618564b Binary files /dev/null and b/app/src/main/res/font/roboto_bold.ttf differ diff --git a/app/src/main/res/font/roboto_medium.ttf b/app/src/main/res/font/roboto_medium.ttf new file mode 100644 index 0000000000..bc5b170260 Binary files /dev/null and b/app/src/main/res/font/roboto_medium.ttf differ diff --git a/app/src/main/res/font/roboto_regular.ttf b/app/src/main/res/font/roboto_regular.ttf new file mode 100644 index 0000000000..3db0d1fb08 Binary files /dev/null and b/app/src/main/res/font/roboto_regular.ttf differ diff --git a/app/src/main/res/font/roboto_semibold.ttf b/app/src/main/res/font/roboto_semibold.ttf new file mode 100644 index 0000000000..7a8ef87d58 Binary files /dev/null and b/app/src/main/res/font/roboto_semibold.ttf differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml deleted file mode 100644 index f8c6127d32..0000000000 --- a/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b0b680c7f1..3d815a6311 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,10 +1,19 @@ - + FirebaseUI Demo CHANGE-HERE - - APP-ID - fbAPP-ID - CHANGE-HERE - \ No newline at end of file + + %1$s — score: %2$d + + + APP-ID + fbAPP-ID + CHANGE-HERE + diff --git a/auth/README.md b/auth/README.md index be8fb1fa7e..613569d90e 100644 --- a/auth/README.md +++ b/auth/README.md @@ -2223,6 +2223,33 @@ Or override individual strings in your `strings.xml`: ``` +### Error message resolution + +Error text is chosen when the exception is built, not when the dialog renders it, and it resolves in this order: + +1. **The type-level hook**, one `fui_error_*` resource per exception type. These ship **deliberately blank**, and a blank value means "skip me" rather than "show nothing". +2. **A per-code string**, selected from the Firebase error code, so a mistyped SMS code and a wrong password no longer produce the same sentence. +3. **The Firebase SDK's own message**, English only, reached only for codes the library does not map. + +Setting a type-level hook therefore overrides *every* code of that type at once. That is occasionally what you want — uniform copy resists account enumeration, since distinguishing "no such account" from "wrong password" tells an attacker which addresses are registered — but it costs you the specific per-code messages: + +```xml + + + Those sign-in details aren\'t correct. + +``` + +Developer misconfiguration is handled separately. `AuthException.MisconfigurationException` carries generic translated copy on `message`, and keeps Firebase's diagnostic on `cause` so it reaches your logs without reaching your users: + +```kotlin +is AuthException.MisconfigurationException -> { + Log.e(TAG, "Check the Firebase console", exception.cause) +} +``` + +One limit worth knowing: `FirebaseAuthUI.signOut`, `withReauth` and `delete` take a `Context` and no configuration, so their messages resolve against that `Context` rather than a `stringProvider` or `locale` you configured. Pass a locale-aware `Context` if that matters. + ## Error Handling FirebaseUI provides a comprehensive exception hierarchy: diff --git a/auth/build.gradle.kts b/auth/build.gradle.kts index 76d2d5ab92..60390d512f 100644 --- a/auth/build.gradle.kts +++ b/auth/build.gradle.kts @@ -37,14 +37,6 @@ android { } lint { - // Common lint options across all modules - disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor" // We almost never care about this - ) - // Module specific disable += mutableSetOf( "UnusedQuantity", @@ -64,12 +56,11 @@ android { "LogConditional" ) - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - - // Pre-existing debt only: 168 localization findings (CPRN-432). Every entry is - // suppressed; new ones still fail. + // Pre-existing debt only: 43 MissingTranslation findings (CPRN-432). Every entry + // is suppressed; new ones still fail. They are strings the Compose rewrite added to + // values/strings.xml and never sent for translation, real UI copy in 50 languages, + // so they clear when translations land, not by editing anything here. Delete this + // file once they do. baseline = file("lint-baseline.xml") } @@ -161,6 +152,12 @@ dependencies { testImplementation(libs.kotlinx.serialization.json) debugImplementation(project(":internal:lintchecks")) + + // Directly, not via :internal:lintchecks. That module declares lintChecks too, but + // lintChecks only applies to the module declaring it, and a debugImplementation + // dependency does not carry lint checks to the consumer — so the custom rules were never + // running here. + lintChecks(project(":internal:lint")) } kotlin { diff --git a/auth/lint-baseline.xml b/auth/lint-baseline.xml index b9c0976fa1..b3d2379778 100644 --- a/auth/lint-baseline.xml +++ b/auth/lint-baseline.xml @@ -1,341 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/auth/src/main/java/com/firebase/ui/auth/AuthException.kt b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt index 6d09582973..eefefb00b6 100644 --- a/auth/src/main/java/com/firebase/ui/auth/AuthException.kt +++ b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt @@ -19,7 +19,9 @@ import com.firebase.ui.auth.AuthException.Companion.from import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider import com.google.firebase.FirebaseException +import com.google.firebase.FirebaseTooManyRequestsException import com.google.firebase.auth.AuthCredential +import com.google.firebase.auth.FirebaseAuthActionCodeException import com.google.firebase.auth.FirebaseAuthException import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException import com.google.firebase.auth.FirebaseAuthInvalidUserException @@ -27,6 +29,7 @@ import com.google.firebase.auth.FirebaseAuthMultiFactorException import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException import com.google.firebase.auth.FirebaseAuthUserCollisionException import com.google.firebase.auth.FirebaseAuthWeakPasswordException +import java.util.Locale /** * Abstract base class representing all possible authentication exceptions in Firebase Auth UI. @@ -93,6 +96,26 @@ abstract class AuthException( cause: Throwable? = null ) : AuthException(message, cause) + /** + * The account exists, but the sign-in method the user just attempted is not available on it. + * + * The attempt was well-formed and the backend answered definitively that this method cannot + * be used for this account, so the error is not recoverable: the error dialog offers no retry + * and the copy points the user at a different way to sign in. + * + * The type is general, but the copy is not. `ERROR_PASSKEY_ENROLLMENT_NOT_FOUND` is the only + * code routed here today and the dialog's fallback for a blank message is `errorPasskeyNotFound`. + * Routing a second code here means giving it its own string and making that fallback a + * per-method choice. + * + * @property message The detailed error message + * @property cause The underlying [Throwable] that caused this exception + */ + class SignInMethodUnavailableException( + message: String, + cause: Throwable? = null + ) : AuthException(message, cause) + /** * The user account does not exist. * @@ -129,11 +152,16 @@ abstract class AuthException( * This exception is thrown when GIdP password policy enforcement is enabled and the supplied * password fails one or more configured constraints (e.g. minimum length, missing uppercase). * - * [message] is a newline-separated, human-readable description of each failing constraint - * as returned by the server, suitable for direct display in the UI. + * [message] is a newline-separated, human-readable description of each failing constraint, + * suitable for direct display in the UI. Built by [from], each constraint is translated copy + * where the library recognises the server's sentence, and the server's own English where it + * does not. * - * @property message Human-readable description of the failing constraints - * @property failingRequirements The individual constraint strings from the server + * [failingRequirements] keeps the **raw** server sentences, untranslated, for hosts that + * render the constraints themselves rather than showing [message]. + * + * @property message Translated description of the failing constraints + * @property failingRequirements The raw, untranslated constraint strings from the server * @property cause The underlying [Throwable] that caused this exception */ class PasswordPolicyViolationException( @@ -256,6 +284,27 @@ abstract class AuthException( cause: Throwable? = null ) : AuthException(message, cause) + /** + * The Firebase project or the app is not set up for the operation that was attempted. + * + * Examples are a sign-in provider left disabled in the Firebase console, an unauthorized + * continue-URL domain, a missing SHA-1 certificate hash, and the reCAPTCHA and tenant + * families. None of these are anything the user can act on. + * + * [message] is generic translated copy, safe to render anywhere — the error dialog, an inline + * error on a screen, or a host's own `onSignInFailure`. The raw Firebase SDK diagnostic is + * untranslated but names the actual misconfiguration, so [from] keeps it on [cause] (the + * original [com.google.firebase.auth.FirebaseAuthException]): it stays in the stack trace and + * is reachable as `exception.cause?.message`. + * + * @property message Generic translated copy, safe to display + * @property cause The original Firebase exception, carrying the raw diagnostic for logs + */ + class MisconfigurationException( + message: String, + cause: Throwable? = null + ) : AuthException(message, cause) + /** * An unknown or unhandled error occurred. * @@ -359,14 +408,24 @@ abstract class AuthException( * This method maps known Firebase exception types to their corresponding [AuthException] * subtypes, providing a consistent exception hierarchy for error handling. * - * **Mapping:** - * - [FirebaseException] → [NetworkException] (for network-related errors) - * - [FirebaseAuthInvalidCredentialsException] → [InvalidCredentialsException] + * **Mapping**, in dispatch order. Several of these types extend one another, so the order + * is load-bearing rather than cosmetic: + * - [FirebaseAuthWeakPasswordException] → [WeakPasswordException], or + * [PasswordPolicyViolationException] when the diagnostic carries a GIdP password-policy + * rejection + * - [FirebaseAuthInvalidCredentialsException] → [InvalidCredentialsException], with the + * message selected by `errorCode`; the `errorCode`s in that family that are developer + * setup faults rather than user error (custom token, OIDC nonce, authenticator + * response) → [MisconfigurationException] * - [FirebaseAuthInvalidUserException] → [UserNotFoundException] - * - [FirebaseAuthWeakPasswordException] → [WeakPasswordException] + * - [FirebaseAuthActionCodeException] → [InvalidCredentialsException] * - [FirebaseAuthUserCollisionException] → [EmailAlreadyInUseException] - * - [FirebaseAuthException] with ERROR_TOO_MANY_REQUESTS → [TooManyRequestsException] * - [FirebaseAuthMultiFactorException] → [MfaRequiredException] + * - [FirebaseAuthRecentLoginRequiredException] → [InvalidCredentialsException] + * - [FirebaseAuthException] with a developer-setup `errorCode` → [MisconfigurationException] + * - [FirebaseTooManyRequestsException] → [TooManyRequestsException] + * - [FirebaseException] → [NetworkException] (for network-related errors), or + * [PasswordPolicyViolationException] when the message carries a policy rejection * - Other exceptions → [UnknownException] * * **Example:** @@ -379,13 +438,51 @@ abstract class AuthException( * } * ``` * + * Messages are resolved against [context]'s own configuration, so this overload honours + * neither a custom [AuthUIStringProvider] nor the `locale` a host configured. Prefer the + * [AuthUIStringProvider] overload wherever one is reachable, which inside an auth flow it + * always is, as `config.stringProvider`. This overload exists for the entry points that + * genuinely have no configuration to draw on, such as [FirebaseAuthUI.signOut], + * [FirebaseAuthUI.withReauth] and [FirebaseAuthUI.delete]. + * * @param firebaseException The Firebase exception to convert + * @param context Used to build a [DefaultAuthUIStringProvider] for the error messages * @return An appropriate [AuthException] subtype */ @JvmStatic fun from(firebaseException: Exception, context: Context): AuthException = from(firebaseException, DefaultAuthUIStringProvider(context)) + /** + * Creates an [AuthException] from [firebaseException], taking message text from + * [stringProvider] so it honours the host's configured strings and locale. + * + * This is the preferred overload; see the [Context] one above for the exception mapping + * table and an example. + * + * Given a non-null [stringProvider], the `message` on an exception returned by **this + * method** is library-owned translated copy, so it is safe to render directly. Each branch + * resolves in this order: the blank-able hook scoped to the exception type, then the + * string for the specific Firebase `errorCode`, then the corresponding generic recovery + * message, and only then the Firebase SDK's own untranslated message. + * [MisconfigurationException] never uses the SDK message at all — the raw diagnostic lives + * on `cause`. A `null` [stringProvider] has nothing to resolve against and falls back to + * the SDK message everywhere except [MisconfigurationException]. + * [PasswordPolicyViolationException] is partial by design: each requirement sentence the + * backend returns is translated when it is recognised and kept verbatim when it is not. + * + * The guarantee covers `from()` only. Subtypes constructed directly carry whatever + * `message` their caller passed, and the email-link subtypes + * ([InvalidEmailLinkException], [EmailLinkWrongDeviceException], + * [EmailLinkCrossDeviceLinkingException], [EmailLinkPromptForEmailException], + * [EmailLinkDifferentAnonymousUserException], [EmailMismatchException]) bake English into + * their own constructors. `getRecoveryMessage` keeps that out of the error dialog by + * resolving those types through [AuthUIStringProvider] instead of reading `message`. + * + * @param firebaseException The Firebase exception to convert + * @param stringProvider Supplies localized message text; pass `config.stringProvider` + * @return An appropriate [AuthException] subtype + */ @JvmStatic @JvmOverloads fun from(firebaseException: Exception, stringProvider: AuthUIStringProvider? = null): AuthException { @@ -399,18 +496,11 @@ abstract class AuthException( is FirebaseAuthWeakPasswordException -> { val sourceText = firebaseException.reason ?: firebaseException.message ?: "" if (sourceText.contains("PASSWORD_DOES_NOT_MEET_REQUIREMENTS", ignoreCase = true)) { - val requirements = parsePasswordPolicyRequirements(sourceText) - PasswordPolicyViolationException( - message = requirements.joinToString("\n").ifEmpty { - stringProvider?.errorWeakPasswordGeneric.nonEmpty() - ?: "Password does not meet policy requirements" - }, - failingRequirements = requirements, - cause = firebaseException - ) + passwordPolicyViolation(sourceText, firebaseException, stringProvider) } else { WeakPasswordException( message = stringProvider?.errorWeakPasswordGeneric.nonEmpty() + ?: stringProvider?.weakPasswordRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Password is too weak", cause = firebaseException, @@ -420,18 +510,168 @@ abstract class AuthException( } is FirebaseAuthInvalidCredentialsException -> { - InvalidCredentialsException( - message = stringProvider?.errorInvalidCredentials.nonEmpty() - ?: firebaseException.message - ?: "Invalid credentials provided", - cause = firebaseException - ) + // `errorInvalidCredentials` is the blank-able hook for the whole exception + // type, so it stays ahead of the per-code string in every branch. + val typeLevel = stringProvider?.errorInvalidCredentials.nonEmpty() + when (firebaseException.errorCode) { + // Under email enumeration protection the backend merges "wrong password" + // and "no such account" into this one code, so the copy cannot claim the + // password specifically. + "ERROR_INVALID_CREDENTIAL" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorIncorrectEmailOrPassword.nonEmpty() + ?: firebaseException.message + ?: "That email or password isn't correct", + cause = firebaseException + ) + + "ERROR_WRONG_PASSWORD" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.invalidPassword.nonEmpty() + ?: firebaseException.message + ?: "Incorrect password.", + cause = firebaseException + ) + + "ERROR_INVALID_EMAIL" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.invalidEmailAddress.nonEmpty() + ?: firebaseException.message + ?: "That email address isn't correct", + cause = firebaseException + ) + + "ERROR_MISSING_EMAIL" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.missingEmailAddress.nonEmpty() + ?: firebaseException.message + ?: "Enter your email address to continue", + cause = firebaseException + ) + + "ERROR_MISSING_PASSWORD", + "ERROR_MISSING_VERIFICATION_CODE" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.requiredField.nonEmpty() + ?: firebaseException.message + ?: "You can't leave this empty.", + cause = firebaseException + ) + + "ERROR_INVALID_PHONE_NUMBER" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.invalidPhoneNumber.nonEmpty() + ?: firebaseException.message + ?: "Enter a valid phone number", + cause = firebaseException + ) + + "ERROR_MISSING_PHONE_NUMBER" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.missingPhoneNumber.nonEmpty() + ?: firebaseException.message + ?: "You can't leave this empty.", + cause = firebaseException + ) + + "ERROR_INVALID_VERIFICATION_CODE" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.invalidVerificationCode.nonEmpty() + ?: firebaseException.message + ?: "Wrong code. Try again.", + cause = firebaseException + ) + + "ERROR_SESSION_EXPIRED" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorSessionExpired.nonEmpty() + ?: firebaseException.message + ?: "This code is no longer valid", + cause = firebaseException + ) + + "ERROR_INVALID_VERIFICATION_ID", + "ERROR_MISSING_VERIFICATION_ID" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorInvalidVerificationId.nonEmpty() + ?: firebaseException.message + ?: "That verification session is no longer valid. Request a new code.", + cause = firebaseException + ) + + "ERROR_RETRY_PHONE_AUTH" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorRetryPhoneAuth.nonEmpty() + ?: firebaseException.message + ?: "Phone verification didn't complete. Try again.", + cause = firebaseException + ) + + "ERROR_USER_MISMATCH" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorUserMismatch.nonEmpty() + ?: firebaseException.message + ?: "Those credentials belong to a different account.", + cause = firebaseException + ) + + "ERROR_PHONE_NUMBER_NOT_FOUND", + "ERROR_MULTI_FACTOR_INFO_NOT_FOUND", + "ERROR_MISSING_MULTI_FACTOR_INFO" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorPhoneNumberNotEnrolled.nonEmpty() + ?: firebaseException.message + ?: "That phone number isn't set up for verification on this account.", + cause = firebaseException + ) + + "ERROR_INVALID_MULTI_FACTOR_SESSION", + "ERROR_MISSING_MULTI_FACTOR_SESSION" -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.errorMultiFactorSessionExpired.nonEmpty() + ?: firebaseException.message + ?: "Your sign-in session expired. Sign in again to continue.", + cause = firebaseException + ) + + // Custom tokens are minted by the developer's own backend; the SDK's + // diagnostic names the setup problem and means nothing to the user, so it + // stays on `cause` while `message` carries renderable generic copy. + "ERROR_INVALID_CUSTOM_TOKEN", + "ERROR_CUSTOM_TOKEN_MISMATCH", + // Same shape: the app built the federated request wrong. + "ERROR_MISSING_OR_INVALID_NONCE", + "ERROR_INVALID_AUTHENTICATOR_RESPONSE" -> MisconfigurationException( + message = stringProvider?.unknownErrorRecoveryMessage.nonEmpty() + ?: "An unknown error occurred.", + cause = firebaseException + ) + + // Not InvalidCredentialsException, so its `typeLevel` hook is skipped too. + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND" -> SignInMethodUnavailableException( + message = stringProvider?.errorPasskeyNotFound.nonEmpty() + ?: firebaseException.message + ?: "We couldn't find a passkey for this account. " + + "Sign in another way.", + cause = firebaseException + ) + + // Unrecognised codes stay recoverable, but the copy must stay generic. + else -> InvalidCredentialsException( + message = typeLevel + ?: stringProvider?.unknownErrorRecoveryMessage.nonEmpty() + ?: firebaseException.message + ?: "Invalid credentials provided", + cause = firebaseException + ) + } } is FirebaseAuthInvalidUserException -> { when (firebaseException.errorCode) { "ERROR_USER_NOT_FOUND" -> UserNotFoundException( message = stringProvider?.errorUserNotFound.nonEmpty() + ?: stringProvider?.userNotFoundRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "User not found", cause = firebaseException @@ -444,8 +684,18 @@ abstract class AuthException( cause = firebaseException ) + "ERROR_INVALID_USER_TOKEN", + "ERROR_USER_TOKEN_EXPIRED" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorMultiFactorSessionExpired.nonEmpty() + ?: firebaseException.message + ?: "Your sign-in session expired. Sign in again to continue.", + cause = firebaseException + ) + else -> UserNotFoundException( message = stringProvider?.errorUserAccountGeneric.nonEmpty() + ?: stringProvider?.userNotFoundRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "User account error", cause = firebaseException @@ -453,10 +703,36 @@ abstract class AuthException( } } + // Must precede the plain FirebaseAuthException arm, which it extends. + is FirebaseAuthActionCodeException -> { + when (firebaseException.errorCode) { + // The type-level hook is the one scoped to the exception this produces — + // `errorInvalidCredentials`. Using `errorUnknownAuth` here would let a + // host customising the unknown-error copy silently lose this string. + "ERROR_EXPIRED_ACTION_CODE", + "ERROR_INVALID_ACTION_CODE" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorActionCodeInvalid.nonEmpty() + ?: firebaseException.message + ?: "That link is no longer valid. Request a new one.", + cause = firebaseException + ) + + else -> UnknownException( + message = stringProvider?.errorUnknownAuth.nonEmpty() + ?: stringProvider?.unknownErrorRecoveryMessage.nonEmpty() + ?: firebaseException.message + ?: "An unknown authentication error occurred", + cause = firebaseException + ) + } + } + is FirebaseAuthUserCollisionException -> { when (firebaseException.errorCode) { "ERROR_EMAIL_ALREADY_IN_USE" -> EmailAlreadyInUseException( message = stringProvider?.errorEmailAlreadyInUse.nonEmpty() + ?: stringProvider?.emailAlreadyInUseRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Email address is already in use", cause = firebaseException, @@ -465,6 +741,7 @@ abstract class AuthException( "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL" -> AccountLinkingRequiredException( message = stringProvider?.errorAccountExistsDifferentCredential.nonEmpty() + ?: stringProvider?.accountLinkingRequiredRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Account already exists with different credentials", cause = firebaseException @@ -472,6 +749,7 @@ abstract class AuthException( "ERROR_CREDENTIAL_ALREADY_IN_USE" -> AccountLinkingRequiredException( message = stringProvider?.errorCredentialAlreadyInUse.nonEmpty() + ?: stringProvider?.accountLinkingRequiredRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Credential is already associated with a different user account", cause = firebaseException @@ -479,6 +757,7 @@ abstract class AuthException( else -> AccountLinkingRequiredException( message = stringProvider?.errorAccountCollisionGeneric.nonEmpty() + ?: stringProvider?.accountLinkingRequiredRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Account collision error", cause = firebaseException @@ -489,6 +768,7 @@ abstract class AuthException( is FirebaseAuthMultiFactorException -> { MfaRequiredException( message = stringProvider?.errorMfaRequiredFallback.nonEmpty() + ?: stringProvider?.mfaRequiredRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Multi-factor authentication required", cause = firebaseException @@ -496,8 +776,10 @@ abstract class AuthException( } is FirebaseAuthRecentLoginRequiredException -> { + // `errorRecentLoginRequired` ships blank; the MFA string says the same thing. InvalidCredentialsException( message = stringProvider?.errorRecentLoginRequired.nonEmpty() + ?: stringProvider?.mfaErrorRecentLoginRequired.nonEmpty() ?: firebaseException.message ?: "Recent login required for this operation", cause = firebaseException @@ -506,23 +788,97 @@ abstract class AuthException( is FirebaseAuthException -> { when (firebaseException.errorCode) { - "ERROR_TOO_MANY_REQUESTS" -> TooManyRequestsException( - message = stringProvider?.errorTooManyRequests.nonEmpty() + // FirebaseAuthWebException code for backing out of the OAuth custom tab, + // and the Credential Manager / Play services equivalent. + "ERROR_WEB_CONTEXT_CANCELED", + "ERROR_USER_CANCELLED" -> AuthCancelledException( + message = stringProvider?.errorAuthCancelled.nonEmpty() + ?: stringProvider?.authCancelledRecoveryMessage.nonEmpty() + ?: firebaseException.message + ?: "Authentication was cancelled", + cause = firebaseException + ) + + // These three produce InvalidCredentialsException, so the type-level hook + // is `errorInvalidCredentials`. `errorUnknownAuth` would let a host that + // customises only the unknown-error copy lose all three specific strings. + "ERROR_UNVERIFIED_EMAIL" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorUnverifiedEmail.nonEmpty() ?: firebaseException.message - ?: "Too many requests. Please try again later", + ?: "Verify your email address before you continue.", cause = firebaseException ) - // FirebaseAuthWebException code for backing out of the OAuth custom tab - "ERROR_WEB_CONTEXT_CANCELED" -> AuthCancelledException( - message = stringProvider?.errorAuthCancelled.nonEmpty() + "ERROR_SECOND_FACTOR_ALREADY_ENROLLED" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorSecondFactorAlreadyEnrolled.nonEmpty() ?: firebaseException.message - ?: "Authentication was cancelled", + ?: "That verification method is already set up on this account.", + cause = firebaseException + ) + + "ERROR_MAXIMUM_SECOND_FACTOR_COUNT_EXCEEDED" -> InvalidCredentialsException( + message = stringProvider?.errorInvalidCredentials.nonEmpty() + ?: stringProvider?.errorMaximumSecondFactorCountExceeded.nonEmpty() + ?: firebaseException.message + ?: "You've reached the limit for verification methods on this account.", + cause = firebaseException + ) + + // Developer setup problems. The user can do nothing about any of them, so + // `message` carries generic translated copy and the raw Firebase + // diagnostic is kept on `cause` for logs. INTERNAL_ERROR and + // ERROR_WEB_INTERNAL_ERROR are deliberately absent — they are backend + // faults, not configuration. + "ERROR_OPERATION_NOT_ALLOWED", + "ERROR_APP_NOT_AUTHORIZED", + "ERROR_UNAUTHORIZED_DOMAIN", + "ERROR_MISSING_CONTINUE_URI", + "ERROR_INVALID_CERT_HASH", + "ERROR_DYNAMIC_LINK_NOT_ACTIVATED", + "ERROR_INVALID_DYNAMIC_LINK_DOMAIN", + "ERROR_INVALID_HOSTING_LINK_DOMAIN", + "ERROR_INVALID_PROVIDER_ID", + "ERROR_ADMIN_RESTRICTED_OPERATION", + "ERROR_UNSUPPORTED_FIRST_FACTOR", + "ERROR_UNSUPPORTED_PASSTHROUGH_OPERATION", + "ERROR_INVALID_REQ_TYPE", + "ERROR_WEB_CONTEXT_ALREADY_PRESENTED", + // Tenant family + "ERROR_INVALID_TENANT_ID", + "ERROR_TENANT_ID_MISMATCH", + "ERROR_UNSUPPORTED_TENANT_OPERATION", + // reCAPTCHA / app verification family + "ERROR_RECAPTCHA_NOT_ENABLED", + "ERROR_CAPTCHA_CHECK_FAILED", + "ERROR_MISSING_RECAPTCHA_TOKEN", + "ERROR_INVALID_RECAPTCHA_TOKEN", + "ERROR_INVALID_RECAPTCHA_ACTION", + "ERROR_MISSING_RECAPTCHA_VERSION", + "ERROR_INVALID_RECAPTCHA_VERSION", + "ERROR_MISSING_CLIENT_TYPE", + "ERROR_MISSING_CLIENT_IDENTIFIER", + "ERROR_ALTERNATE_CLIENT_IDENTIFIER_REQUIRED", + // Email-template settings in the Firebase console. These arrive as + // FirebaseAuthEmailException, which extends FirebaseAuthException + // directly and so lands in this arm. + "ERROR_INVALID_MESSAGE_PAYLOAD", + "ERROR_INVALID_SENDER", + "ERROR_INVALID_RECIPIENT_EMAIL", + // Host integration and project quota. ERROR_MISSING_ACTIVITY ships: it is + // declared on the Recaptcha-activity exception, not in the SDK code table. + "ERROR_MISSING_ACTIVITY", + "ERROR_WEB_STORAGE_UNSUPPORTED", + "ERROR_QUOTA_EXCEEDED" -> MisconfigurationException( + message = stringProvider?.unknownErrorRecoveryMessage.nonEmpty() + ?: "An unknown error occurred.", cause = firebaseException ) else -> UnknownException( message = stringProvider?.errorUnknownAuth.nonEmpty() + ?: stringProvider?.unknownErrorRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "An unknown authentication error occurred", cause = firebaseException @@ -530,21 +886,27 @@ abstract class AuthException( } } + // Rate limiting arrives as a plain FirebaseTooManyRequestsException, which is NOT a + // FirebaseAuthException and carries no error code. Without this arm it falls to the + // FirebaseException branch below and a throttled user is told they are offline. + is FirebaseTooManyRequestsException -> { + TooManyRequestsException( + message = stringProvider?.errorTooManyRequests.nonEmpty() + ?: stringProvider?.tooManyRequestsRecoveryMessage.nonEmpty() + ?: firebaseException.message + ?: "Too many requests. Please try again later", + cause = firebaseException + ) + } + is FirebaseException -> { val msg = firebaseException.message ?: "" if (msg.contains("PASSWORD_DOES_NOT_MEET_REQUIREMENTS", ignoreCase = true)) { - val requirements = parsePasswordPolicyRequirements(msg) - PasswordPolicyViolationException( - message = requirements.joinToString("\n").ifEmpty { - stringProvider?.errorWeakPasswordGeneric.nonEmpty() - ?: "Password does not meet policy requirements" - }, - failingRequirements = requirements, - cause = firebaseException - ) + passwordPolicyViolation(msg, firebaseException, stringProvider) } else { NetworkException( message = stringProvider?.errorNetworkGeneric.nonEmpty() + ?: stringProvider?.networkErrorRecoveryMessage.nonEmpty() ?: msg.ifEmpty { "Network error occurred" }, cause = firebaseException ) @@ -557,6 +919,7 @@ abstract class AuthException( ) { AuthCancelledException( message = stringProvider?.errorAuthCancelled.nonEmpty() + ?: stringProvider?.authCancelledRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "Authentication was cancelled", cause = firebaseException @@ -564,6 +927,7 @@ abstract class AuthException( } else { UnknownException( message = stringProvider?.errorUnknownAuth.nonEmpty() + ?: stringProvider?.unknownErrorRecoveryMessage.nonEmpty() ?: firebaseException.message ?: "An unknown error occurred", cause = firebaseException @@ -575,6 +939,89 @@ abstract class AuthException( private fun String?.nonEmpty(): String? = this?.ifEmpty { null } + /** + * Builds the [PasswordPolicyViolationException] for a GIdP password-policy rejection: + * `message` is localized one requirement sentence at a time, while `failingRequirements` + * keeps the backend's raw sentences. + */ + private fun passwordPolicyViolation( + sourceText: String, + cause: Exception, + stringProvider: AuthUIStringProvider? + ): PasswordPolicyViolationException { + val requirements = parsePasswordPolicyRequirements(sourceText) + return PasswordPolicyViolationException( + message = requirements + .joinToString("\n") { localizePasswordRequirement(it, stringProvider) ?: it } + .ifEmpty { + // `errorWeakPasswordGeneric` is the host's hook and ships blank. + stringProvider?.errorWeakPasswordGeneric.nonEmpty() + ?: stringProvider?.errorPasswordPolicyGeneric.nonEmpty() + ?: "Password does not meet policy requirements" + }, + failingRequirements = requirements, + cause = cause + ) + } + + /** + * Translates one GIdP password-policy requirement sentence, or returns `null` when the + * sentence is not recognised. + * + * The sentences are the backend's own English, e.g. "Password must contain at least 10 + * characters". On `null` the caller keeps that sentence verbatim, so a reworded or newly + * added requirement degrades to untranslated English rather than to a wrong message. + */ + private fun localizePasswordRequirement( + requirement: String, + stringProvider: AuthUIStringProvider? + ): String? { + if (stringProvider == null) return null + // GIdP writes "upper case" and "lower case" as two words; one word is accepted too. + val text = requirement.lowercase(Locale.ROOT) + return when { + text.contains("upper case") || text.contains("uppercase") -> + stringProvider.passwordMissingUppercase.nonEmpty() + + text.contains("lower case") || text.contains("lowercase") -> + stringProvider.passwordMissingLowercase.nonEmpty() + + // "numeric" is a substring of "non-alphanumeric": the guard keeps the two arms + // disjoint regardless of the order they are tested in. + text.contains("numeric") && !text.contains("non-alphanumeric") -> + stringProvider.passwordMissingDigit.nonEmpty() + + // Unverified wording: the probe project had special characters disabled. + text.contains("non-alphanumeric") || text.contains("special character") -> + stringProvider.passwordMissingSpecialCharacter.nonEmpty() + + // The number is the project's own configured minimum, so it is read out of the + // sentence rather than assumed. + text.contains("at least") -> + firstNumberIn(requirement)?.let { + stringProvider.passwordTooShort(it).nonEmpty() + } + + // "fewer than N" is exclusive, so the maximum passwordTooLong states is N - 1. + // Unverified wording: the probe project left the maximum at its 4096 default. + text.contains("fewer than") -> + firstNumberIn(requirement)?.let { + stringProvider.passwordTooLong(it - 1).nonEmpty() + } + + // "at most N" and "no more than N" are inclusive, so N is the maximum as written. + text.contains("at most") || text.contains("no more than") -> + firstNumberIn(requirement)?.let { + stringProvider.passwordTooLong(it).nonEmpty() + } + + else -> null + } + } + + private fun firstNumberIn(text: String): Int? = + Regex("\\d+").find(text)?.value?.toIntOrNull() + // Finds the [...] content that immediately follows PASSWORD_DOES_NOT_MEET_REQUIREMENTS // in both FirebaseException and FirebaseAuthWeakPasswordException messages. // GIdP returns human-readable requirement strings inside those brackets, e.g. diff --git a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt index 1978eabde0..e321ffcf21 100644 --- a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt @@ -20,10 +20,12 @@ import androidx.annotation.MainThread import androidx.annotation.RestrictTo import com.firebase.ui.auth.configuration.AuthUIConfiguration import com.firebase.ui.auth.configuration.auth_provider.AuthProvider +import com.firebase.ui.auth.configuration.auth_provider.Provider import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException import com.firebase.ui.auth.configuration.auth_provider.signOutFromFacebook import com.firebase.ui.auth.configuration.auth_provider.signOutFromGoogle import com.firebase.ui.auth.ui.screens.reauth.toReauthConfiguration +import com.firebase.ui.auth.util.ProviderAvailability import com.google.firebase.Firebase import com.google.firebase.FirebaseApp import com.google.firebase.auth.AuthResult @@ -400,6 +402,11 @@ class FirebaseAuthUI private constructor( * to reflect the change. The operation is performed asynchronously and will emit * appropriate states during the process. * + * It also clears the session held by any social provider linked to the account, so the next + * sign-in starts clean: Google's saved credential state is cleared, meaning the account picker + * is shown again instead of silently re-selecting the previous account, and any Facebook + * session is logged out. Failures there are logged and do not fail the sign-out. + * * **Example:** * ```kotlin * val authUI = FirebaseAuthUI.getInstance() @@ -431,21 +438,39 @@ class FirebaseAuthUI private constructor( // Update state to loading updateAuthState(AuthState.Loading(context.getString(R.string.fui_loading_signing_out))) + // Capture the linked providers before signing out: `auth.signOut()` clears + // `currentUser`, and `FirebaseUser.providerId` is always "firebase" — the + // per-provider ids live in `providerData`. + val linkedProviderIds = auth.currentUser?.providerData + ?.map { it.providerId } + .orEmpty() + // Sign out from Firebase Auth auth.signOut() - .also { - signOutFromGoogle( - auth = auth, - context = context, - credentialManagerProvider = testCredentialManagerProvider - ?: AuthProvider.Google.DefaultCredentialManagerProvider(), - ) - signOutFromFacebook( - auth = auth, - loginManagerProvider = testLoginManagerProvider - ?: AuthProvider.Facebook.DefaultLoginManagerProvider(), - ) - } + + // Clear the provider-side session for each provider linked to the account. This is + // the linked set rather than the provider used for this session, so it can clear a + // little more than strictly necessary — cheap either way, and it never leaves a + // provider session behind. + if (Provider.GOOGLE.id in linkedProviderIds) { + signOutFromGoogle( + context = context, + credentialManagerProvider = testCredentialManagerProvider + ?: AuthProvider.Google.DefaultCredentialManagerProvider(), + ) + } + // Facebook is a `compileOnly` dependency, so an app that doesn't offer Facebook + // sign-in has no Facebook SDK at runtime — and `providerData` can still carry + // `facebook.com` for an account linked on another platform. Touching the Facebook + // extensions at all links the SDK, so the classpath probe, not the account, decides. + if (Provider.FACEBOOK.id in linkedProviderIds && + ProviderAvailability.IS_FACEBOOK_AVAILABLE + ) { + signOutFromFacebook( + loginManagerProvider = testLoginManagerProvider + ?: AuthProvider.Facebook.DefaultLoginManagerProvider(), + ) + } // Update state to idle (user signed out) updateAuthState(AuthState.Idle) diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt index 3c5f2df498..4f9fe0d891 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt @@ -22,7 +22,6 @@ import kotlinx.coroutines.tasks.await internal fun AuthFlowScope.rememberAnonymousSignInHandler( onSignInFailure: (AuthException) -> Unit = {}, ): () -> Unit { - val context = androidx.compose.ui.platform.LocalContext.current val coroutineScope = rememberCoroutineScope() return { coroutineScope.launch { @@ -33,7 +32,7 @@ internal fun AuthFlowScope.rememberAnonymousSignInHandler( emit(AuthState.Error(e)) if (e !is AuthException.AuthCancelledException) onSignInFailure(e) } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) if (authException !is AuthException.AuthCancelledException) onSignInFailure(authException) } @@ -63,7 +62,7 @@ internal suspend fun AuthFlowScope.signInAnonymously() { emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt index bb1d277926..ea7e7e707a 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt @@ -192,7 +192,7 @@ internal suspend fun AuthFlowScope.createOrLinkUserWithEmailAndPassword( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } @@ -351,7 +351,7 @@ internal suspend fun AuthFlowScope.signInWithEmailAndPassword( throw e } catch (e: Exception) { val authException = recoverLegacyDifferentSignInMethod(email, e) - ?: AuthException.from(e, context) + ?: AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } @@ -365,7 +365,7 @@ private suspend fun AuthFlowScope.recoverLegacyDifferentSignInMethod( return null } - val authException = AuthException.from(cause) + val authException = AuthException.from(cause, config.stringProvider) if (authException !is AuthException.InvalidCredentialsException && authException !is AuthException.UserNotFoundException) { return null @@ -500,7 +500,7 @@ internal suspend fun AuthFlowScope.signInAndLinkWithCredential( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } @@ -565,7 +565,7 @@ internal suspend fun AuthFlowScope.sendSignInLinkToEmail( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } @@ -702,7 +702,7 @@ internal suspend fun AuthFlowScope.signInWithEmailLink( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } @@ -824,7 +824,7 @@ internal suspend fun AuthFlowScope.sendPasswordResetEmail( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt index 0a8a7212be..591ae68b3d 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt @@ -14,7 +14,6 @@ package com.firebase.ui.auth.configuration.auth_provider -import com.google.firebase.auth.FirebaseAuth import android.content.Context import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult @@ -95,7 +94,7 @@ internal fun AuthFlowScope.rememberSignInWithFacebookLauncher( currentScope.emit(AuthState.Error(e)) if (e !is AuthException.AuthCancelledException) currentOnSignInFailure(e) } catch (e: Exception) { - val authException = AuthException.from(e, currentContext) + val authException = AuthException.from(e, currentScope.config.stringProvider) currentScope.emit(AuthState.Error(authException)) if (authException !is AuthException.AuthCancelledException) currentOnSignInFailure(authException) } @@ -108,7 +107,7 @@ internal fun AuthFlowScope.rememberSignInWithFacebookLauncher( override fun onError(error: FacebookException) { Log.e("FacebookAuthProvider", "Error during Facebook sign in", error) - val authException = AuthException.from(error, currentContext) + val authException = AuthException.from(error, currentScope.config.stringProvider) currentScope.emit( AuthState.Error( authException @@ -203,7 +202,7 @@ internal suspend fun AuthFlowScope.signInWithFacebook( emit(AuthState.Error(e)) throw e } catch (e: FacebookException) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } catch (e: CancellationException) { @@ -217,29 +216,28 @@ internal suspend fun AuthFlowScope.signInWithFacebook( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } } /** - * Signs out the current user from Facebook. + * Logs the user out of their Facebook session via Facebook's LoginManager. * - * Invokes Facebook's LoginManager to log out the user from their Facebook session. - * This method silently catches and ignores any exceptions that may occur during the - * logout process to ensure the sign-out flow continues even if Facebook logout fails. + * Best-effort: failures are logged and swallowed so sign-out continues. [LinkageError] is caught + * alongside [Exception] because the Facebook SDK is `compileOnly`, so an absent or mismatched SDK + * surfaces as an error rather than an exception. * - * This is typically called as part of the overall sign-out flow when a user signs out - * from Firebase Authentication. + * The caller decides whether Facebook applies; reaching this function at all links the SDK. */ internal fun signOutFromFacebook( - auth: FirebaseAuth, loginManagerProvider: AuthProvider.Facebook.LoginManagerProvider = AuthProvider.Facebook.DefaultLoginManagerProvider(), ) { try { - if (Provider.fromId(auth.currentUser?.providerId) != Provider.FACEBOOK) return loginManagerProvider.logOut() + } catch (e: LinkageError) { + Log.e("FacebookAuthProvider", "Facebook SDK not available or mismatched", e) } catch (e: Exception) { Log.e("FacebookAuthProvider", "Error during Facebook sign out", e) } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt index 96ffbb79f3..873be28da2 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt @@ -1,6 +1,5 @@ package com.firebase.ui.auth.configuration.auth_provider -import com.google.firebase.auth.FirebaseAuth import android.content.Context import android.util.Log import androidx.compose.runtime.Composable @@ -38,7 +37,7 @@ internal fun AuthFlowScope.rememberGoogleSignInHandler( emit(AuthState.Error(e)) if (e !is AuthException.AuthCancelledException) onSignInFailure(e) } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) if (authException !is AuthException.AuthCancelledException) onSignInFailure(authException) } @@ -73,7 +72,7 @@ internal suspend fun AuthFlowScope.signInWithGoogle( authorizationProvider.authorize(context, requestedScopes) } catch (e: Exception) { // Continue with sign-in even if scope authorization fails - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) } } @@ -193,7 +192,7 @@ internal suspend fun AuthFlowScope.signInWithGoogle( throw e } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } @@ -214,19 +213,25 @@ internal suspend fun AuthFlowScope.signInWithGoogle( * **Note:** This does not sign out from Firebase Auth itself. Call [com.firebase.ui.auth.FirebaseAuthUI.signOut] * separately if you need to sign out from Firebase. * + * Callers are responsible for deciding whether Google is involved at all — this function does not + * check the signed-in user, and [com.firebase.ui.auth.FirebaseAuthUI.signOut] has already cleared + * it by the time it calls here. + * * @param context Android context for Credential Manager */ internal suspend fun signOutFromGoogle( - auth: FirebaseAuth, context: Context, credentialManagerProvider: AuthProvider.Google.CredentialManagerProvider = AuthProvider.Google.DefaultCredentialManagerProvider(), ) { try { - if (Provider.fromId(auth.currentUser?.providerId) != Provider.GOOGLE) return credentialManagerProvider.clearCredentialState( context = context, credentialManager = CredentialManager.create(context) ) + } catch (e: CancellationException) { + // Must not be swallowed: this suspends, so cancellation has to reach the caller for + // FirebaseAuthUI.signOut to report it rather than emitting Idle for a half-done sign-out. + throw e } catch (e: Exception) { Log.e("GoogleAuthProvider", "Error during Google sign out", e) } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt index 1b5fd3db8d..86e40f3d11 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt @@ -49,7 +49,7 @@ internal fun AuthFlowScope.rememberOAuthSignInHandler( emit(AuthState.Error(e)) if (e !is AuthException.AuthCancelledException) onSignInFailure(e) } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) if (authException !is AuthException.AuthCancelledException) onSignInFailure(authException) } @@ -191,7 +191,7 @@ internal suspend fun AuthFlowScope.signInWithProvider( throw e } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt index d7c6757a2a..bfbf387747 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt @@ -68,7 +68,7 @@ internal suspend fun AuthFlowScope.verifyPhoneNumber( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } @@ -107,7 +107,7 @@ internal suspend fun AuthFlowScope.submitVerificationCode( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } @@ -161,7 +161,7 @@ internal suspend fun AuthFlowScope.signInWithPhoneAuthCredential( emit(AuthState.Error(e)) throw e } catch (e: Exception) { - val authException = AuthException.from(e, context) + val authException = AuthException.from(e, config.stringProvider) emit(AuthState.Error(authException)) throw authException } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt index 7e1c3698d6..58d0e4b79a 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt @@ -184,6 +184,9 @@ interface AuthUIStringProvider { /** Error message when password doesn't meet minimum length requirement. Should support string formatting with minimum length parameter. */ fun passwordTooShort(minimumLength: Int): String + /** Error message when the password is longer than the maximum length allowed. Should support string formatting with maximum length parameter. */ + fun passwordTooLong(maximumLength: Int): String + /** Error message when password is missing at least one uppercase letter (A-Z) */ val passwordMissingUppercase: String @@ -628,4 +631,62 @@ interface AuthUIStringProvider { /** Error when authentication is cancelled. Return empty to use the Firebase SDK message. */ val errorAuthCancelled: String + + // ============================================================================================= + // AuthException messages selected by Firebase Auth error code + // + // Every member below has a default so that adding one is not a breaking change, and each + // default delegates to a coarser member rather than returning a hardcoded English literal — + // a host that has implemented this interface itself keeps getting its own translated copy. + // ============================================================================================= + + /** Error when sign-in fails and the server will not say whether the email or the password was wrong. */ + val errorIncorrectEmailOrPassword: String get() = errorInvalidCredentials + + /** Error when the SMS verification session is gone and a new code has to be requested. */ + val errorInvalidVerificationId: String get() = errorInvalidCredentials + + /** Error when phone verification did not complete and has to be retried. */ + val errorRetryPhoneAuth: String get() = errorInvalidCredentials + + /** Error when the supplied credentials belong to a different account than the one being confirmed. */ + val errorUserMismatch: String get() = errorUnknownAuth + + /** Error when the phone number is not set up as a verification method on the account. */ + val errorPhoneNumberNotEnrolled: String get() = errorInvalidCredentials + + /** Error when a sign-in or verification session has expired. */ + val errorSessionExpired: String get() = errorInvalidCredentials + + /** Error when the sign-in session expired part-way through two-step verification. */ + val errorMultiFactorSessionExpired: String get() = errorSessionExpired + + /** Error when an emailed sign-in or password reset link has expired or is malformed. */ + val errorActionCodeInvalid: String get() = errorInvalidCredentials + + /** Error when the account email has to be verified before the operation can continue. */ + val errorUnverifiedEmail: String get() = errorUnknownAuth + + /** Error when adding a verification method that is already set up on the account. */ + val errorSecondFactorAlreadyEnrolled: String get() = errorUnknownAuth + + /** Error when the account already has the maximum number of verification methods. */ + val errorMaximumSecondFactorCountExceeded: String get() = errorUnknownAuth + + /** + * Error when the password fails the project's password policy and the server named no + * individual requirement. When the server does name them, each one is rendered through + * [passwordTooShort], [passwordTooLong], [passwordMissingUppercase], + * [passwordMissingLowercase], [passwordMissingDigit] and [passwordMissingSpecialCharacter] + * instead, and this string is not used. + */ + val errorPasswordPolicyGeneric: String get() = errorWeakPasswordGeneric + + /** + * Error when the account has no passkey enrolled and the user has to sign in another way. + * + * Defaults to [errorUnknownAuth], not [errorInvalidCredentials]: this message is shown for a + * non-recoverable error, so the dialog offers no retry and credential copy would contradict it. + */ + val errorPasskeyNotFound: String get() = errorUnknownAuth } diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt index e25776a76b..9279d3c49a 100644 --- a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt +++ b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt @@ -26,6 +26,10 @@ class DefaultAuthUIStringProvider( /** * Allows overriding locale. */ + // AppBundleLocaleChanges tells app modules to pair a dynamic locale change with a Play Core + // language download. A library cannot: the bundle configuration and any Play Core dependency + // belong to the app that embeds us, so there is nothing here to fix. + @Suppress("AppBundleLocaleChanges") private val localizedContext = locale?.let { locale -> context.createConfigurationContext( Configuration(context.resources.configuration).apply { @@ -155,6 +159,9 @@ class DefaultAuthUIStringProvider( override fun passwordTooShort(minimumLength: Int): String = localizedContext.getString(R.string.fui_error_password_too_short, minimumLength) + override fun passwordTooLong(maximumLength: Int): String = + localizedContext.getString(R.string.fui_error_password_too_long, maximumLength) + override val passwordMissingUppercase: String get() = localizedContext.getString(R.string.fui_error_password_missing_uppercase) override val passwordMissingLowercase: String @@ -567,4 +574,43 @@ class DefaultAuthUIStringProvider( override val errorAuthCancelled: String get() = localizedContext.getString(R.string.fui_error_auth_cancelled) + + override val errorIncorrectEmailOrPassword: String + get() = localizedContext.getString(R.string.fui_error_incorrect_email_or_password) + + override val errorInvalidVerificationId: String + get() = localizedContext.getString(R.string.fui_error_invalid_verification_id) + + override val errorRetryPhoneAuth: String + get() = localizedContext.getString(R.string.fui_error_retry_phone_auth) + + override val errorUserMismatch: String + get() = localizedContext.getString(R.string.fui_error_user_mismatch) + + override val errorPhoneNumberNotEnrolled: String + get() = localizedContext.getString(R.string.fui_error_phone_number_not_enrolled) + + override val errorSessionExpired: String + get() = localizedContext.getString(R.string.fui_error_session_expired) + + override val errorMultiFactorSessionExpired: String + get() = localizedContext.getString(R.string.fui_error_multi_factor_session_expired) + + override val errorActionCodeInvalid: String + get() = localizedContext.getString(R.string.fui_error_action_code_invalid) + + override val errorUnverifiedEmail: String + get() = localizedContext.getString(R.string.fui_error_unverified_email) + + override val errorSecondFactorAlreadyEnrolled: String + get() = localizedContext.getString(R.string.fui_error_second_factor_already_enrolled) + + override val errorMaximumSecondFactorCountExceeded: String + get() = localizedContext.getString(R.string.fui_error_maximum_second_factor_count_exceeded) + + override val errorPasswordPolicyGeneric: String + get() = localizedContext.getString(R.string.fui_error_password_policy_generic) + + override val errorPasskeyNotFound: String + get() = localizedContext.getString(R.string.fui_error_passkey_not_found) } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt index ddb18c6426..2b4e90a768 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt @@ -143,19 +143,27 @@ internal fun getRecoveryMessage( stringProvider: AuthUIStringProvider ): String { return when (error) { - is AuthException.NetworkException -> stringProvider.networkErrorRecoveryMessage + // AuthException.from already puts generic translated copy on the message and keeps the + // raw diagnostic on the cause, so this arm is belt-and-braces: an instance constructed + // directly with a raw diagnostic still cannot leak it into the dialog. + is AuthException.MisconfigurationException -> stringProvider.unknownErrorRecoveryMessage + is AuthException.NetworkException -> + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.networkErrorRecoveryMessage is AuthException.InvalidCredentialsException -> { - // Use the actual error message from Firebase if available, otherwise fallback to generic message - error.message?.takeIf { it.isNotBlank() && it != "Invalid credentials provided" } + // AuthException.from now picks library-owned copy per Firebase error code, so the + // message is the specific one; the generic string is only the empty-message fallback. + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.invalidCredentialsRecoveryMessage } - is AuthException.UserNotFoundException -> stringProvider.userNotFoundRecoveryMessage + is AuthException.SignInMethodUnavailableException -> + // Passkey-specific fallback behind a general type — see the exception's KDoc. + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.errorPasskeyNotFound + is AuthException.UserNotFoundException -> + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.userNotFoundRecoveryMessage is AuthException.WeakPasswordException -> { - // Include specific reason if available - val baseMessage = stringProvider.weakPasswordRecoveryMessage - error.reason?.let { reason -> - "$baseMessage\n\nReason: $reason" - } ?: baseMessage + // `error.reason` is untranslated SDK text, so it is deliberately not appended. + error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.weakPasswordRecoveryMessage } is AuthException.PasswordPolicyViolationException -> { @@ -165,24 +173,30 @@ internal fun getRecoveryMessage( is AuthException.EmailAlreadyInUseException -> { // Include email if available - val baseMessage = stringProvider.emailAlreadyInUseRecoveryMessage + val baseMessage = error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.emailAlreadyInUseRecoveryMessage error.email?.let { email -> "$baseMessage ($email)" } ?: baseMessage } - is AuthException.TooManyRequestsException -> stringProvider.tooManyRequestsRecoveryMessage + is AuthException.TooManyRequestsException -> + error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.tooManyRequestsRecoveryMessage is AuthException.PhoneVerificationCooldownException -> { // Use the custom message which includes remaining cooldown time - error.message ?: stringProvider.unknownErrorRecoveryMessage + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.unknownErrorRecoveryMessage } - is AuthException.MfaRequiredException -> stringProvider.mfaRequiredRecoveryMessage + is AuthException.MfaRequiredException -> + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.mfaRequiredRecoveryMessage is AuthException.AccountLinkingRequiredException -> { // Use the custom message which includes email and provider details - error.message ?: stringProvider.accountLinkingRequiredRecoveryMessage + error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.accountLinkingRequiredRecoveryMessage } is AuthException.DifferentSignInMethodRequiredException -> { - error.message ?: stringProvider.accountLinkingRequiredRecoveryMessage + error.message?.takeIf { it.isNotBlank() } + ?: stringProvider.accountLinkingRequiredRecoveryMessage } is AuthException.EmailMismatchException -> stringProvider.emailMismatchMessage is AuthException.InvalidEmailLinkException -> stringProvider.emailLinkInvalidLinkMessage @@ -194,7 +208,8 @@ internal fun getRecoveryMessage( val providerName = error.providerName ?: stringProvider.emailProvider stringProvider.emailLinkCrossDeviceLinkingMessage(providerName) } - is AuthException.AuthCancelledException -> stringProvider.authCancelledRecoveryMessage + is AuthException.AuthCancelledException -> + error.message?.takeIf { it.isNotBlank() } ?: stringProvider.authCancelledRecoveryMessage is AuthException.UnknownException -> { // Use custom message if available (e.g., for configuration errors) error.message?.takeIf { it.isNotBlank() } ?: stringProvider.unknownErrorRecoveryMessage @@ -214,6 +229,7 @@ internal fun getRecoveryActionText( error: AuthException, stringProvider: AuthUIStringProvider ): String { + if (!isRecoverable(error)) return stringProvider.dismissAction return when (error) { is AuthException.AuthCancelledException -> stringProvider.continueText is AuthException.EmailAlreadyInUseException -> stringProvider.signInDefault // Use existing "Sign in" text @@ -224,14 +240,11 @@ internal fun getRecoveryActionText( is AuthException.EmailLinkPromptForEmailException -> stringProvider.continueText is AuthException.EmailLinkCrossDeviceLinkingException -> stringProvider.continueText is AuthException.EmailLinkWrongDeviceException -> stringProvider.continueText - is AuthException.EmailLinkDifferentAnonymousUserException -> stringProvider.dismissAction is AuthException.UserNotFoundException -> stringProvider.signupPageTitle // Navigate to sign-up when user not found is AuthException.NetworkException, is AuthException.InvalidCredentialsException, is AuthException.WeakPasswordException, - is AuthException.PasswordPolicyViolationException, - is AuthException.TooManyRequestsException, - is AuthException.PhoneVerificationCooldownException -> stringProvider.retryAction + is AuthException.PasswordPolicyViolationException -> stringProvider.retryAction is AuthException.UnknownException -> stringProvider.retryAction else -> stringProvider.retryAction @@ -262,6 +275,9 @@ internal fun isRecoverable(error: AuthException): Boolean { is AuthException.EmailLinkCrossDeviceLinkingException -> true is AuthException.EmailLinkWrongDeviceException -> true is AuthException.EmailLinkDifferentAnonymousUserException -> false + is AuthException.MisconfigurationException -> false // Retrying cannot fix project setup + // The method is not available on this account; repeating it cannot change that. + is AuthException.SignInMethodUnavailableException -> false is AuthException.UnknownException -> true else -> true } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/components/TopLevelDialogController.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/TopLevelDialogController.kt index a5b73917e5..89a4a82ed2 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/components/TopLevelDialogController.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/components/TopLevelDialogController.kt @@ -19,10 +19,12 @@ import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import com.firebase.ui.auth.AuthException import com.firebase.ui.auth.AuthState import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.LocalAuthUIStringProvider /** * CompositionLocal for accessing the top-level dialog controller from any composable. @@ -40,7 +42,7 @@ val LocalTopLevelDialogController = compositionLocalOf AuthState ) { + /** + * Only ever set by the deprecated constructor below. A caller that passed a provider without + * also providing [LocalAuthUIStringProvider] still works, instead of trading a compile-time + * argument for a runtime `error("No AuthUIStringProvider provided")`. + */ + private var explicitStringProvider: AuthUIStringProvider? = null + + @Deprecated( + "The string provider is now read from LocalAuthUIStringProvider at render time.", + ReplaceWith("TopLevelDialogController(currentAuthState)") + ) + constructor( + stringProvider: AuthUIStringProvider, + currentAuthState: () -> AuthState + ) : this(currentAuthState) { + explicitStringProvider = stringProvider + } + private var dialogState by mutableStateOf(null) private val shownErrorStates = mutableSetOf() @@ -125,11 +147,14 @@ class TopLevelDialogController( /** * Composable that renders the current dialog, if any. * This should be called once at the root level of your auth flow. - * - * Uses the existing [ErrorRecoveryDialog] component. + * + * Uses the existing [ErrorRecoveryDialog] component. Strings come from + * [LocalAuthUIStringProvider], read here at render time, unless the controller was built + * through the deprecated constructor that takes one explicitly. */ @Composable fun CurrentDialog() { + val stringProvider = explicitStringProvider ?: LocalAuthUIStringProvider.current val state = dialogState when (state) { is DialogState.ErrorDialog -> { @@ -174,16 +199,40 @@ class TopLevelDialogController( * live auth state on every [TopLevelDialogController.showErrorDialog] call without being * recreated (and losing its de-duplication history) whenever the auth state changes. * - * Keyed on [stringProvider] rather than left unkeyed: callers must pass a `remember`ed - * [stringProvider] (stable across recompositions), otherwise the controller — and its - * de-duplication history — would be recreated on every recomposition. + * The `remember` is deliberately unkeyed, so any key would be a way to lose a dialog that was + * just shown. Nothing kept across recompositions goes stale as a result: strings are resolved + * from [LocalAuthUIStringProvider] at render time, and [authState] is read through + * [rememberUpdatedState] rather than captured, so the first composition's lambda is not pinned + * for the controller's life. + */ +@Composable +fun rememberTopLevelDialogController( + authState: () -> AuthState +): TopLevelDialogController { + val currentAuthState by rememberUpdatedState(authState) + return remember { + TopLevelDialogController { currentAuthState() } + } +} + +/** + * Creates and remembers a [TopLevelDialogController] bound to an explicit [stringProvider]. + * + * Kept only for source compatibility. It still keys the `remember` on [stringProvider], so a + * caller whose provider is not stable across recompositions loses the controller's state — that + * is the reason to move to the single-argument overload above. */ +@Deprecated( + "The string provider is now read from LocalAuthUIStringProvider at render time.", + ReplaceWith("rememberTopLevelDialogController(authState)") +) @Composable fun rememberTopLevelDialogController( stringProvider: AuthUIStringProvider, authState: () -> AuthState ): TopLevelDialogController { return remember(stringProvider) { + @Suppress("DEPRECATION") TopLevelDialogController(stringProvider, authState) } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt index c986fcd4c5..94bd9efbc1 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt +++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt @@ -78,7 +78,6 @@ import com.firebase.ui.auth.configuration.auth_provider.rememberOAuthSignInHandl import com.firebase.ui.auth.configuration.auth_provider.rememberSignInWithFacebookLauncher import com.firebase.ui.auth.configuration.auth_provider.signInWithEmailLink import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider -import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider import com.firebase.ui.auth.configuration.string_provider.LocalAuthUIStringProvider import com.firebase.ui.auth.configuration.theme.LocalAuthUITheme import com.firebase.ui.auth.ui.components.LocalTopLevelDialogController @@ -181,7 +180,9 @@ fun FirebaseAuthScreen( val activity = LocalActivity.current val context = LocalContext.current val coroutineScope = rememberCoroutineScope() - val stringProvider = remember(context) { DefaultAuthUIStringProvider(context) } + // The host's provider, not one built from LocalContext: only this honours a custom + // AuthUIStringProvider and the configured locale. + val stringProvider = configuration.stringProvider // The reauth effects below run outside composition, so they cannot call stringResource // themselves. @@ -202,7 +203,7 @@ fun FirebaseAuthScreen( hostAuthFlowScope(authUI, configuration, hostStateHolder) } val authState = rawAuthState - val dialogController = rememberTopLevelDialogController(stringProvider) { authState } + val dialogController = rememberTopLevelDialogController { authState } val lastSuccessfulUserId = remember { mutableStateOf(null) } val pendingLinkingCredential = remember { mutableStateOf(null) } val pendingResolver = remember { mutableStateOf(null) } @@ -412,6 +413,7 @@ fun FirebaseAuthScreen( metadata = authRouteMetadata(AuthRoute.MethodPicker) ) { if (customMethodPickerLayout != null) { + // Takes over the entire screen; see the KDoc on customMethodPickerLayout. Box(modifier = Modifier.fillMaxSize()) { customMethodPickerLayout(configuration.providers, onProviderSelected) } diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ContinueUrlBuilder.kt b/auth/src/main/java/com/firebase/ui/auth/util/ContinueUrlBuilder.kt index 80efbd8bd4..427cef241a 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/ContinueUrlBuilder.kt +++ b/auth/src/main/java/com/firebase/ui/auth/util/ContinueUrlBuilder.kt @@ -13,7 +13,9 @@ */ package com.firebase.ui.auth.util +import android.net.Uri import androidx.annotation.RestrictTo +import androidx.core.net.toUri import com.firebase.ui.auth.util.EmailLinkParser.LinkParameters.ANONYMOUS_USER_ID_IDENTIFIER import com.firebase.ui.auth.util.EmailLinkParser.LinkParameters.FORCE_SAME_DEVICE_IDENTIFIER import com.firebase.ui.auth.util.EmailLinkParser.LinkParameters.PROVIDER_ID_IDENTIFIER @@ -22,15 +24,20 @@ import com.firebase.ui.auth.util.EmailLinkParser.LinkParameters.SESSION_IDENTIFI /** * Builder for constructing continue URLs with embedded session and authentication parameters. * Used in email link sign-in flows to pass state between devices. + * + * The incoming URL comes from the consumer's [com.google.firebase.auth.ActionCodeSettings], so it + * may already carry a query string and/or a fragment. Parameters are appended through [Uri], the + * same parser [EmailLinkParser] reads them back with, which places them in the query whatever the + * URL's shape and percent-encodes their values. */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class ContinueUrlBuilder(url: String) { - private val continueUrl: StringBuilder + private var continueUrl: Uri init { require(url.isNotBlank()) { "URL cannot be empty" } - continueUrl = StringBuilder(url).append("?") + continueUrl = url.toUri() } fun appendSessionId(sessionId: String): ContinueUrlBuilder { @@ -57,16 +64,9 @@ class ContinueUrlBuilder(url: String) { private fun addQueryParam(key: String, value: String) { if (value.isBlank()) return - val isFirstParam = continueUrl.last() == '?' - val mark = if (isFirstParam) "" else "&" - continueUrl.append("$mark$key=$value") + continueUrl = continueUrl.buildUpon().appendQueryParameter(key, value).build() } - fun build(): String { - if (continueUrl.last() == '?') { - // No params added so we remove the '?' - continueUrl.setLength(continueUrl.length - 1) - } - return continueUrl.toString() - } -} \ No newline at end of file + // Untouched when nothing was appended: Uri hands back the string it was parsed from. + fun build(): String = continueUrl.toString() +} diff --git a/auth/src/main/res/layout/fui_welcome_back_password_prompt_layout.xml b/auth/src/main/res/layout/fui_welcome_back_password_prompt_layout.xml index 56c59080b0..9872d9a16c 100644 --- a/auth/src/main/res/layout/fui_welcome_back_password_prompt_layout.xml +++ b/auth/src/main/res/layout/fui_welcome_back_password_prompt_layout.xml @@ -34,8 +34,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:textIsSelectable="false" - app:layout_constraintTop_toBottomOf="@+id/heading" - tools:text="@string/fui_welcome_back_password_prompt_body" /> + app:layout_constraintTop_toBottomOf="@+id/heading" /> تسجيل الدخول متابعة تشير المتابعة إلى موافقتك على %1$s و%2$s. - %1$s \u00A0 \u00A0 %2$s حدث خطأ في الشبكة، يُرجى التحقّق من اتصال الإنترنت. Google Facebook @@ -41,12 +40,9 @@ يجب ملء هذا الحقل. عنوان البريد الإلكتروني هذا غير صحيح يجب إدخال عنوان بريدك الإلكتروني للمتابعة - يُرجى إدخال الاسم الأول واسم العائلة. - جارٍ البحث عن حسابات حالية… اشتراك الاسم الأول واسم العائلة حفظ - جارٍ الاشتراك… كلمة المرور ليست قوية بما فيه الكفاية. يجب استخدام %1$d رقم على الأقل ومزيج من الأحرف والأرقام كلمة المرور ليست قوية بما فيه الكفاية. يجب استخدام رقم واحد (%1$d) على الأقل ومزيج من الأحرف والأرقام كلمة المرور ليست قوية بما فيه الكفاية. يجب استخدام رقمين (%1$d) على الأقل ومزيج من الأحرف والأرقام كلمة المرور ليست قوية بما فيه الكفاية. يجب استخدام %1$d أرقام على الأقل ومزيج من الأحرف والأرقام كلمة المرور ليست قوية بما فيه الكفاية. يجب استخدام %1$d رقمًا على الأقل ومزيج من الأحرف والأرقام كلمة المرور ليست قوية بما فيه الكفاية. يجب استخدام %1$d رقم على الأقل ومزيج من الأحرف والأرقام فشل تسجيل حساب البريد الإلكتروني بنود الخدمة @@ -54,17 +50,12 @@ لديك حساب حاليًا تسرّنا عودتك! لديك حساب حاليًا\n - لقد سبق أن استخدمت %1$s. سجّل الدخول باستخدام %2$s للمتابعة. - لقد سبق أن استخدمت %1$s لتسجيل الدخول. يُرجى إدخال كلمة المرور لهذا الحساب. - سبق واستخدمت العنوان %1$s. يمكنك ربط حسابك على %2$s بحساب %1$s عبر تسجيل الدخول باستخدام رابط البريد الإلكتروني المضمّن في الرسالة الإلكترونية أدناه.\n\nلكي تنجح في ربط حسابك على %2$s بهذا البريد الإلكتروني، عليك فتح الرابط على الجهاز أو المتصفّح نفسه. - جارٍ تسجيل الدخول… هل تواجه مشكلة في تسجيل الدخول؟ استرداد كلمة المرور إثبات ملكية بريدك الإلكتروني تم إرسال تعليمات إلى البريد الإلكتروني هذا تشرح كيفية إعادة ضبط كلمة المرور. إرسال اتبع التعليمات التي تم إرسالها إلى %1$s لاسترداد كلمة المرور. - جارٍ الإرسال… لا يتطابق عنوان البريد الإلكتروني هذا مع حساب حالي حدث خطأ غير معروف. كلمة المرور غير صحيحة. @@ -90,19 +81,14 @@ يُرجى إدخال رقم هاتف صالح أدخل الرمز المكوّن من 6 أرقام الذي أرسلناه إلى إعادة إرسال الرمز بعد %1$s - إثبات ملكية رقم هاتفك جارٍ التحقق… الرمز غير صحيح. يُرجى المحاولة مجددًا. تم استخدام رقم الهاتف هذا لعدد كبير جدًا من المرات - حدثت مشكلة أثناء إثبات ملكية رقم هاتفك لم يعد هذا الرمز صالحًا - تسجيل الدخول عبر رقم الهاتف - تمّ التحقّق تلقائيًا من رقم الهاتف. إعادة إرسال الرمز تأكيد ملكية رقم الهاتف - Use a different phone number + استخدام رقم هاتف آخر عند النقر على “%1$s”، قد يتمّ إرسال رسالة قصيرة SMS وقد يتمّ تطبيق رسوم الرسائل والبيانات. - يشير النقر على "%1$s" إلى موافقتك على %2$s و%3$s. وقد يتمّ إرسال رسالة قصيرة كما قد تنطبق رسوم الرسائل والبيانات. خطأ في المصادقة أعد المحاولة مطلوب تحقق إضافي. يرجى إكمال المصادقة متعددة العوامل. @@ -143,29 +129,11 @@ تم التحقق من الهوية. أعد المحاولة. إدارة المصادقة الثنائية كلمة المرور غير صحيحة - اختر طريقة التحقق - أضف طبقة أمان إضافية - رسالة نصية - تطبيق المصادقة - رقم الهاتف هذا مرتبط بحساب آخر - التحقق مطلوب - امسح رمز الاستجابة السريعة باستخدام تطبيق المصادقة - تطبيق المصادقة معد بالفعل - هل أنت متأكد من إزالة طريقة التحقق هذه؟ - تعذرت إزالة طريقة التحقق - تمت إزالة طريقة التحقق - أدخل رمز التحقق - تعذر تحديث الملف الشخصي - تم تحديث الملف الشخصي - أكد هويتك للمتابعة - إعادة المصادقة مطلوبة - نجحت إعادة المصادقة - إعادة المصادقة إزالة إعادة إرسال بريد التحقق المفتاح السري تسجيل الخروج - مسجل الدخول باسم + مسجل الدخول باسم %1$s تخطي استخدم طريقة أخرى رمز التحقق @@ -175,4 +143,17 @@ المصادقة متعددة العوامل معطلة حاليًا + البريد الإلكتروني أو كلمة المرور غير صحيحة + لم تعد جلسة التحقق هذه صالحة. اطلب رمزًا جديدًا. + لم تكتمل عملية التحقق من رقم الهاتف. أعد المحاولة. + بيانات الاعتماد هذه تخص حسابًا آخر. + رقم الهاتف هذا غير مُعدّ للتحقق في هذا الحساب. + انتهت صلاحية جلسة تسجيل الدخول. سجِّل الدخول مرة أخرى للمتابعة. + لم يعد هذا الرابط صالحًا. اطلب رابطًا جديدًا. + تحقَّق من عنوان بريدك الإلكتروني قبل المتابعة. + طريقة التحقق هذه مُعدّة بالفعل في هذا الحساب. + لقد وصلت إلى الحد الأقصى لطرق التحقق في هذا الحساب. + كلمة المرور لا تستوفي المتطلبات. جرِّب كلمة مرور أخرى. + كلمة المرور طويلة جدًا. الحد الأقصى للطول هو %1$d. + تعذّر العثور على مفتاح مرور لهذا الحساب. سجِّل الدخول بطريقة أخرى. diff --git a/auth/src/main/res/values-b+es+419/strings.xml b/auth/src/main/res/values-b+es+419/strings.xml index 6039dc47f8..dbef1a1699 100755 --- a/auth/src/main/res/values-b+es+419/strings.xml +++ b/auth/src/main/res/values-b+es+419/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -110,17 +96,17 @@ La autenticación fue cancelada. Vuelva a intentarlo cuando esté listo. - Choose Authentication Method - Set Up SMS Verification - Set Up Authenticator App - Verify Your Code + Elige el método de autenticación + Configurar verificación por SMS + Configurar aplicación de autenticación + Verifica tu código - Select a second authentication method to secure your account - Enter your phone number to receive verification codes - Scan the QR code with your authenticator app - Enter the code sent to your phone - Enter the code from your authenticator app - Enter your verification code + Selecciona un segundo método de autenticación para proteger tu cuenta + Introduce tu número de teléfono para recibir códigos de verificación + Escanea el código QR con tu aplicación de autenticación + Introduce el código enviado a tu teléfono + Introduce el código de tu aplicación de autenticación + Introduce tu código de verificación Confirmar contraseña Las contraseñas no coinciden @@ -172,25 +158,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar - Multi-factor authentication is currently disabled + La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-bg/strings.xml b/auth/src/main/res/values-bg/strings.xml index 479a681434..4a5c0f8518 100755 --- a/auth/src/main/res/values-bg/strings.xml +++ b/auth/src/main/res/values-bg/strings.xml @@ -3,7 +3,6 @@ Вход Напред Продължавайки, приемате нашите %1$s и %2$s. - %1$s     %2$s Грешка в мрежата – проверете връзката с интернет. Google Facebook @@ -41,12 +40,9 @@ Трябва да се попълни. Този имейл адрес е неправилен Въведете имейл адреса си, за да продължите - Моля, въведете име и фамилия. - Проверява се за съществуващи профили… Регистрация Име и фамилия Запазване - Създава се регистрация… Паролата не е достатъчно надеждна. Използвайте поне %1$d знак и комбинация от букви и цифри Паролата не е достатъчно надеждна. Използвайте поне %1$d знака и комбинация от букви и цифри Регистрацията с имейл адрес не бе успешна Общите условия @@ -54,17 +50,12 @@ Вече имате профил Добре дошли отново! Вече имате профил\n - Вече използвахте %1$s. За да продължите, влезте с %2$s. - Вече използвахте %1$s за вход. Въведете паролата си за този профил. - Вече използвахте %1$s. Можете да свържете профила си в(ъв) %2$s с имейл адреса %1$s чрез посочената по-долу връзка за вход.\n\nЗа успешното свързване на профила си в(ъв) %2$s с този имейл адрес, трябва да отворите връзката на същото устройство или браузър. - Влизате… Имате проблем при влизането? Възстановяване на паролата Проверете електронната си поща На този имейл адрес ще получите инструкции за повторно задаване на паролата. Изпращане За да възстановите паролата си, изпълнете инструкциите, изпратени до %1$s. - Изпраща се… Този имейл адрес не съответства на съществуващ профил Възникна неизвестна грешка. Грешна парола. @@ -90,19 +81,14 @@ Въведете валиден телефонен номер Въведете 6-цифрения код, който изпратихме до Повторно изпращане на кода след %1$s - Потвърждаване на телефонния ви номер Потвърждава се… Неправилен код. Опитайте отново. Този телефонен номер е използван твърде много пъти - При потвърждаването на телефонния ви номер възникна проблем Този код вече не е валиден - Вход с телефонен номер - Телефонният номер е потвърден автоматично Повторно изпращане на кода Потвърждаване на телефонния номер - Use a different phone number + Използване на друг телефонен номер Докосвайки „%1$s“, може да получите SMS съобщение. То може да се таксува по тарифите за данни и SMS. - Докосвайки „%1$s", приемате нашите %2$s и %3$s. Възможно е да получите SMS съобщение. То може да се таксува по тарифите за данни и SMS. Грешка при удостоверяване Опитай отново Необходима е допълнителна проверка. Моля, завършете многофакторното удостоверяване. @@ -143,29 +129,11 @@ Самоличността е потвърдена. Опитайте отново действието си. Управление на двуфакторно удостоверяване Неправилна парола - Изберете метод за потвърждение - Добавете допълнително ниво на сигурност - SMS - Приложение за удостоверяване - Този телефонен номер е свързан с друг акаунт - Необходимо е потвърждение - Сканирайте QR кода с приложението си за удостоверяване - Приложението за удостоверяване вече е настроено - Сигурни ли сте, че искате да премахнете този метод? - Методът не може да бъде премахнат - Методът е премахнат - Въведете кода за потвърждение - Профилът не може да бъде актуализиран - Профилът е актуализиран - Потвърдете самоличността си, за да продължите - Необходимо е повторно удостоверяване - Повторното удостоверяване е успешно - Повторно удостоверяване Премахване Изпращане на имейл за потвърждение отново Таен ключ Изход - Влезли сте като + Влезли сте като %1$s Пропускане Използване на друг метод Код за потвърждение @@ -175,4 +143,17 @@ Многофакторната автентификация в момента е деактивирана + Имейл адресът или паролата не са правилни + Тази сесия за потвърждаване вече не е валидна. Заявете нов код. + Потвърждаването по телефон не завърши. Опитайте отново. + Тези идентификационни данни принадлежат на друг профил. + Този телефонен номер не е настроен за потвърждаване в този профил. + Сесията ви за вход изтече. Влезте отново, за да продължите. + Тази връзка вече не е валидна. Заявете нова. + Потвърдете имейл адреса си, преди да продължите. + Този метод за потвърждаване вече е настроен в този профил. + Достигнахте ограничението за методи за потвърждаване в този профил. + Паролата ви не отговаря на изискванията. Опитайте с друга. + Паролата е твърде дълга. Максималната дължина е %1$d. + Не намерихме код за достъп за този профил. Влезте по друг начин. diff --git a/auth/src/main/res/values-bn/strings.xml b/auth/src/main/res/values-bn/strings.xml index 6b0870da65..55a29b236f 100755 --- a/auth/src/main/res/values-bn/strings.xml +++ b/auth/src/main/res/values-bn/strings.xml @@ -3,7 +3,6 @@ সাইন-ইন করুন চালিয়ে যান চালিয়ে যাওয়ার অর্থ, আপনি আমাদের %1$s এবং %2$s-এর সাথে সম্মত। - %1$s \u00A0 \u00A0 %2$s নেটওয়ার্কে সমস্যা, ইন্টারনেট কানেকশন দেখে নিন। Google Facebook @@ -41,12 +40,9 @@ আপনি এটি খালি ছাড়তে পারেন না। ইমেল অ্যাড্রেসটি সঠিক নয় চালিয়ে যাওয়ার জন্য আপনার ইমেল অ্যাড্রেস লিখুন - আপনার নাম এবং পদবি লিখুন। - আগে থেকে কোনও অ্যাকাউন্ট আছে কিনা চেক করা হচ্ছে… সাইন-আপ করুন নাম ও পদবি সেভ করুন - সাইন-আপ করা হচ্ছে… পাসওয়ার্ডটি পর্যাপ্ত সুরক্ষিত নয়। কমপক্ষে %1$dটি বিশেষ বর্ণ এবং অক্ষর ও সংখ্যা মিশিয়ে ব্যবহার করুন পাসওয়ার্ডটি পর্যাপ্ত সুরক্ষিত নয়। কমপক্ষে %1$dটি বিশেষ বর্ণ এবং অক্ষর ও সংখ্যা মিশিয়ে ব্যবহার করুন ইমেল অ্যাকাউন্টটি রেজিস্টার করা যায়নি পরিষেবার শর্তাবলি @@ -54,17 +50,12 @@ আপনার আগে থেকেই একটি অ্যাকাউন্ট আছে আবার স্বাগতম! আপনার আগে থেকেই একটি অ্যাকাউন্ট আছে\n - আপনি %1$s আগেই ব্যবহার করেছেন। চালিয়ে যেতে %2$s দিয়ে সাইন-ইন করুন। - আপনি আগেই %1$s দিয়ে সাইন-ইন করেছেন। সেই অ্যাকাউন্টের জন্য পাসওয়ার্ডটি লিখুন। - আপনি আগে থেকেই %1$s ব্যবহার করে ফেলেছেন। নিচের ইমেল লিঙ্কে সাইন-ইন করে আপনি আপনার %2$s অ্যাকাউন্ট %1$s-এ কানেক্ট করতে পারেন।\n\nএই ইমেলে আপনার %2$s অ্যাকাউন্টটি কানেক্ট করার জন্য আপনাকে একই ডিভাইস অথবা ব্রাউজারে লিঙ্কটি খুলতে হবে। - সাইন-ইন করা হচ্ছে… সাইন-ইন করতে সমস্যা হচ্ছে? পাসওয়ার্ড পুনরুদ্ধার করুন আপনার ইমেল দেখুন কীভাবে পাসওয়ার্ড পুনরুদ্ধার করতে হবে তা ইমেলে পাঠানো নির্দেশাবলী থেকে জানুন। পাঠান আপনার পাসওয়ার্ড পুনরুদ্ধার করতে %1$s তে পাঠানো নির্দেশ অনুসরণ করুন। - পাঠানো হচ্ছে… এই ইমেল অ্যাড্রেসটি আগে থেকে থাকা অ্যাকাউন্টের সাথে মিলছে না অজানা একটি সমস্যা হয়েছে। পাসওয়ার্ডটি সঠিক নয়। @@ -90,25 +81,20 @@ একটি সঠিক ফোন নম্বর লিখুন আমাদের পাঠানো ৬-সংখ্যার কোডটি লিখুন %1$s এ কোডটি আবার পাঠান - আপনার ফোন নম্বর যাচাই করুন যাচাই করা হচ্ছে… কোডটি ভুল। আবার চেষ্টা করুন। এই ফোন নম্বরটি অনেকবার ব্যবহার করা হয়েছে - আপনার ফোন নম্বরটি যাচাই করতে সমস্যা হয়েছে এই কোডটি আর ব্যবহার করা যাবে না - ফোন নম্বর দিয়ে সাইন-ইন করুন - ফোন নম্বরটি নিজে থেকে যাচাই করা হয়েছে কোডটি আবার পাঠান ফোন নম্বর যাচাই করুন - Use a different phone number + অন্য একটি ফোন নম্বর ব্যবহার করুন %1$s এ ট্যাপ করলে আপনি একটি এসএমএস পাঠাতে পারেন। মেসেজ ও ডেটার চার্জ প্রযোজ্য। - “%1$s” বোতামে ট্যাপ করার অর্থ, আপনি আমাদের %2$s এবং %3$s-এর সাথে সম্মত। একটি এসএমএস পাঠানো হতে পারে। মেসেজ এবং ডেটার উপরে প্রযোজ্য চার্জ লাগতে পারে। - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + প্রমাণীকরণ সংক্রান্ত সমস্যা + আবার চেষ্টা করুন + অতিরিক্ত যাচাইকরণ প্রয়োজন। অনুগ্রহ করে মাল্টি-ফ্যাক্টর প্রমাণীকরণ সম্পূর্ণ করুন। + অ্যাকাউন্ট লিঙ্ক করা প্রয়োজন। অনুগ্রহ করে অন্য কোনও সাইন-ইন পদ্ধতি ব্যবহার করে দেখুন। + প্রমাণীকরণ বাতিল করা হয়েছে। আপনি প্রস্তুত হলে আবার চেষ্টা করুন। প্রমাণীকরণ পদ্ধতি চয়ন করুন @@ -144,29 +130,11 @@ পরিচয় যাচাই করা হয়েছে। আপনার কাজ আবার চেষ্টা করুন। দ্বি-ফ্যাক্টর প্রমাণীকরণ পরিচালনা করুন ভুল পাসওয়ার্ড - একটি যাচাইকরণ পদ্ধতি বেছে নিন - অতিরিক্ত নিরাপত্তা স্তর যোগ করুন - SMS - প্রমাণীকরণকারী অ্যাপ - এই ফোন নম্বরটি অন্য অ্যাকাউন্টের সাথে যুক্ত - যাচাইকরণ প্রয়োজন - আপনার প্রমাণীকরণকারী অ্যাপ দিয়ে QR কোড স্ক্যান করুন - প্রমাণীকরণকারী অ্যাপ ইতিমধ্যে সেট আপ করা আছে - আপনি কি নিশ্চিত যে এই পদ্ধতিটি সরাতে চান? - পদ্ধতি সরাতে অক্ষম - পদ্ধতি সরানো হয়েছে - যাচাইকরণ কোড লিখুন - প্রোফাইল আপডেট করতে অক্ষম - প্রোফাইল আপডেট করা হয়েছে - চালিয়ে যেতে আপনার পরিচয় নিশ্চিত করুন - পুনরায় প্রমাণীকরণ প্রয়োজন - পুনরায় প্রমাণীকরণ সফল - পুনরায় প্রমাণীকরণ সরান যাচাইকরণ ইমেল পুনরায় পাঠান গোপন কী সাইন আউট - হিসাবে সাইন ইন করা হয়েছে + %1$s হিসাবে সাইন ইন করা হয়েছে এড়িয়ে যান একটি ভিন্ন পদ্ধতি ব্যবহার করুন যাচাইকরণ কোড @@ -176,4 +144,17 @@ মাল্টি-ফ্যাক্টর প্রমাণীকরণ বর্তমানে নিষ্ক্রিয় + ইমেল বা পাসওয়ার্ড সঠিক নয় + এই যাচাইকরণ সেশনটি আর বৈধ নেই। নতুন কোডের জন্য অনুরোধ করুন। + ফোন যাচাইকরণ সম্পূর্ণ হয়নি। আবার চেষ্টা করুন। + এই ক্রেডেনশিয়াল অন্য একটি অ্যাকাউন্টের। + এই অ্যাকাউন্টে যাচাইকরণের জন্য এই ফোন নম্বরটি সেট আপ করা নেই। + আপনার সাইন-ইন সেশনের মেয়াদ শেষ হয়ে গেছে। চালিয়ে যেতে আবার সাইন-ইন করুন। + এই লিঙ্কটি আর বৈধ নয়। নতুন একটির জন্য অনুরোধ করুন। + চালিয়ে যাওয়ার আগে আপনার ইমেল অ্যাড্রেস যাচাই করুন। + এই যাচাইকরণ পদ্ধতিটি এই অ্যাকাউন্টে ইতিমধ্যেই সেট আপ করা আছে। + এই অ্যাকাউন্টে যাচাইকরণ পদ্ধতির সীমায় আপনি পৌঁছে গেছেন। + আপনার পাসওয়ার্ড প্রয়োজনীয় শর্ত পূরণ করে না। অন্য একটি পাসওয়ার্ড ব্যবহার করুন। + পাসওয়ার্ড খুব বড়। সর্বাধিক দৈর্ঘ্য হল %1$d। + এই অ্যাকাউন্টের জন্য পাসকী খুঁজে পাওয়া যায়নি। অন্য উপায়ে সাইন-ইন করুন। diff --git a/auth/src/main/res/values-ca/strings.xml b/auth/src/main/res/values-ca/strings.xml index e457554c9d..be6c1a71b3 100755 --- a/auth/src/main/res/values-ca/strings.xml +++ b/auth/src/main/res/values-ca/strings.xml @@ -3,7 +3,6 @@ Inicia la sessió Continua En continuar, acceptes les nostres %1$s i la nostra %2$s. - %1$s \u00A0 \u00A0 %2$s Error de la xarxa; comprova la connexió a Internet. Google Facebook @@ -41,30 +40,22 @@ Aquest camp no es pot deixar en blanc. Aquesta adreça electrònica no és correcta Introdueix l\'adreça electrònica per continuar - Introdueix nom i cognoms. - S\'estan comprovant els comptes existents… Registra\'t Nom i cognoms Desa - T\'estàs registrant… - La contrasenya no és prou segura. Utilitza com a mínim %1$d caràcter i combina lletres i números. La contrasenya no és prou segura. Utilitza com a mínim %1$d caràcters i combina lletres i números. + La contrasenya no és prou segura. Utilitza com a mínim %1$d caràcter i combina lletres i números. La contrasenya no és prou segura. Utilitza com a mínim %1$d de caràcters i combina lletres i números. La contrasenya no és prou segura. Utilitza com a mínim %1$d caràcters i combina lletres i números. No s\'ha pogut registrar el compte de correu electrònic Condicions del servei política de privadesa Ja tens un compte Benvingut de nou! Ja tens un compte\n - Ja has utilitzat l\'adreça electrònica %1$s. Inicia la sessió amb %2$s per continuar. - Ja has utilitzat l\'adreça electrònica %1$s per iniciar la sessió. Introdueix la contrasenya d\'aquest compte. - Ja has utilitzat l\'adreça electrònica %1$s. Pots connectar el compte que tens a %2$s amb %1$s iniciant la sessió amb l\'enllaç de correu electrònic que figura a continuació.\n\nPerquè el compte que tens a %2$s es connecti amb aquesta adreça electrònica, has d\'obrir l\'enllaç al mateix dispositiu o navegador. - S\'està iniciant la sessió… No pots iniciar la sessió? Recupera la contrasenya Comprova el correu electrònic Rep un correu electrònic amb instruccions per restablir la contrasenya. Envia Per recuperar la contrasenya, segueix les instruccions que s\'han enviat a %1$s. - S\'està enviant… Aquesta adreça electrònica no coincideix amb cap compte S\'ha produït un error desconegut. La contrasenya no és correcta. @@ -90,25 +81,20 @@ Introdueix un número de telèfon vàlid Introdueix el codi de 6 dígits que s\'ha enviat al número Torna a enviar el codi d\'aquí a %1$s - Verifica el número de telèfon S\'està verificant… El codi no és correcte. Torna-ho a provar. Aquest número de telèfon s\'ha utilitzat massa vegades - Hi ha hagut un problema en verificar el número de telèfon Aquest codi ja no és vàlid - Inicia la sessió amb el número de telèfon - El número de telèfon s\'ha verificat automàticament Torna a enviar el codi Verifica el número de telèfon - Use a different phone number + Utilitza un altre número de telèfon En tocar %1$s, és possible que s\'enviï un SMS. Es poden aplicar tarifes de dades i missatges. - En tocar %1$s, acceptes les nostres %2$s i la nostra %3$s. És possible que s\'enviï un SMS. Es poden aplicar tarifes de dades i missatges. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Error d\'autenticació + Torna-ho a provar + Cal una verificació addicional. Completeu l\'autenticació multifactor. + Cal enllaçar el compte. Proveu un altre mètode d\'inici de sessió. + L\'autenticació s\'ha cancel·lat. Torneu-ho a provar quan estigueu a punt. Trieu el mètode d\'autenticació @@ -144,29 +130,11 @@ Identitat verificada. Torna a provar la teva acció. Gestiona l\'autenticació de dos factors Contrasenya incorrecta - Tria un mètode de verificació - Afegeix una capa de seguretat addicional - SMS - Aplicació d\'autenticació - Aquest número de telèfon està associat a un altre compte - Verificació necessària - Escaneja el codi QR amb la teva aplicació d\'autenticació - L\'aplicació d\'autenticació ja està configurada - Estàs segur que vols eliminar aquest mètode? - No es pot eliminar el mètode - Mètode eliminat - Introdueix el codi de verificació - No es pot actualitzar el perfil - Perfil actualitzat - Confirma la teva identitat per continuar - Es requereix reautenticació - Reautenticació correcta - Reautentica Elimina Torna a enviar el correu de verificació Clau secreta Tanca la sessió - Has iniciat sessió com a + Has iniciat sessió com a %1$s Omet Utilitza un mètode diferent Codi de verificació @@ -176,4 +144,17 @@ L\'autenticació multifactor està desactivada actualment + Aquest correu electrònic o aquesta contrasenya no són correctes + Aquesta sessió de verificació ja no és vàlida. Sol·licita un codi nou. + La verificació per telèfon no s\'ha completat. Torna-ho a provar. + Aquestes credencials pertanyen a un altre compte. + Aquest número de telèfon no està configurat per a la verificació en aquest compte. + La teva sessió ha caducat. Torna a iniciar la sessió per continuar. + Aquest enllaç ja no és vàlid. Sol·licita\'n un de nou. + Verifica la teva adreça electrònica abans de continuar. + Aquest mètode de verificació ja està configurat en aquest compte. + Has assolit el límit de mètodes de verificació d\'aquest compte. + La teva contrasenya no compleix els requisits. Prova\'n una altra. + La contrasenya és massa llarga. La longitud màxima és %1$d. + No hem trobat cap clau d\'accés per a aquest compte. Inicia la sessió d\'una altra manera. diff --git a/auth/src/main/res/values-cs/strings.xml b/auth/src/main/res/values-cs/strings.xml index 64e5b18c18..09c0939f50 100755 --- a/auth/src/main/res/values-cs/strings.xml +++ b/auth/src/main/res/values-cs/strings.xml @@ -3,7 +3,6 @@ Přihlásit se Pokračovat Budete-li pokračovat, vyjadřujete svůj souhlas s dokumenty %1$s a %2$s. - %1$s \u00A0 \u00A0 %2$s Chyba sítě. Zkontrolujte připojení k internetu. Google Facebook @@ -41,12 +40,9 @@ Toto pole nesmí zůstat prázdné. E-mailová adresa není správná Nejprve zadejte svou e-mailovou adresu - Zadejte jméno a příjmení - Vyhledávání stávajících účtů… Registrace Jméno a příjmení Uložit - Registrace… Heslo není dostatečně silné. Použijte alespoň %1$d znak a kombinaci písmen a číslic Heslo není dostatečně silné. Použijte alespoň %1$d znaky a kombinaci písmen a číslic Heslo není dostatečně silné. Použijte alespoň %1$d znaku a kombinaci písmen a číslic Heslo není dostatečně silné. Použijte alespoň %1$d znaků a kombinaci písmen a číslic Registrace e-mailového účtu se nezdařila Smluvní podmínky @@ -54,17 +50,12 @@ Již máte účet Vítejte zpět! Již máte účet\n - Již jste použili adresu %1$s. Chcete-li pokračovat, přihlaste se prostřednictvím služby %2$s. - K přihlášení jste již použili adresu %1$s. Zadejte příslušné heslo k účtu. - E-mailovou adresu %1$s jste již použili. Svůj účet %2$s můžete s e-mailovou adresou %1$s propojit přihlášením prostřednictvím odkazu níže.\n\nAby došlo k úspěšnému propojení účtu %2$s s touto e-mailovou adresou, musíte odkaz otevřít na stejném zařízení nebo ve stejném prohlížeči. - Přihlašování… Máte potíže s přihlášením? Obnovit heslo Zkontrolujte svůj e-mail Na tento e-mail vám zašleme pokyny, jak heslo obnovit. Odeslat Postupujte podle pokynů odeslaných na adresu %1$s a obnovte heslo. - Odesílání… E-mailová adresa neodpovídá žádnému stávajícímu účtu Došlo k neznámé chybě. Nesprávné heslo. @@ -90,19 +81,14 @@ Zadejte platné telefonní číslo Zadejte šestimístný kód, který jsme vám zaslali Znovu zaslat kód za %1$s - Ověřit telefonní číslo Ověřování… Špatný kód. Zkuste to znovu. Toto telefonní číslo již bylo použito příliš mnohokrát - Při ověřování vašeho telefonního čísla došlo k potížím Platnost kódu vypršela - Přihlášení pomocí telefonního čísla - Telefonní číslo bylo automaticky ověřeno Znovu poslat kód Ověřit telefonní číslo - Use a different phone number + Použít jiné telefonní číslo Po klepnutí na možnost %1$s může být odeslána SMS. Mohou být účtovány poplatky za zprávy a data. - Klepnutím na tlačítko %1$s vyjadřujete svůj souhlas s dokumenty %2$s a %3$s. Může být odeslána SMS a mohou být účtovány poplatky za zprávy a data. Chyba ověření Zkusit znovu Vyžadováno další ověření. Dokončete prosím vícefaktorové ověření. @@ -143,29 +129,11 @@ Identita ověřena. Zkuste akci znovu. Správa dvoufázového ověření Nesprávné heslo - Vyberte metodu ověření - Přidejte další vrstvu zabezpečení - SMS - Ověřovací aplikace - Toto telefonní číslo je spojeno s jiným účtem - Vyžaduje se ověření - Naskenujte QR kód pomocí ověřovací aplikace - Ověřovací aplikace je již nastavena - Opravdu chcete odstranit tuto metodu? - Metodu nelze odstranit - Metoda odstraněna - Zadejte ověřovací kód - Profil nelze aktualizovat - Profil aktualizován - Potvrďte svou identitu a pokračujte - Vyžaduje se opětovné ověření - Opětovné ověření proběhlo úspěšně - Znovu ověřit Odstranit Znovu odeslat ověřovací e-mail Tajný klíč Odhlásit se - Přihlášen jako + Přihlášen jako %1$s Přeskočit Použít jinou metodu Ověřovací kód @@ -175,4 +143,17 @@ Vícefaktorové ověřování je aktuálně zakázáno + E-mail nebo heslo nejsou správné + Tato ověřovací relace už není platná. Vyžádejte si nový kód. + Ověření telefonu se nedokončilo. Zkuste to znovu. + Tyto přihlašovací údaje patří k jinému účtu. + Toto telefonní číslo není u tohoto účtu nastaveno pro ověřování. + Platnost vašeho přihlášení vypršela. Pokračujte opětovným přihlášením. + Tento odkaz už není platný. Vyžádejte si nový. + Před pokračováním ověřte svou e-mailovou adresu. + Tento způsob ověření je u tohoto účtu už nastavený. + Dosáhli jste limitu způsobů ověření pro tento účet. + Vaše heslo nesplňuje požadavky. Zkuste jiné. + Heslo je příliš dlouhé. Maximální délka je %1$d. + Pro tento účet jsme nenašli žádný přístupový klíč. Přihlaste se jiným způsobem. diff --git a/auth/src/main/res/values-da/strings.xml b/auth/src/main/res/values-da/strings.xml index 52c217d8a9..cdade3cd41 100755 --- a/auth/src/main/res/values-da/strings.xml +++ b/auth/src/main/res/values-da/strings.xml @@ -3,7 +3,6 @@ Log ind Fortsæt Ved at fortsætte indikerer du, at du accepterer vores %1$s og %2$s. - %1$s \u00A0 \u00A0 %2$s Netværksfejl. Tjek din internetforbindelse. Google Facebook @@ -41,12 +40,9 @@ Dette felt skal udfyldes. Mailadressen er ikke korrekt Angiv din mailadresse for at fortsætte - Angiv et for- og efternavn. - Søger efter eksisterende konti… Tilmeld dig For- og efternavn Gem - Tilmelder… Adgangskoden er ikke stærk nok. Brug mindst %1$d tegn og en blanding af bogstaver og tal Adgangskoden er ikke stærk nok. Brug mindst %1$d tegn og en blanding af bogstaver og tal Registrering af mailkonto mislykkedes Servicevilkår @@ -54,17 +50,13 @@ Du har allerede en konto Velkommen tilbage! Du har allerede en konto\n - Du har allerede brugt %1$s. Log ind med %2$s for at fortsætte. - Du har allerede brugt %1$s til at logge ind. Angiv din adgangskode for den pågældende konto. - Du har allerede brugt %1$s. Du kan knytte din %2$s-konto til %1$s ved at logge ind med dette maillink.\n\nHvis det skal lykkes at knytte din %2$s-konto til mailadressen på denne måde, skal du åbne linket med den samme enhed eller i samme browser. - Logger ind… Har du problemer med at logge ind? Gendan adgangskode Tjek din mail. Få en vejledning sendt til denne mail om, hvordan du nulstiller din adgangskode. - Send + + Send Følg vejledningen, der blev sendt til %1$s, for at gendanne din adgangskode. - Sender… Mailadressen matcher ikke en eksisterende konto Der opstod en ukendt fejl. Ugyldig adgangskode. @@ -90,19 +82,14 @@ Angiv et gyldigt telefonnummer Angiv den 6-cifrede kode, vi sendte til Send koden igen om %1$s - Bekræft dit telefonnummer Bekræfter… Koden er forkert. Prøv igen. Dette telefonnummer er blevet brugt for mange gange - Dit telefonnummer kunne ikke bekræftes Denne kode er ikke længere gyldig - Log ind med telefonnummer - Telefonnummeret blev bekræftet automatisk Send koden igen Bekræft telefonnummer - Use a different phone number + Brug et andet telefonnummer Når du trykker på “%1$s”, sendes der måske en sms. Der opkræves muligvis gebyrer for beskeder og data. - Når du trykker på "%1$s", indikerer du, at du accepterer vores %2$s og %3$s. Der sendes måske en sms. Der opkræves muligvis gebyrer for beskeder og data. Godkendelsesfejl Prøv igen Yderligere bekræftelse påkrævet. Fuldfør venligst multifaktorgodkendelse. @@ -143,29 +130,11 @@ Identitet bekræftet. Prøv din handling igen. Administrer totrinsgodkendelse Forkert adgangskode - Vælg en bekræftelsesmetode - Tilføj et ekstra sikkerhedslag - SMS - Godkendelsesapp - Dette telefonnummer er tilknyttet en anden konto - Bekræftelse påkrævet - Scan QR-koden med din godkendelsesapp - Godkendelsesapp er allerede konfigureret - Er du sikker på, at du vil fjerne denne metode? - Kan ikke fjerne metode - Metode fjernet - Indtast bekræftelseskode - Kan ikke opdatere profil - Profil opdateret - Bekræft din identitet for at fortsætte - Gengodkendelse påkrævet - Gengodkendelse lykkedes - Gengodkend Fjern Send bekræftelsesemail igen Hemmelig nøgle Log ud - Logget ind som + Logget ind som %1$s Spring over Brug en anden metode Bekræftelseskode @@ -175,4 +144,17 @@ Multifaktorgodkendelse er i øjeblikket deaktiveret + Mailadressen eller adgangskoden er ikke korrekt + Denne bekræftelsessession er ikke længere gyldig. Anmod om en ny kode. + Telefonbekræftelsen blev ikke fuldført. Prøv igen. + Disse loginoplysninger tilhører en anden konto. + Dette telefonnummer er ikke konfigureret til bekræftelse på denne konto. + Din loginsession er udløbet. Log ind igen for at fortsætte. + Dette link er ikke længere gyldigt. Anmod om et nyt. + Bekræft din mailadresse, før du fortsætter. + Denne bekræftelsesmetode er allerede konfigureret på denne konto. + Du har nået grænsen for bekræftelsesmetoder på denne konto. + Din adgangskode opfylder ikke kravene. Prøv en anden. + Adgangskoden er for lang. Den maksimale længde er %1$d. + Vi kunne ikke finde en adgangsnøgle til denne konto. Log ind på en anden måde. diff --git a/auth/src/main/res/values-de-rAT/strings.xml b/auth/src/main/res/values-de-rAT/strings.xml index ce10dd5d0d..a7aec600c0 100755 --- a/auth/src/main/res/values-de-rAT/strings.xml +++ b/auth/src/main/res/values-de-rAT/strings.xml @@ -3,7 +3,6 @@ Anmelden Weiter Indem Sie fortfahren, stimmen Sie unseren %1$s und unserer %2$s zu. - %1$s \u00A0 \u00A0 %2$s Netzwerkfehler. Überprüfen Sie Ihre Internetverbindung. Google Facebook @@ -41,12 +40,9 @@ Pflichtfeld. Ungültige E-Mail-Adresse E-Mail-Adresse eingeben, um fortzufahren - Geben Sie einen Vor- und Nachnamen ein. - Auf vorhandene Konten wird geprüft… Registrieren Vor- und Nachname Speichern - Registrierung wird durchgeführt… Das Passwort ist zu schwach. Verwenden Sie mindestens %1$d Zeichen und eine Mischung aus Buchstaben und Ziffern. Das Passwort ist zu schwach. Verwenden Sie mindestens %1$d Zeichen und eine Mischung aus Buchstaben und Ziffern. Registrierung des E-Mail-Kontos fehlgeschlagen Nutzungsbedingungen @@ -54,17 +50,12 @@ Sie haben bereits ein Konto Willkommen zurück! Sie haben schon ein Konto\n - Sie haben %1$s bereits verwendet. Melden Sie sich mit %2$s an, um fortzufahren. - Sie haben %1$s bereits zur Anmeldung verwendet. Geben Sie das Passwort für dieses Konto ein. - Sie haben %1$s schon benutzt. Sie können Ihr %2$s-Konto mit %1$s verbinden, indem Sie sich über den folgenden E-Mail-Link anmelden.\n\nÖffnen Sie den Link mit demselben Gerät oder Browser, um Ihr %2$s-Konto mit dieser E-Mail-Adresse zu verbinden. - Anmeldung wird durchgeführt… Probleme bei der Anmeldung? Passwort zurücksetzen Im Posteingang nachsehen In der an diese E-Mail-Adresse gesendeten Anleitung wird beschrieben, wie Sie Ihr Passwort zurücksetzen können. Senden Folgen Sie der an %1$s gesendeten Anleitung, um Ihr Passwort zurückzusetzen. - Wird gesendet… Diese E-Mail-Adresse passt zu keinem Konto Ein unbekannter Fehler ist aufgetreten. Falsches Passwort @@ -90,24 +81,19 @@ Geben Sie eine gültige Telefonnummer ein Geben Sie den 6-stelligen Code ein, der gesendet wurde an Code in %1$s erneut senden - Telefonnummer bestätigen Wird verifiziert… Falscher Code. Versuchen Sie es noch einmal. Diese Telefonnummer wurde schon zu oft verwendet - Bei der Bestätigung Ihrer Telefonnummer ist ein Problem aufgetreten Dieser Code ist nicht mehr gültig - Mit einer Telefonnummer anmelden - Telefonnummer wurde automatisch bestätigt Code erneut senden Telefonnummer bestätigen - Use a different phone number + Eine andere Telefonnummer verwenden Wenn Sie auf “%1$s” tippen, erhalten Sie möglicherweise eine SMS. Es können Gebühren für SMS und Datenübertragung anfallen. - Indem Sie auf “%1$s” tippen, stimmen Sie unseren %2$s und unserer %3$s zu. Sie erhalten möglicherweise eine SMS und es können Gebühren für die Nachricht und die Datenübertragung anfallen. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Authentifizierungsfehler + Erneut versuchen + Zusätzliche Verifizierung erforderlich. Bitte schließen Sie die Multi-Faktor-Authentifizierung ab. + Das Konto muss verknüpft werden. Bitte versuchen Sie eine andere Anmeldemethode. + Die Authentifizierung wurde abgebrochen. Bitte versuchen Sie es erneut, wenn Sie bereit sind. Authentifizierungsmethode auswählen @@ -172,25 +158,20 @@ Verifizieren Bitte verifizieren Sie %1$s, um fortzufahren. - Wählen Sie eine Verifizierungsmethode - Fügen Sie eine zusätzliche Sicherheitsebene hinzu - SMS - Authenticator-App - Diese Telefonnummer ist mit einem anderen Konto verknüpft - Verifizierung erforderlich - Scannen Sie den QR-Code mit Ihrer Authenticator-App - Authenticator-App ist bereits eingerichtet - Möchten Sie diese Methode wirklich entfernen? - Methode kann nicht entfernt werden - Methode entfernt - Geben Sie den Verifizierungscode ein - Profil kann nicht aktualisiert werden - Profil aktualisiert - Bestätigen Sie Ihre Identität, um fortzufahren - Erneute Authentifizierung erforderlich - Erneute Authentifizierung erfolgreich - Erneut authentifizieren Die Multi-Faktor-Authentifizierung ist derzeit deaktiviert + E-Mail-Adresse oder Passwort ist nicht korrekt + Diese Bestätigungssitzung ist nicht mehr gültig. Fordern Sie einen neuen Code an. + Die Telefonbestätigung wurde nicht abgeschlossen. Versuchen Sie es erneut. + Diese Anmeldedaten gehören zu einem anderen Konto. + Diese Telefonnummer ist für dieses Konto nicht zur Bestätigung eingerichtet. + Ihre Anmeldesitzung ist abgelaufen. Melden Sie sich erneut an, um fortzufahren. + Dieser Link ist nicht mehr gültig. Fordern Sie einen neuen an. + Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. + Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. + Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. + Ihr Passwort erfüllt die Anforderungen nicht. Versuchen Sie es mit einem anderen. + Das Passwort ist zu lang. Die maximale Länge beträgt %1$d. + Für dieses Konto wurde kein Passkey gefunden. Melden Sie sich auf andere Weise an. diff --git a/auth/src/main/res/values-de-rCH/strings.xml b/auth/src/main/res/values-de-rCH/strings.xml index f2b070a17c..a89a9561bc 100755 --- a/auth/src/main/res/values-de-rCH/strings.xml +++ b/auth/src/main/res/values-de-rCH/strings.xml @@ -3,7 +3,6 @@ Anmelden Weiter Indem Sie fortfahren, stimmen Sie unseren %1$s und unserer %2$s zu. - %1$s \u00A0 \u00A0 %2$s Netzwerkfehler. Überprüfen Sie Ihre Internetverbindung. Google Facebook @@ -41,12 +40,9 @@ Pflichtfeld. Ungültige E-Mail-Adresse E-Mail-Adresse eingeben, um fortzufahren - Geben Sie einen Vor- und Nachnamen ein. - Auf vorhandene Konten wird geprüft… Registrieren Vor- und Nachname Speichern - Registrierung wird durchgeführt… Das Passwort ist zu schwach. Verwenden Sie mindestens %1$d Zeichen und eine Mischung aus Buchstaben und Ziffern. Das Passwort ist zu schwach. Verwenden Sie mindestens %1$d Zeichen und eine Mischung aus Buchstaben und Ziffern. Registrierung des E-Mail-Kontos fehlgeschlagen Nutzungsbedingungen @@ -54,17 +50,12 @@ Sie haben bereits ein Konto Willkommen zurück! Sie haben schon ein Konto\n - Sie haben %1$s bereits verwendet. Melden Sie sich mit %2$s an, um fortzufahren. - Sie haben %1$s bereits zur Anmeldung verwendet. Geben Sie das Passwort für dieses Konto ein. - Sie haben %1$s schon benutzt. Sie können Ihr %2$s-Konto mit %1$s verbinden, indem Sie sich über den folgenden E-Mail-Link anmelden.\n\nÖffnen Sie den Link mit demselben Gerät oder Browser, um Ihr %2$s-Konto mit dieser E-Mail-Adresse zu verbinden. - Anmeldung wird durchgeführt… Probleme bei der Anmeldung? Passwort zurücksetzen Im Posteingang nachsehen In der an diese E-Mail-Adresse gesendeten Anleitung wird beschrieben, wie Sie Ihr Passwort zurücksetzen können. Senden Folgen Sie der an %1$s gesendeten Anleitung, um Ihr Passwort zurückzusetzen. - Wird gesendet… Diese E-Mail-Adresse passt zu keinem Konto Ein unbekannter Fehler ist aufgetreten. Falsches Passwort @@ -90,25 +81,20 @@ Geben Sie eine gültige Telefonnummer ein Geben Sie den 6-stelligen Code ein, der gesendet wurde an Code in %1$s erneut senden - Telefonnummer bestätigen Wird verifiziert… Falscher Code. Versuchen Sie es noch einmal. Diese Telefonnummer wurde schon zu oft verwendet - Bei der Bestätigung Ihrer Telefonnummer ist ein Problem aufgetreten Dieser Code ist nicht mehr gültig - Mit einer Telefonnummer anmelden - Telefonnummer wurde automatisch bestätigt Code erneut senden Telefonnummer bestätigen - Use a different phone number + Eine andere Telefonnummer verwenden Wenn Sie auf “%1$s” tippen, erhalten Sie möglicherweise eine SMS. Es können Gebühren für SMS und Datenübertragung anfallen. - Indem Sie auf “%1$s” tippen, stimmen Sie unseren %2$s und unserer %3$s zu. Sie erhalten möglicherweise eine SMS und es können Gebühren für die Nachricht und die Datenübertragung anfallen. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Authentifizierungsfehler + Erneut versuchen + Zusätzliche Verifizierung erforderlich. Bitte schließen Sie die Multi-Faktor-Authentifizierung ab. + Das Konto muss verknüpft werden. Bitte versuchen Sie eine andere Anmeldemethode. + Die Authentifizierung wurde abgebrochen. Bitte versuchen Sie es erneut, wenn Sie bereit sind. Authentifizierungsmethode auswählen @@ -173,25 +159,20 @@ Verifizieren Bitte verifizieren Sie %1$s, um fortzufahren. - Wählen Sie eine Verifizierungsmethode - Fügen Sie eine zusätzliche Sicherheitsebene hinzu - SMS - Authenticator-App - Diese Telefonnummer ist mit einem anderen Konto verknüpft - Verifizierung erforderlich - Scannen Sie den QR-Code mit Ihrer Authenticator-App - Authenticator-App ist bereits eingerichtet - Möchten Sie diese Methode wirklich entfernen? - Methode kann nicht entfernt werden - Methode entfernt - Geben Sie den Verifizierungscode ein - Profil kann nicht aktualisiert werden - Profil aktualisiert - Bestätigen Sie Ihre Identität, um fortzufahren - Erneute Authentifizierung erforderlich - Erneute Authentifizierung erfolgreich - Erneut authentifizieren Die Multi-Faktor-Authentifizierung ist derzeit deaktiviert + E-Mail-Adresse oder Passwort ist nicht korrekt + Diese Bestätigungssitzung ist nicht mehr gültig. Fordern Sie einen neuen Code an. + Die Telefonbestätigung wurde nicht abgeschlossen. Versuchen Sie es erneut. + Diese Anmeldedaten gehören zu einem anderen Konto. + Diese Telefonnummer ist für dieses Konto nicht zur Bestätigung eingerichtet. + Ihre Anmeldesitzung ist abgelaufen. Melden Sie sich erneut an, um fortzufahren. + Dieser Link ist nicht mehr gültig. Fordern Sie einen neuen an. + Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. + Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. + Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. + Ihr Passwort erfüllt die Anforderungen nicht. Versuchen Sie es mit einem anderen. + Das Passwort ist zu lang. Die maximale Länge beträgt %1$d. + Für dieses Konto wurde kein Passkey gefunden. Melden Sie sich auf andere Weise an. diff --git a/auth/src/main/res/values-de/strings.xml b/auth/src/main/res/values-de/strings.xml index 9fe8f66f83..a7aec600c0 100755 --- a/auth/src/main/res/values-de/strings.xml +++ b/auth/src/main/res/values-de/strings.xml @@ -3,7 +3,6 @@ Anmelden Weiter Indem Sie fortfahren, stimmen Sie unseren %1$s und unserer %2$s zu. - %1$s \u00A0 \u00A0 %2$s Netzwerkfehler. Überprüfen Sie Ihre Internetverbindung. Google Facebook @@ -41,12 +40,9 @@ Pflichtfeld. Ungültige E-Mail-Adresse E-Mail-Adresse eingeben, um fortzufahren - Geben Sie einen Vor- und Nachnamen ein. - Auf vorhandene Konten wird geprüft… Registrieren Vor- und Nachname Speichern - Registrierung wird durchgeführt… Das Passwort ist zu schwach. Verwenden Sie mindestens %1$d Zeichen und eine Mischung aus Buchstaben und Ziffern. Das Passwort ist zu schwach. Verwenden Sie mindestens %1$d Zeichen und eine Mischung aus Buchstaben und Ziffern. Registrierung des E-Mail-Kontos fehlgeschlagen Nutzungsbedingungen @@ -54,17 +50,12 @@ Sie haben bereits ein Konto Willkommen zurück! Sie haben schon ein Konto\n - Sie haben %1$s bereits verwendet. Melden Sie sich mit %2$s an, um fortzufahren. - Sie haben %1$s bereits zur Anmeldung verwendet. Geben Sie das Passwort für dieses Konto ein. - Sie haben %1$s schon benutzt. Sie können Ihr %2$s-Konto mit %1$s verbinden, indem Sie sich über den folgenden E-Mail-Link anmelden.\n\nÖffnen Sie den Link mit demselben Gerät oder Browser, um Ihr %2$s-Konto mit dieser E-Mail-Adresse zu verbinden. - Anmeldung wird durchgeführt… Probleme bei der Anmeldung? Passwort zurücksetzen Im Posteingang nachsehen In der an diese E-Mail-Adresse gesendeten Anleitung wird beschrieben, wie Sie Ihr Passwort zurücksetzen können. Senden Folgen Sie der an %1$s gesendeten Anleitung, um Ihr Passwort zurückzusetzen. - Wird gesendet… Diese E-Mail-Adresse passt zu keinem Konto Ein unbekannter Fehler ist aufgetreten. Falsches Passwort @@ -90,19 +81,14 @@ Geben Sie eine gültige Telefonnummer ein Geben Sie den 6-stelligen Code ein, der gesendet wurde an Code in %1$s erneut senden - Telefonnummer bestätigen Wird verifiziert… Falscher Code. Versuchen Sie es noch einmal. Diese Telefonnummer wurde schon zu oft verwendet - Bei der Bestätigung Ihrer Telefonnummer ist ein Problem aufgetreten Dieser Code ist nicht mehr gültig - Mit einer Telefonnummer anmelden - Telefonnummer wurde automatisch bestätigt Code erneut senden Telefonnummer bestätigen - Use a different phone number + Eine andere Telefonnummer verwenden Wenn Sie auf “%1$s” tippen, erhalten Sie möglicherweise eine SMS. Es können Gebühren für SMS und Datenübertragung anfallen. - Indem Sie auf "%1$s" tippen, stimmen Sie unseren %2$s und unserer %3$s zu. Sie erhalten möglicherweise eine SMS und es können Gebühren für die Nachricht und die Datenübertragung anfallen. Authentifizierungsfehler Erneut versuchen Zusätzliche Verifizierung erforderlich. Bitte schließen Sie die Multi-Faktor-Authentifizierung ab. @@ -172,25 +158,20 @@ Verifizieren Bitte verifizieren Sie %1$s, um fortzufahren. - Wählen Sie eine Verifizierungsmethode - Fügen Sie eine zusätzliche Sicherheitsebene hinzu - SMS - Authenticator-App - Diese Telefonnummer ist mit einem anderen Konto verknüpft - Verifizierung erforderlich - Scannen Sie den QR-Code mit Ihrer Authenticator-App - Authenticator-App ist bereits eingerichtet - Möchten Sie diese Methode wirklich entfernen? - Methode kann nicht entfernt werden - Methode entfernt - Geben Sie den Verifizierungscode ein - Profil kann nicht aktualisiert werden - Profil aktualisiert - Bestätigen Sie Ihre Identität, um fortzufahren - Erneute Authentifizierung erforderlich - Erneute Authentifizierung erfolgreich - Erneut authentifizieren Die Multi-Faktor-Authentifizierung ist derzeit deaktiviert + E-Mail-Adresse oder Passwort ist nicht korrekt + Diese Bestätigungssitzung ist nicht mehr gültig. Fordern Sie einen neuen Code an. + Die Telefonbestätigung wurde nicht abgeschlossen. Versuchen Sie es erneut. + Diese Anmeldedaten gehören zu einem anderen Konto. + Diese Telefonnummer ist für dieses Konto nicht zur Bestätigung eingerichtet. + Ihre Anmeldesitzung ist abgelaufen. Melden Sie sich erneut an, um fortzufahren. + Dieser Link ist nicht mehr gültig. Fordern Sie einen neuen an. + Bestätigen Sie Ihre E-Mail-Adresse, bevor Sie fortfahren. + Diese Bestätigungsmethode ist für dieses Konto bereits eingerichtet. + Sie haben die maximale Anzahl an Bestätigungsmethoden für dieses Konto erreicht. + Ihr Passwort erfüllt die Anforderungen nicht. Versuchen Sie es mit einem anderen. + Das Passwort ist zu lang. Die maximale Länge beträgt %1$d. + Für dieses Konto wurde kein Passkey gefunden. Melden Sie sich auf andere Weise an. diff --git a/auth/src/main/res/values-el/strings.xml b/auth/src/main/res/values-el/strings.xml index 0854ec409f..dec7b633fb 100755 --- a/auth/src/main/res/values-el/strings.xml +++ b/auth/src/main/res/values-el/strings.xml @@ -3,7 +3,6 @@ Σύνδεση Συνέχεια Αν συνεχίσετε, δηλώνετε ότι αποδέχεστε τους %1$s και την %2$s. - %1$s \u00A0 \u00A0 %2$s Σφάλμα δικτύου, ελέγξτε τη σύνδεση στο διαδίκτυο. Google Facebook @@ -41,12 +40,9 @@ Αυτό το πεδίο είναι υποχρεωτικό. Αυτή η διεύθυνση ηλεκτρονικού ταχυδρομείου δεν είναι σωστή Εισαγάγετε τη διεύθυνση ηλεκτρονικού ταχυδρομείου για να συνεχίσετε - Εισαγάγετε ένα όνομα και ένα επώνυμο. - Γίνεται έλεγχος για υπάρχοντες λογαριασμούς… Εγγραφή Όνομα και επώνυμο Αποθήκευση - Γίνεται εγγραφή… Ο κωδικός πρόσβασης δεν είναι αρκετά ισχυρός. Χρησιμοποιήστε τουλάχιστον %1$d χαρακτήρα και έναν συνδυασμό γραμμάτων και αριθμών Ο κωδικός πρόσβασης δεν είναι αρκετά ισχυρός. Χρησιμοποιήστε τουλάχιστον %1$d χαρακτήρες και έναν συνδυασμό γραμμάτων και αριθμών Η εγγραφή του λογαριασμού ηλεκτρονικού ταχυδρομείου δεν ήταν επιτυχής Όρους Παροχής Υπηρεσιών @@ -54,17 +50,12 @@ Έχετε ήδη λογαριασμό Καλώς ορίσατε και πάλι! Έχετε ήδη λογαριασμό\n - Έχετε ήδη χρησιμοποιήσει τη διεύθυνση %1$s. Συνδεθείτε μέσω %2$s για να συνεχίσετε. - Έχετε ήδη χρησιμοποιήσει τη διεύθυνση %1$s για να συνδεθείτε. Εισαγάγετε τον κωδικό πρόσβασής σας για αυτόν τον λογαριασμό. - Χρησιμοποιήσατε ήδη το %1$s. Για να συνδέσετε τον λογαριασμό σας %2$s με το %1$s, συνδεθείτε στον σύνδεσμο ηλεκτρονικού ταχυδρομείου παρακάτω.\n\nΣε αυτήν τη ροή για την επιτυχή σύνδεση του λογαριασμού %2$s με αυτήν τη διεύθυνση ηλεκτρονικού ταχυδρομείου, θα πρέπει να ανοίξετε τον σύνδεσμο στην ίδια συσκευή ή στο ίδιο πρόγραμμα περιήγησης. - Γίνεται σύνδεση… Πρόβλημα κατά τη σύνδεση; Ανάκτηση κωδικού πρόσβασης Ελέγξτε τα εισερχόμενα του ηλεκτρονικού ταχυδρομείου σας Δείτε τις οδηγίες που στάλθηκαν σε αυτήν τη διεύθυνση ηλεκτρονικού ταχυδρομείου, οι οποίες εξηγούν πώς να επαναφέρετε τον κωδικό πρόσβασής σας. Αποστολή Ακολουθήστε τις οδηγίες που στάλθηκαν στο %1$s για να ανακτήσετε τον κωδικό πρόσβασής σας. - Γίνεται αποστολή… Αυτή η διεύθυνση ηλεκτρονικού ταχυδρομείου δεν ταιριάζει με κάποιον υπάρχοντα λογαριασμό Παρουσιάστηκε άγνωστο σφάλμα. Λανθασμένος κωδικός πρόσβασης. @@ -90,25 +81,20 @@ Καταχωρίστε έναν έγκυρο αριθμό τηλεφώνου Εισαγάγετε τον 6ψήφιο κωδικό που σας στείλαμε στο Επανάληψη αποστολής κωδικού σε %1$s - Επαλήθευση του τηλεφώνου σας Επαλήθευση… Εσφαλμένος κωδικός. Δοκιμάστε ξανά. Αυτός ο αριθμός τηλεφώνου χρησιμοποιήθηκε πάρα πολλές φορές - Παρουσιάστηκε πρόβλημα με την επαλήθευση του αριθμού τηλεφώνου Αυτός ο κωδικός δεν είναι πλέον έγκυρος - Σύνδεση με αριθμό τηλεφώνου - Ο αριθμός τηλεφώνου επαληθεύτηκε αυτόματα Επανάληψη αποστολής κωδικού Επαλήθευση αριθμού τηλεφώνου - Use a different phone number + Χρήση διαφορετικού αριθμού τηλεφώνου Αν πατήσετε “%1$s”, μπορεί να σταλεί ένα SMS. Ενδέχεται να ισχύουν χρεώσεις μηνυμάτων και δεδομένων. - Αν πατήσετε “%1$s”, δηλώνετε ότι αποδέχεστε τους %2$s και την %3$s. Μπορεί να σταλεί ένα SMS. Ενδέχεται να ισχύουν χρεώσεις μηνυμάτων και δεδομένων. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Σφάλμα ελέγχου ταυτότητας + Δοκιμάστε ξανά + Απαιτείται πρόσθετη επαλήθευση. Ολοκληρώστε τον έλεγχο ταυτότητας πολλαπλών παραγόντων. + Ο λογαριασμός πρέπει να συνδεθεί. Δοκιμάστε διαφορετική μέθοδο σύνδεσης. + Ο έλεγχος ταυτότητας ακυρώθηκε. Δοκιμάστε ξανά όταν είστε έτοιμοι. Επιλέξτε μέθοδο ελέγχου ταυτότητας @@ -144,29 +130,11 @@ Η ταυτότητα επαληθεύτηκε. Δοκιμάστε ξανά την ενέργειά σας. Διαχείριση ελέγχου ταυτότητας δύο παραγόντων Εσφαλμένος κωδικός πρόσβασης - Επιλέξτε μέθοδο επαλήθευσης - Προσθέστε ένα επιπλέον επίπεδο ασφαλείας - SMS - Εφαρμογή ελέγχου ταυτότητας - Αυτός ο αριθμός τηλεφώνου σχετίζεται με άλλο λογαριασμό - Απαιτείται επαλήθευση - Σαρώστε τον κωδικό QR με την εφαρμογή ελέγχου ταυτότητας - Η εφαρμογή ελέγχου ταυτότητας έχει ήδη ρυθμιστεί - Θέλετε σίγουρα να καταργήσετε αυτήν τη μέθοδο; - Δεν είναι δυνατή η κατάργηση της μεθόδου - Η μέθοδος καταργήθηκε - Εισαγάγετε τον κωδικό επαλήθευσης - Δεν είναι δυνατή η ενημέρωση του προφίλ - Το προφίλ ενημερώθηκε - Επιβεβαιώστε την ταυτότητά σας για να συνεχίσετε - Απαιτείται επανέλεγχος ταυτότητας - Ο επανέλεγχος ταυτότητας ολοκληρώθηκε - Επανέλεγχος Κατάργηση Επαναποστολή email επαλήθευσης Μυστικό κλειδί Αποσύνδεση - Συνδεδεμένος ως + Συνδεδεμένος ως %1$s Παράλειψη Χρήση διαφορετικής μεθόδου Κωδικός επαλήθευσης @@ -176,4 +144,17 @@ Ο έλεγχος ταυτότητας πολλαπλών παραγόντων είναι απενεργοποιημένος προς το παρόν + Το ηλεκτρονικό ταχυδρομείο ή ο κωδικός πρόσβασης δεν είναι σωστά + Αυτή η περίοδος επαλήθευσης δεν είναι πλέον έγκυρη. Ζητήστε νέο κωδικό. + Η επαλήθευση τηλεφώνου δεν ολοκληρώθηκε. Δοκιμάστε ξανά. + Αυτά τα διαπιστευτήρια ανήκουν σε διαφορετικό λογαριασμό. + Αυτός ο αριθμός τηλεφώνου δεν έχει ρυθμιστεί για επαλήθευση σε αυτόν τον λογαριασμό. + Η περίοδος σύνδεσής σας έληξε. Συνδεθείτε ξανά για να συνεχίσετε. + Αυτός ο σύνδεσμος δεν είναι πλέον έγκυρος. Ζητήστε νέον. + Επαληθεύστε τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας πριν συνεχίσετε. + Αυτή η μέθοδος επαλήθευσης έχει ήδη ρυθμιστεί σε αυτόν τον λογαριασμό. + Έχετε φτάσει το όριο μεθόδων επαλήθευσης για αυτόν τον λογαριασμό. + Ο κωδικός πρόσβασής σας δεν πληροί τις προϋποθέσεις. Δοκιμάστε έναν άλλον. + Ο κωδικός πρόσβασης είναι πολύ μεγάλος. Το μέγιστο μήκος είναι %1$d. + Δεν βρέθηκε κλειδί πρόσβασης για αυτόν τον λογαριασμό. Συνδεθείτε με άλλον τρόπο. diff --git a/auth/src/main/res/values-en-rAU/strings.xml b/auth/src/main/res/values-en-rAU/strings.xml index 7f8dab7e63..abeb637841 100755 --- a/auth/src/main/res/values-en-rAU/strings.xml +++ b/auth/src/main/res/values-en-rAU/strings.xml @@ -3,7 +3,6 @@ Sign in Continue By continuing, you are indicating that you accept our %1$s and %2$s. - %1$s \u00A0 \u00A0 %2$s Network error, check your Internet connection. Google Facebook @@ -41,12 +40,9 @@ You can\'t leave this empty. Incorrect email address Enter your email address to continue - Please enter a first name and surname. - Checking for existing accounts… Sign up First name & surname Save - Signing up… Password not strong enough. Use at least %1$d character and a mix of letters and numbers Password not strong enough. Use at least %1$d characters and a mix of letters and numbers Email account registration unsuccessful Terms of Service @@ -54,17 +50,12 @@ You already have an account Welcome back! You already have an account\n - You\'ve already used %1$s. Sign in with %2$s to continue. - You\'ve already used %1$s to sign in. Enter your password for that account. - You\'ve already used %1$s. You can connect your %2$s account with %1$s by signing in with the email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser. - Signing in… Trouble signing in? Recover password Check your email Receive instructions to this email that explain how to reset your password. Send Follow the instructions sent to %1$s to recover your password. - Sending… That email address doesn\'t match an existing account An unknown error occurred. Incorrect password. @@ -90,19 +81,14 @@ Enter a valid phone number Enter the 6-digit code that we sent to Resend code in %1$s - Verify your phone number Verifying… Wrong code. Try again. This phone number has been used too many times - There was a problem verifying your phone number This code is no longer valid - Sign in with phone number - Phone number automatically verified Resend code Verify Phone Number Use a different phone number By tapping “%1$s”, an SMS may be sent. Message & data rates may apply. - By tapping "%1$s", you are indicating that you accept our %2$s and %3$s. An SMS may be sent. Message & data rates may apply. Authentication Error Try again Additional verification required. Please complete multi-factor authentication. @@ -124,7 +110,7 @@ Confirm password Passwords do not match - Password must be at least %1$d characters long + Password must be at least %1$d characters long Password must contain at least one uppercase letter Password must contain at least one lowercase letter Password must contain at least one number @@ -143,29 +129,11 @@ Identity verified. Retry your action. Manage two-factor authentication Incorrect password - Choose a verification method - Add an extra layer of security - SMS - Authenticator app - This phone number is associated with another account - Verification required - Scan the QR code with your authenticator app - Authenticator app is already set up - Are you sure you want to remove this method? - Unable to remove method - Method removed - Enter the verification code - Unable to update profile - Profile updated - Confirm your identity to continue - Re-authentication required - Re-authentication successful - Re-authenticate Remove Resend verification email Secret key Sign out - Signed in as + Signed in as %1$s Skip Use a different method Verification code @@ -175,4 +143,17 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rCA/strings.xml b/auth/src/main/res/values-en-rCA/strings.xml index 2476ca4b8b..2fe6ccd0bb 100755 --- a/auth/src/main/res/values-en-rCA/strings.xml +++ b/auth/src/main/res/values-en-rCA/strings.xml @@ -3,7 +3,6 @@ Sign in Continue By continuing, you are indicating that you accept our %1$s and %2$s. - %1$s \u00A0 \u00A0 %2$s Network error, check your Internet connection. Google Facebook @@ -41,12 +40,9 @@ You can\'t leave this empty. Incorrect email address Enter your email address to continue - Please enter a first name and surname. - Checking for existing accounts… Sign up First name & surname Save - Signing up… Password not strong enough. Use at least %1$d character and a mix of letters and numbers Password not strong enough. Use at least %1$d characters and a mix of letters and numbers Email account registration unsuccessful Terms of Service @@ -54,17 +50,12 @@ You already have an account Welcome back! You already have an account\n - You\'ve already used %1$s. Sign in with %2$s to continue. - You\'ve already used %1$s to sign in. Enter your password for that account. - You\'ve already used %1$s. You can connect your %2$s account with %1$s by signing in with the email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser. - Signing in… Trouble signing in? Recover password Check your email Receive instructions to this email that explain how to reset your password. Send Follow the instructions sent to %1$s to recover your password. - Sending… That email address doesn\'t match an existing account An unknown error occurred. Incorrect password. @@ -90,19 +81,14 @@ Enter a valid phone number Enter the 6-digit code that we sent to Resend code in %1$s - Verify your phone number Verifying… Wrong code. Try again. This phone number has been used too many times - There was a problem verifying your phone number This code is no longer valid - Sign in with phone number - Phone number automatically verified Resend code Verify Phone Number Use a different phone number By tapping “%1$s”, an SMS may be sent. Message & data rates may apply. - By tapping "%1$s", you are indicating that you accept our %2$s and %3$s. An SMS may be sent. Message & data rates may apply. Authentication Error Try again Additional verification required. Please complete multi-factor authentication. @@ -124,7 +110,7 @@ Confirm password Passwords do not match - Password must be at least %1$d characters long + Password must be at least %1$d characters long Password must contain at least one upper-case letter Password must contain at least one lower-case letter Password must contain at least one number @@ -143,29 +129,11 @@ Identity verified. Retry your action. Manage two-factor authentication Incorrect password - Choose a verification method - Add an extra layer of security - SMS - Authenticator app - This phone number is associated with another account - Verification required - Scan the QR code with your authenticator app - Authenticator app is already set up - Are you sure you want to remove this method? - Unable to remove method - Method removed - Enter the verification code - Unable to update profile - Profile updated - Confirm your identity to continue - Re-authentication required - Re-authentication successful - Re-authenticate Remove Resend verification email Secret key Sign out - Signed in as + Signed in as %1$s Skip Use a different method Verification code @@ -175,4 +143,17 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rGB/strings.xml b/auth/src/main/res/values-en-rGB/strings.xml index 8c19093629..067e59b004 100755 --- a/auth/src/main/res/values-en-rGB/strings.xml +++ b/auth/src/main/res/values-en-rGB/strings.xml @@ -3,7 +3,6 @@ Sign in Continue By continuing, you are indicating that you accept our %1$s and %2$s. - %1$s \u00A0 \u00A0 %2$s Network error, check your Internet connection. Google Facebook @@ -41,12 +40,9 @@ You can\'t leave this empty. Incorrect email address Enter your email address to continue - Please enter a first name and surname. - Checking for existing accounts… Sign up First name & surname Save - Signing up… Password not strong enough. Use at least %1$d character and a mix of letters and numbers Password not strong enough. Use at least %1$d characters and a mix of letters and numbers Email account registration unsuccessful Terms of Service @@ -54,17 +50,12 @@ You already have an account Welcome back! You already have an account\n - You\'ve already used %1$s. Sign in with %2$s to continue. - You\'ve already used %1$s to sign in. Enter your password for that account. - You\'ve already used %1$s. You can connect your %2$s account with %1$s by signing in with the email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser. - Signing in… Trouble signing in? Recover password Check your email Receive instructions to this email that explain how to reset your password. Send Follow the instructions sent to %1$s to recover your password. - Sending… That email address doesn\'t match an existing account An unknown error occurred. Incorrect password. @@ -90,19 +81,14 @@ Enter a valid phone number Enter the 6-digit code that we sent to Resend code in %1$s - Verify your phone number Verifying… Wrong code. Try again. This phone number has been used too many times - There was a problem verifying your phone number This code is no longer valid - Sign in with phone number - Phone number automatically verified Resend code Verify Phone Number Use a different phone number By tapping “%1$s”, an SMS may be sent. Message & data rates may apply. - By tapping "%1$s", you are indicating that you accept our %2$s and %3$s. An SMS may be sent. Message & data rates may apply. Authentication Error Try again Additional verification required. Please complete multi-factor authentication. @@ -124,7 +110,7 @@ Confirm password Passwords do not match - Password must be at least %1$d characters long + Password must be at least %1$d characters long Password must contain at least one upper-case letter Password must contain at least one lower-case letter Password must contain at least one number @@ -143,29 +129,11 @@ Identity verified. Retry your action. Manage two-factor authentication Incorrect password - Choose a verification method - Add an extra layer of security - SMS - Authenticator app - This phone number is associated with another account - Verification required - Scan the QR code with your authenticator app - Authenticator app is already set up - Are you sure you want to remove this method? - Unable to remove method - Method removed - Enter the verification code - Unable to update profile - Profile updated - Confirm your identity to continue - Re-authentication required - Re-authentication successful - Re-authenticate Remove Resend verification email Secret key Sign out - Signed in as + Signed in as %1$s Skip Use a different method Verification code @@ -175,4 +143,17 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rIE/strings.xml b/auth/src/main/res/values-en-rIE/strings.xml index ee4d48e585..4f0240cd78 100755 --- a/auth/src/main/res/values-en-rIE/strings.xml +++ b/auth/src/main/res/values-en-rIE/strings.xml @@ -3,7 +3,6 @@ Sign in Continue By continuing, you are indicating that you accept our %1$s and %2$s. - %1$s \u00A0 \u00A0 %2$s Network error, check your Internet connection. Google Facebook @@ -41,12 +40,9 @@ You can\'t leave this empty. Incorrect email address Enter your email address to continue - Please enter a first name and surname. - Checking for existing accounts… Sign up First name & surname Save - Signing up… Password not strong enough. Use at least %1$d character and a mix of letters and numbers Password not strong enough. Use at least %1$d characters and a mix of letters and numbers Email account registration unsuccessful Terms of Service @@ -54,17 +50,12 @@ You already have an account Welcome back! You already have an account\n - You\'ve already used %1$s. Sign in with %2$s to continue. - You\'ve already used %1$s to sign in. Enter your password for that account. - You\'ve already used %1$s. You can connect your %2$s account with %1$s by signing in with the email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser. - Signing in… Trouble signing in? Recover password Check your email Receive instructions to this email that explain how to reset your password. Send Follow the instructions sent to %1$s to recover your password. - Sending… That email address doesn\'t match an existing account An unknown error occurred. Incorrect password. @@ -90,19 +81,14 @@ Enter a valid phone number Enter the 6-digit code that we sent to Resend code in %1$s - Verify your phone number Verifying… Wrong code. Try again. This phone number has been used too many times - There was a problem verifying your phone number This code is no longer valid - Sign in with phone number - Phone number automatically verified Resend code Verify Phone Number Use a different phone number By tapping “%1$s”, an SMS may be sent. Message & data rates may apply. - By tapping "%1$s", you are indicating that you accept our %2$s and %3$s. An SMS may be sent. Message & data rates may apply. Authentication Error Try again Additional verification required. Please complete multi-factor authentication. @@ -136,29 +122,11 @@ Identity verified. Retry your action. Manage two-factor authentication Incorrect password - Choose a verification method - Add an extra layer of security - SMS - Authenticator app - This phone number is associated with another account - Verification required - Scan the QR code with your authenticator app - Authenticator app is already set up - Are you sure you want to remove this method? - Unable to remove method - Method removed - Enter the verification code - Unable to update profile - Profile updated - Confirm your identity to continue - Re-authentication required - Re-authentication successful - Re-authenticate Remove Resend verification email Secret key Sign out - Signed in as + Signed in as %1$s Skip Use a different method Verification code @@ -168,4 +136,17 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rIN/strings.xml b/auth/src/main/res/values-en-rIN/strings.xml index ee4d48e585..4f0240cd78 100755 --- a/auth/src/main/res/values-en-rIN/strings.xml +++ b/auth/src/main/res/values-en-rIN/strings.xml @@ -3,7 +3,6 @@ Sign in Continue By continuing, you are indicating that you accept our %1$s and %2$s. - %1$s \u00A0 \u00A0 %2$s Network error, check your Internet connection. Google Facebook @@ -41,12 +40,9 @@ You can\'t leave this empty. Incorrect email address Enter your email address to continue - Please enter a first name and surname. - Checking for existing accounts… Sign up First name & surname Save - Signing up… Password not strong enough. Use at least %1$d character and a mix of letters and numbers Password not strong enough. Use at least %1$d characters and a mix of letters and numbers Email account registration unsuccessful Terms of Service @@ -54,17 +50,12 @@ You already have an account Welcome back! You already have an account\n - You\'ve already used %1$s. Sign in with %2$s to continue. - You\'ve already used %1$s to sign in. Enter your password for that account. - You\'ve already used %1$s. You can connect your %2$s account with %1$s by signing in with the email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser. - Signing in… Trouble signing in? Recover password Check your email Receive instructions to this email that explain how to reset your password. Send Follow the instructions sent to %1$s to recover your password. - Sending… That email address doesn\'t match an existing account An unknown error occurred. Incorrect password. @@ -90,19 +81,14 @@ Enter a valid phone number Enter the 6-digit code that we sent to Resend code in %1$s - Verify your phone number Verifying… Wrong code. Try again. This phone number has been used too many times - There was a problem verifying your phone number This code is no longer valid - Sign in with phone number - Phone number automatically verified Resend code Verify Phone Number Use a different phone number By tapping “%1$s”, an SMS may be sent. Message & data rates may apply. - By tapping "%1$s", you are indicating that you accept our %2$s and %3$s. An SMS may be sent. Message & data rates may apply. Authentication Error Try again Additional verification required. Please complete multi-factor authentication. @@ -136,29 +122,11 @@ Identity verified. Retry your action. Manage two-factor authentication Incorrect password - Choose a verification method - Add an extra layer of security - SMS - Authenticator app - This phone number is associated with another account - Verification required - Scan the QR code with your authenticator app - Authenticator app is already set up - Are you sure you want to remove this method? - Unable to remove method - Method removed - Enter the verification code - Unable to update profile - Profile updated - Confirm your identity to continue - Re-authentication required - Re-authentication successful - Re-authenticate Remove Resend verification email Secret key Sign out - Signed in as + Signed in as %1$s Skip Use a different method Verification code @@ -168,4 +136,17 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rSG/strings.xml b/auth/src/main/res/values-en-rSG/strings.xml index ee4d48e585..4f0240cd78 100755 --- a/auth/src/main/res/values-en-rSG/strings.xml +++ b/auth/src/main/res/values-en-rSG/strings.xml @@ -3,7 +3,6 @@ Sign in Continue By continuing, you are indicating that you accept our %1$s and %2$s. - %1$s \u00A0 \u00A0 %2$s Network error, check your Internet connection. Google Facebook @@ -41,12 +40,9 @@ You can\'t leave this empty. Incorrect email address Enter your email address to continue - Please enter a first name and surname. - Checking for existing accounts… Sign up First name & surname Save - Signing up… Password not strong enough. Use at least %1$d character and a mix of letters and numbers Password not strong enough. Use at least %1$d characters and a mix of letters and numbers Email account registration unsuccessful Terms of Service @@ -54,17 +50,12 @@ You already have an account Welcome back! You already have an account\n - You\'ve already used %1$s. Sign in with %2$s to continue. - You\'ve already used %1$s to sign in. Enter your password for that account. - You\'ve already used %1$s. You can connect your %2$s account with %1$s by signing in with the email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser. - Signing in… Trouble signing in? Recover password Check your email Receive instructions to this email that explain how to reset your password. Send Follow the instructions sent to %1$s to recover your password. - Sending… That email address doesn\'t match an existing account An unknown error occurred. Incorrect password. @@ -90,19 +81,14 @@ Enter a valid phone number Enter the 6-digit code that we sent to Resend code in %1$s - Verify your phone number Verifying… Wrong code. Try again. This phone number has been used too many times - There was a problem verifying your phone number This code is no longer valid - Sign in with phone number - Phone number automatically verified Resend code Verify Phone Number Use a different phone number By tapping “%1$s”, an SMS may be sent. Message & data rates may apply. - By tapping "%1$s", you are indicating that you accept our %2$s and %3$s. An SMS may be sent. Message & data rates may apply. Authentication Error Try again Additional verification required. Please complete multi-factor authentication. @@ -136,29 +122,11 @@ Identity verified. Retry your action. Manage two-factor authentication Incorrect password - Choose a verification method - Add an extra layer of security - SMS - Authenticator app - This phone number is associated with another account - Verification required - Scan the QR code with your authenticator app - Authenticator app is already set up - Are you sure you want to remove this method? - Unable to remove method - Method removed - Enter the verification code - Unable to update profile - Profile updated - Confirm your identity to continue - Re-authentication required - Re-authentication successful - Re-authenticate Remove Resend verification email Secret key Sign out - Signed in as + Signed in as %1$s Skip Use a different method Verification code @@ -168,4 +136,17 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-en-rZA/strings.xml b/auth/src/main/res/values-en-rZA/strings.xml index ee4d48e585..4f0240cd78 100755 --- a/auth/src/main/res/values-en-rZA/strings.xml +++ b/auth/src/main/res/values-en-rZA/strings.xml @@ -3,7 +3,6 @@ Sign in Continue By continuing, you are indicating that you accept our %1$s and %2$s. - %1$s \u00A0 \u00A0 %2$s Network error, check your Internet connection. Google Facebook @@ -41,12 +40,9 @@ You can\'t leave this empty. Incorrect email address Enter your email address to continue - Please enter a first name and surname. - Checking for existing accounts… Sign up First name & surname Save - Signing up… Password not strong enough. Use at least %1$d character and a mix of letters and numbers Password not strong enough. Use at least %1$d characters and a mix of letters and numbers Email account registration unsuccessful Terms of Service @@ -54,17 +50,12 @@ You already have an account Welcome back! You already have an account\n - You\'ve already used %1$s. Sign in with %2$s to continue. - You\'ve already used %1$s to sign in. Enter your password for that account. - You\'ve already used %1$s. You can connect your %2$s account with %1$s by signing in with the email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser. - Signing in… Trouble signing in? Recover password Check your email Receive instructions to this email that explain how to reset your password. Send Follow the instructions sent to %1$s to recover your password. - Sending… That email address doesn\'t match an existing account An unknown error occurred. Incorrect password. @@ -90,19 +81,14 @@ Enter a valid phone number Enter the 6-digit code that we sent to Resend code in %1$s - Verify your phone number Verifying… Wrong code. Try again. This phone number has been used too many times - There was a problem verifying your phone number This code is no longer valid - Sign in with phone number - Phone number automatically verified Resend code Verify Phone Number Use a different phone number By tapping “%1$s”, an SMS may be sent. Message & data rates may apply. - By tapping "%1$s", you are indicating that you accept our %2$s and %3$s. An SMS may be sent. Message & data rates may apply. Authentication Error Try again Additional verification required. Please complete multi-factor authentication. @@ -136,29 +122,11 @@ Identity verified. Retry your action. Manage two-factor authentication Incorrect password - Choose a verification method - Add an extra layer of security - SMS - Authenticator app - This phone number is associated with another account - Verification required - Scan the QR code with your authenticator app - Authenticator app is already set up - Are you sure you want to remove this method? - Unable to remove method - Method removed - Enter the verification code - Unable to update profile - Profile updated - Confirm your identity to continue - Re-authentication required - Re-authentication successful - Re-authenticate Remove Resend verification email Secret key Sign out - Signed in as + Signed in as %1$s Skip Use a different method Verification code @@ -168,4 +136,17 @@ Multi-factor authentication is currently disabled + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + Password is too long. The maximum length is %1$d. + We couldn\'t find a passkey for this account. Sign in another way. diff --git a/auth/src/main/res/values-es-rAR/strings.xml b/auth/src/main/res/values-es-rAR/strings.xml index 6e6ec21946..5952f377ae 100755 --- a/auth/src/main/res/values-es-rAR/strings.xml +++ b/auth/src/main/res/values-es-rAR/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rBO/strings.xml b/auth/src/main/res/values-es-rBO/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rBO/strings.xml +++ b/auth/src/main/res/values-es-rBO/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rCL/strings.xml b/auth/src/main/res/values-es-rCL/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rCL/strings.xml +++ b/auth/src/main/res/values-es-rCL/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rCO/strings.xml b/auth/src/main/res/values-es-rCO/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rCO/strings.xml +++ b/auth/src/main/res/values-es-rCO/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rCR/strings.xml b/auth/src/main/res/values-es-rCR/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rCR/strings.xml +++ b/auth/src/main/res/values-es-rCR/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rDO/strings.xml b/auth/src/main/res/values-es-rDO/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rDO/strings.xml +++ b/auth/src/main/res/values-es-rDO/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rEC/strings.xml b/auth/src/main/res/values-es-rEC/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rEC/strings.xml +++ b/auth/src/main/res/values-es-rEC/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rGT/strings.xml b/auth/src/main/res/values-es-rGT/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rGT/strings.xml +++ b/auth/src/main/res/values-es-rGT/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rHN/strings.xml b/auth/src/main/res/values-es-rHN/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rHN/strings.xml +++ b/auth/src/main/res/values-es-rHN/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rMX/strings.xml b/auth/src/main/res/values-es-rMX/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rMX/strings.xml +++ b/auth/src/main/res/values-es-rMX/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rNI/strings.xml b/auth/src/main/res/values-es-rNI/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rNI/strings.xml +++ b/auth/src/main/res/values-es-rNI/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rPA/strings.xml b/auth/src/main/res/values-es-rPA/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rPA/strings.xml +++ b/auth/src/main/res/values-es-rPA/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rPE/strings.xml b/auth/src/main/res/values-es-rPE/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rPE/strings.xml +++ b/auth/src/main/res/values-es-rPE/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rPR/strings.xml b/auth/src/main/res/values-es-rPR/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rPR/strings.xml +++ b/auth/src/main/res/values-es-rPR/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rPY/strings.xml b/auth/src/main/res/values-es-rPY/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rPY/strings.xml +++ b/auth/src/main/res/values-es-rPY/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rSV/strings.xml b/auth/src/main/res/values-es-rSV/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rSV/strings.xml +++ b/auth/src/main/res/values-es-rSV/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rUS/strings.xml b/auth/src/main/res/values-es-rUS/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rUS/strings.xml +++ b/auth/src/main/res/values-es-rUS/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rUY/strings.xml b/auth/src/main/res/values-es-rUY/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rUY/strings.xml +++ b/auth/src/main/res/values-es-rUY/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es-rVE/strings.xml b/auth/src/main/res/values-es-rVE/strings.xml index f95dda99e2..9c49949bd9 100755 --- a/auth/src/main/res/values-es-rVE/strings.xml +++ b/auth/src/main/res/values-es-rVE/strings.xml @@ -3,7 +3,6 @@ Acceder Continuar Si continúas, indicas que aceptas nuestras %1$s y %2$s. - %1$s     %2$s Se produjo un error de red. Comprueba tu conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ Este campo no puede estar en blanco. La dirección de correo electrónico es incorrecta Ingresa tu dirección de correo electrónico para continuar - Ingresa un nombre y un apellido. - Buscando cuentas existentes… Registrarse Nombre y apellido Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números + La contraseña no es lo suficientemente segura. Usa al menos %1$d carácter y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d de caracteres y una combinación de letras y números La contraseña no es lo suficientemente segura. Usa al menos %1$d caracteres y una combinación de letras y números No se pudo registrar la cuenta de correo electrónico Condiciones del Servicio Política de Privacidad Ya tienes una cuenta ¡Te damos la bienvenida nuevamente! Ya tienes una cuenta\n - Ya usaste %1$s. Accede con %2$s para continuar. - Ya usaste %1$s para acceder. Ingresa la contraseña correspondiente. - Ya usaste %1$s. Si quieres conectar tu cuenta de %2$s con %1$s, accede mediante el vínculo del correo electrónico que aparece a continuación.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el vínculo en el mismo dispositivo o navegador. - Accediendo… ¿Tienes problemas para acceder? Recuperar contraseña Revisa tu correo electrónico Recibe un correo electrónico con instrucciones para cambiar la contraseña. Enviar Sigue las instrucciones que se enviaron a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con una cuenta existente. Se produjo un error desconocido. Contraseña incorrecta. @@ -90,19 +81,14 @@ Ingresa un número de teléfono válido Ingresa el código de 6 dígitos que enviamos al número Se reenviará el código en %1$s - Verifica tu número de teléfono Verificando… Código incorrecto. Vuelve a intentarlo. Este número de teléfono se usó demasiadas veces - Ocurrió un problema durante la verificación de tu número de teléfono Este código ya no es válido - Acceder con el número de teléfono - Se verificó automáticamente el número de teléfono Reenviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Si presionas “%1$s”, se enviará un SMS. Se aplicarán las tarifas de mensajes y datos. - Si presionas "%1$s", indicas que aceptas nuestras %2$s y %3$s. Es posible que se te envíe un SMS. Podrían aplicarse las tarifas de mensajes y datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -165,25 +151,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + No se completó la verificación telefónica. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión expiró. Vuelve a acceder para continuar. + Este vínculo ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Alcanzaste el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple con los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No encontramos una llave de acceso para esta cuenta. Accede de otra manera. diff --git a/auth/src/main/res/values-es/strings.xml b/auth/src/main/res/values-es/strings.xml index ac5767d33a..54d1ca74cc 100755 --- a/auth/src/main/res/values-es/strings.xml +++ b/auth/src/main/res/values-es/strings.xml @@ -3,7 +3,6 @@ Iniciar sesión Continuar Si continúas, confirmas que aceptas nuestras %1$s y nuestra %2$s. - %1$s     %2$s Se ha producido un error de red. Comprueba la conexión a Internet. Google Facebook @@ -41,30 +40,22 @@ No puedes dejar este campo en blanco. La dirección de correo electrónico no es correcta Para continuar, introduce tu dirección de correo electrónico - Introduce tu nombre y apellidos. - Buscando cuentas ya disponibles… Registrarse Nombre y apellidos Guardar - Registrando… - La contraseña no es lo suficientemente segura. Usa %1$d carácter como mínimo y combina letras y números. La contraseña no es lo suficientemente segura. Usa %1$d caracteres como mínimo y combina letras y números. + La contraseña no es lo suficientemente segura. Usa %1$d carácter como mínimo y combina letras y números. La contraseña no es lo suficientemente segura. Usa %1$d de caracteres como mínimo y combina letras y números. La contraseña no es lo suficientemente segura. Usa %1$d caracteres como mínimo y combina letras y números. No se ha podido registrar la cuenta de correo electrónico Términos del Servicio Política de Privacidad Ya tienes una cuenta ¡Hola de nuevo! Ya tienes una cuenta\n - Ya has usado %1$s. Para continuar, inicia sesión con %2$s. - Ya has usado %1$s para iniciar sesión. Introduce la contraseña de esa cuenta. - Ya has usado la dirección %1$s. Para conectar tu cuenta de %2$s con %1$s, inicia sesión a través del enlace de correo electrónico que aparece más adelante.\n\nPara que este flujo conecte correctamente tu cuenta de %2$s con este correo electrónico, debes abrir el enlace en el mismo dispositivo o navegador. - Iniciando sesión… ¿No puedes iniciar sesión? Recuperar contraseña Consulta tu correo electrónico Recibe un correo electrónico con instrucciones sobre cómo cambiar la contraseña. Enviar Sigue las instrucciones que te hemos enviado a %1$s para recuperar la contraseña. - Enviando… La dirección de correo electrónico no coincide con ninguna cuenta Se ha producido un error desconocido. La contraseña no es correcta. @@ -90,19 +81,14 @@ Introduce un número de teléfono válido Introduce el código de seis dígitos que hemos enviado a Volver a enviar el código en %1$s - Verificar el número de teléfono Verificando… El código es incorrecto. Vuelve a intentarlo. Este número de teléfono se ha usado demasiadas veces - No se ha podido verificar el número de teléfono Este código ya no es válido - Iniciar sesión con el número de teléfono - Se ha verificado automáticamente el número de teléfono Volver a enviar código Verificar número de teléfono - Use a different phone number + Usar otro número de teléfono Al tocar %1$s, podría enviarse un SMS. Es posible que se apliquen cargos de mensajería y de uso de datos. - Si tocas %1$s, confirmas que aceptas nuestras %2$s y nuestra %3$s. Podría enviarse un SMS, por lo que es posible que se apliquen cargos de mensajería y de uso de datos. Error de autenticación Intentar de nuevo Se requiere verificación adicional. Complete la autenticación de múltiples factores. @@ -172,25 +158,20 @@ Verificar Verifica %1$s para continuar. - Elige un método de verificación - Agrega una capa adicional de seguridad - SMS - Aplicación de autenticación - Este número de teléfono está asociado con otra cuenta - Verificación requerida - Escanea el código QR con tu aplicación de autenticación - La aplicación de autenticación ya está configurada - ¿Estás seguro de que quieres eliminar este método? - No se puede eliminar el método - Método eliminado - Ingresa el código de verificación - No se puede actualizar el perfil - Perfil actualizado - Confirma tu identidad para continuar - Se requiere reautenticación - Reautenticación exitosa - Reautenticar La autenticación multifactor está actualmente desactivada + El correo electrónico o la contraseña no son correctos + Esta sesión de verificación ya no es válida. Solicita un código nuevo. + La verificación telefónica no se ha completado. Vuelve a intentarlo. + Estas credenciales pertenecen a otra cuenta. + Este número de teléfono no está configurado para la verificación en esta cuenta. + Tu sesión ha caducado. Vuelve a iniciar sesión para continuar. + Este enlace ya no es válido. Solicita uno nuevo. + Verifica tu dirección de correo electrónico antes de continuar. + Este método de verificación ya está configurado en esta cuenta. + Has alcanzado el límite de métodos de verificación de esta cuenta. + Tu contraseña no cumple los requisitos. Prueba con otra. + La contraseña es demasiado larga. La longitud máxima es %1$d. + No hemos encontrado ninguna clave de acceso para esta cuenta. Inicia sesión de otra forma. diff --git a/auth/src/main/res/values-fa/strings.xml b/auth/src/main/res/values-fa/strings.xml index a7f6c74701..36303088b6 100755 --- a/auth/src/main/res/values-fa/strings.xml +++ b/auth/src/main/res/values-fa/strings.xml @@ -3,7 +3,6 @@ ورود به سیستم ادامه درصورت ادامه‌دادن، موافقتتان را با %1$s و %2$s اعلام می‌کنید. - %1$s \u00A0 \u00A0 %2$s خطای شبکه. اتصال اینترنت را بررسی کنید. Google‏ Facebook @@ -41,12 +40,9 @@ اینجا نباید خالی باشد. آن نشانی ایمیل درست نیست برای ادامه، آدرس ایمیلتان را وارد کنید - لطفاً نام و نام خانوادگی را وارد کنید. - در حال بررسی حساب‌های موجود… ثبت‌نام نام و نام خانوادگی ذخیره - در حال ثبت‌نام… گذرواژه به اندازه کافی قوی نیست. حداقل باید از %1$d نویسه و ترکیبی از حروف و اعداد استفاده شود گذرواژه به اندازه کافی قوی نیست. حداقل باید از %1$d نویسه و ترکیبی از حروف و اعداد استفاده شود ثبت حساب ایمیل ناموفق بود شرایط خدمات @@ -54,17 +50,12 @@ از قبل حسابی دارید باز هم خوش آمدید! از قبل حسابی دارید\n - قبلاً از %1$s استفاده کرده‌اید. برای ادامه، با %2$s وارد سیستم شوید. - قبلاً از %1$s برای ورود به سیستم استفاده کرده‌اید. گذرواژه این حساب را وارد کنید. - قبلاً از %1$s استفاده کرده‌اید. می‌توانید با ورود به سیستم ازطریق پیوند ایمیل زیر، حساب %2$s را به %1$s وصل کنید.\n\nبرای اینکه این فرایند بتواند با موفقیت حساب %2$s شما را به این ایمیل متصل کند، باید پیوند را در همان دستگاه یا مرورگر باز کنید. - درحال ورود به سیستم… مشکل در ورود به سیستم؟ بازیابی گذرواژه رایانامه‌تان را بررسی کنید دستورالعمل نحوه بازنشانی گذرواژه‌تان به این ایمیل ارسال شده است، آن را ببینید. ارسال برای بازیابی گذرواژه‌تان، دستورالعمل‌های ارسال‌شده به %1$s را دنبال کنید. - درحال ارسال… هیچ حسابی منطبق با این نشانی ایمیل وجود ندارد خطای ناشناس رخ داد. گذرواژه نادرست @@ -90,25 +81,20 @@ شماره تلفن معتبری وارد کنید وارد کردن کد ۶ رقمی ارسال‌شده به کد پس از %1$s:0 مجدداً ارسال می‌شود - تأیید شماره تلفن درحال تأیید… کد اشتباه است. دوباره امتحان کنید. از این شماره تلفن به دفعات زیاد استفاده شده است - هنگام تأیید شماره تلفنتان مشکلی پیش آمد این کد دیگر معتبر نیست - ورود به سیستم با شماره تلفن - شماره تلفن به‌طور خودکار به‌تأیید رسید ارسال مجدد کد تأیید شماره تلفن - Use a different phone number + استفاده از شماره تلفن دیگر با ضربه زدن روی «%1$s»، پیامکی برایتان ارسال می‌شود. هزینه پیام و داده اعمال می‌شود. - درصورت ضربه‌زدن روی «%1$s»، موافقتتان را با %2$s و %3$s اعلام می‌کنید. پیامکی ارسال می‌شود. ممکن است هزینه داده و «پیام» محاسبه شود. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + خطای احراز هویت + دوباره امتحان کنید + تأیید بیشتری لازم است. لطفاً احراز هویت چندعاملی را کامل کنید. + حساب باید پیوند داده شود. لطفاً روش ورود دیگری را امتحان کنید. + احراز هویت لغو شد. هروقت آماده بودید، دوباره امتحان کنید. روش احراز هویت را انتخاب کنید @@ -144,29 +130,11 @@ هویت تأیید شد. دوباره امتحان کنید. مدیریت احراز هویت دو مرحله‌ای رمز عبور نادرست - روش تأیید را انتخاب کنید - لایه امنیتی اضافی اضافه کنید - پیامک - برنامه احراز هویت - این شماره تلفن با حساب دیگری مرتبط است - تأیید لازم است - کد QR را با برنامه احراز هویت خود اسکن کنید - برنامه احراز هویت قبلاً تنظیم شده است - آیا مطمئن هستید که می‌خواهید این روش را حذف کنید؟ - نمی‌توان روش را حذف کرد - روش حذف شد - کد تأیید را وارد کنید - نمی‌توان نمایه را به‌روزرسانی کرد - نمایه به‌روزرسانی شد - برای ادامه، هویت خود را تأیید کنید - احراز هویت مجدد لازم است - احراز هویت مجدد موفق بود - احراز هویت مجدد حذف ارسال مجدد ایمیل تأیید کلید مخفی خروج - وارد شده به عنوان + وارد شده به عنوان %1$s رد شدن استفاده از روش دیگر کد تأیید @@ -176,4 +144,17 @@ احراز هویت چند مرحله‌ای در حال حاضر غیرفعال است + آن ایمیل یا گذرواژه درست نیست + این جلسه تأیید دیگر معتبر نیست. کد جدیدی درخواست کنید. + تأیید شماره تلفن کامل نشد. دوباره امتحان کنید. + این اطلاعات ورود به حساب دیگری تعلق دارد. + این شماره تلفن برای تأیید در این حساب تنظیم نشده است. + جلسه ورود به سیستم شما منقضی شد. برای ادامه، دوباره وارد سیستم شوید. + این پیوند دیگر معتبر نیست. پیوند جدیدی درخواست کنید. + پیش از ادامه، نشانی ایمیلتان را تأیید کنید. + این روش تأیید قبلاً در این حساب تنظیم شده است. + به حداکثر تعداد روش‌های تأیید در این حساب رسیده‌اید. + گذرواژه شما الزامات را برآورده نمی‌کند. گذرواژه دیگری را امتحان کنید. + گذرواژه خیلی طولانی است. حداکثر طول %1$d است. + کلید عبوری برای این حساب پیدا نشد. به روش دیگری وارد سیستم شوید. diff --git a/auth/src/main/res/values-fi/strings.xml b/auth/src/main/res/values-fi/strings.xml index 1dbdcef0d0..43a63435d9 100755 --- a/auth/src/main/res/values-fi/strings.xml +++ b/auth/src/main/res/values-fi/strings.xml @@ -3,7 +3,6 @@ Kirjaudu sisään Jatka Jatkamalla vahvistat, että hyväksyt seuraavat: %1$s ja %2$s. - %1$s \u00A0 \u00A0 %2$s Verkkovirhe, tarkista internetyhteys. Google Facebook @@ -41,12 +40,9 @@ Tätä ei voi jättää tyhjäksi. Virheellinen sähköpostiosoite Jatka antamalla sähköpostiosoite. - Lisää etu- ja sukunimi. - Tarkistetaan nykyisiä tilejä… Hanki tili Etu- ja sukunimi Tallenna - Rekisteröityminen käynnissä… Salasana ei ole tarpeeksi vahva. Siinä on oltava vähintään %1$d merkkiä – sekä kirjaimia että numeroita. Salasana ei ole tarpeeksi vahva. Siinä on oltava vähintään %1$d merkkiä – sekä kirjaimia että numeroita. Sähköpostitilin rekisteröinti epäonnistui. Käyttöehdot @@ -54,17 +50,12 @@ Sinulla on jo tili. Tervetuloa takaisin! Sinulla on jo tili\n - Olet jo käyttänyt osoitetta %1$s. Jatka kirjautumalla palvelun %2$s kautta. - Olet jo käyttänyt osoitetta %1$s kirjautumiseen. Anna kyseisen tilin salasana. - %1$s on jo käytössä. Voit yhdistää %2$s ‑tilin sähköpostiosoitteeseen %1$s kirjautumalla sisään alla olevasta sähköpostilinkistä.\n\nJotta palvelun %2$s tilisi voidaan yhdistää tähän sähköpostiosoitteeseen tällä prosessilla, sinun on avattava linkki samalla laitteella tai selaimella. - Kirjaudutaan sisään… Eikö kirjautuminen onnistu? Palauta salasana Tarkista sähköpostisi Tilaa tähän sähköpostiin ohjeet, joissa neuvotaan, miten voit nollata salasanan. Lähetä Palauta salasana noudattamalla osoitteeseen %1$s lähetettyjä ohjeita. - Lähetetään… Sähköpostiosoite ei vastaa aiemmin luotua tiliä. Tapahtui tuntematon virhe. Väärä salasana @@ -90,19 +81,14 @@ Anna voimassa oleva puhelinnumero. Anna 6 merkin pituinen koodi, jonka lähetimme numeroon Lähetä koodi uudelleen seuraavan ajan kuluttua: %1$s. - Puhelinnumeron vahvistaminen Vahvistetaan… Väärä koodi. Yritä uudelleen. Tätä puhelinnumeroa on käytetty liian monta kertaa. - Puhelinnumerosi vahvistamisessa tapahtui virhe. Tämä koodi ei ole enää voimassa. - Kirjaudu puhelinnumerolla - Puhelinnumero vahvistettu automaattisesti Lähetä koodi uudelleen Vahvista puhelinnumero - Use a different phone number + Käytä toista puhelinnumeroa Kun napautat %1$s, tekstiviesti voidaan lähettää. Datan ja viestien käyttö voi olla maksullista. - Napauttamalla %1$s vahvistat hyväksyväsi seuraavat: %2$s ja %3$s. Tekstiviesti voidaan lähettää, ja datan ja viestien käyttö voi olla maksullista. Todennusvirhe Yritä uudelleen Lisävarmistus vaaditaan. Suorita monitekijätodennus loppuun. @@ -143,29 +129,11 @@ Henkilöllisyys vahvistettu. Yritä toimintoasi uudelleen. Hallinnoi kaksivaiheista todennusta Virheellinen salasana - Valitse vahvistustapa - Lisää ylimääräinen turvallisuuskerros - SMS - Todennussovellus - Tämä puhelinnumero on liitetty toiseen tiliin - Vahvistus vaaditaan - Skannaa QR-koodi todennussovelluksellasi - Todennussovellus on jo määritetty - Haluatko varmasti poistaa tämän menetelmän? - Menetelmää ei voitu poistaa - Menetelmä poistettu - Syötä vahvistuskoodi - Profiilia ei voitu päivittää - Profiili päivitetty - Vahvista henkilöllisyytesi jatkaaksesi - Uudelleentodennus vaaditaan - Uudelleentodennus onnistui - Todenna uudelleen Poista Lähetä vahvistussähköposti uudelleen Salainen avain Kirjaudu ulos - Kirjautuneena nimellä + Kirjautuneena nimellä %1$s Ohita Käytä eri menetelmää Vahvistuskoodi @@ -175,4 +143,17 @@ Monivaiheinen todennus on tällä hetkellä poistettu käytöstä + Sähköposti tai salasana on virheellinen + Tämä vahvistusistunto ei ole enää voimassa. Pyydä uusi koodi. + Puhelinvahvistus ei valmistunut. Yritä uudelleen. + Nämä tunnistetiedot kuuluvat toiselle tilille. + Tätä puhelinnumeroa ei ole määritetty vahvistukseen tällä tilillä. + Kirjautumisistuntosi vanheni. Kirjaudu sisään uudelleen jatkaaksesi. + Tämä linkki ei ole enää voimassa. Pyydä uusi. + Vahvista sähköpostiosoitteesi ennen kuin jatkat. + Tämä vahvistustapa on jo määritetty tällä tilillä. + Olet saavuttanut tämän tilin vahvistustapojen enimmäismäärän. + Salasanasi ei täytä vaatimuksia. Kokeile toista. + Salasana on liian pitkä. Enimmäispituus on %1$d. + Tälle tilille ei löytynyt avainkoodia. Kirjaudu sisään toisella tavalla. diff --git a/auth/src/main/res/values-fil/strings.xml b/auth/src/main/res/values-fil/strings.xml index 39b866524a..2a7925ccdd 100755 --- a/auth/src/main/res/values-fil/strings.xml +++ b/auth/src/main/res/values-fil/strings.xml @@ -3,14 +3,13 @@ Mag-sign in Magpatuloy Sa pagpapatuloy, ipinababatid mo na tinatanggap mo ang aming %1$s at %2$s. - %1$s \u00A0 \u00A0 %2$s Error sa network, tingnan ang koneksyon mo sa internet. Google - Fecebook + Facebook Twitter GitHub Telepono - Email + Email Mag-sign in sa Google Mag-sign in sa Google Mag-sign in sa Facebook @@ -36,17 +35,14 @@ Bansa Pumili ng bansa Maghanap ng bansa hal. +1, "US" - Password + Password Bagong password Hindi mo ito maaaring iwanan na walang laman. Mali ang email address na iyon Ilagay ang iyong email address upang magpatuloy - Maglagay ng pangalan at apelyido. - Tinitingnan ang mga umiiral nang account… Mag-sign up Pangalan at apelyido I-save - Nagsa-sign up… Hindi masyadong malakas ang password. Gumamit ng %1$d character man lang at kumbinasyon ng mga titik at mga numero Hindi masyadong malakas ang password. Gumamit ng %1$d na character man lang at kumbinasyon ng mga titik at mga numero Hindi nagtagumpay ang pagpaparehistro ng email account Mga Tuntunin ng Serbisyo @@ -54,17 +50,12 @@ Mayroon ka nang account Maligayang pagbabalik! May account ka na\n - Nagamit mo na ang %1$s.        Mag-sign in gamit ang %2$s upang magpatuloy. - Nagamit mo na ang %1$s        upang mag-sign in. Ilagay ang iyong password para sa account na iyon. - Nagamit mo na ang %1$s. Maaari mong ikonekta ang iyong %2$s account sa %1$s sa pamamagitan ng pag-sign in gamit ang link ng email sa ibaba.\n\nPara matagumpay na maikonekta ng daloy na ito ang iyong %2$s account sa email na ito, kailangan mong buksan ang link sa parehong device o browser. - Nagsa-sign in… Nagkaproblema sa pag-sign in? I-recover ang password Suriin ang iyong email Kumuha ng mga tagubilin na ipadala sa email na ito na magpapaliwanag kung paano  i-reset ang iyong password. Ipadala Sundin ang mga tagubilin na ipinadala sa %1$s upang ma-recover ang iyong password. - Ipinapadala… Hindi tumutugma ang email address na iyon sa isang kasalukuyang account Nagkaroon ng hindi malamang error. Maling password. @@ -90,19 +81,14 @@ Maglagay ng wastong numero ng telepono Ilagay ang 6-digit na code na ipinadala namin sa Ipadala muli ang code sa loob ng %1$s - I-verify ang numero ng iyong telepono Bine-verify… Maling code. Subukang muli. Masyadong maraming beses nang nagamit ang numero ng teleponong ito - Nagkaproblema sa pag-verify ng numero ng iyong telepono Wala nang bisa ang code na ito - Mag-sign in gamit ang numero ng telepono - Awtomatikong na-verify ang numero ng telepono Ipadala Muli ang Code I-verify ang Numero ng Telepono - Use a different phone number + Gumamit ng ibang numero ng telepono Sa pag-tap sa “%1$s,“ maaaring magpadala ng SMS. Maaaring ipatupad ang mga rate ng pagmemensahe at data. - Sa pag-tap sa “%1$s”, ipinababatid mo na tinatanggap mo ang aming %2$s at %3$s. Maaaring magpadala ng SMS. Maaaring ipatupad ang mga rate ng pagmemensahe at data. Error sa Pagpapatotoo Subukan Muli Kailangan ang karagdagang pagpapatotoo. Mangyaring kumpletuhin ang multi-factor authentication. @@ -143,36 +129,31 @@ Na-verify ang pagkakakilanlan. Subukan muli ang iyong aksyon. Pamahalaan ang two-factor authentication Maling password - Pumili ng paraan ng pag-verify - Magdagdag ng karagdagang layer ng seguridad - SMS - Authenticator app - Ang numerong ito ay nauugnay sa ibang account - Kinakailangan ang pag-verify - I-scan ang QR code gamit ang iyong authenticator app - Naka-set up na ang authenticator app - Sigurado ka bang gusto mong alisin ang paraang ito? - Hindi ma-alis ang paraan - Naalis ang paraan - Ilagay ang verification code - Hindi ma-update ang profile - Na-update ang profile - Kumpirmahin ang iyong pagkakakilanlan upang magpatuloy - Kinakailangan ang muling pag-authenticate - Matagumpay ang muling pag-authenticate - Mag-authenticate muli Alisin Ipadala muli ang verification email - Secret key + Sikretong key Mag-sign out - Naka-sign in bilang + Naka-sign in bilang %1$s Laktawan Gumamit ng ibang paraan - Verification code + Code sa pag-verify Na-verify ang email I-verify Nagpadala kami ng verification email sa %1$s Kasalukuyang naka-disable ang multi-factor authentication + Mali ang email o password na iyon + Wala nang bisa ang verification session na iyon. Humiling ng bagong code. + Hindi nakumpleto ang pag-verify ng telepono. Subukang muli. + Kabilang ang mga kredensyal na iyon sa ibang account. + Hindi naka-set up ang numero ng teleponong iyon para sa pag-verify sa account na ito. + Nag-expire na ang iyong sign-in session. Mag-sign in muli para magpatuloy. + Wala nang bisa ang link na iyon. Humiling ng bago. + I-verify ang iyong email address bago ka magpatuloy. + Naka-set up na ang paraan ng pag-verify na iyon sa account na ito. + Naabot mo na ang limitasyon para sa mga paraan ng pag-verify sa account na ito. + Hindi natutugunan ng iyong password ang mga kinakailangan. Sumubok ng iba. + Masyadong mahaba ang password. Ang maximum na haba ay %1$d. + Wala kaming nakitang passkey para sa account na ito. Mag-sign in sa ibang paraan. diff --git a/auth/src/main/res/values-fr-rCH/strings.xml b/auth/src/main/res/values-fr-rCH/strings.xml index 447df07468..158e0aaccd 100755 --- a/auth/src/main/res/values-fr-rCH/strings.xml +++ b/auth/src/main/res/values-fr-rCH/strings.xml @@ -3,7 +3,6 @@ Se connecter Continuer En continuant, vous acceptez les %1$s et les %2$s. - %1$s   %2$s Erreur de réseau, vérifiez votre connexion Internet. Google Facebook @@ -41,30 +40,22 @@ Ce champ est obligatoire. Cette adresse e-mail n\'est pas valide Saisissez votre adresse e-mail pour continuer - Veuillez saisir un prénom et un nom. - Vérification des comptes existants… S\'inscrire Nom et prénom Enregistrer - Inscription… - Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractère et une combinaison de chiffres et de lettres. Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractères et une combinaison de chiffres et de lettres. + Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractère et une combinaison de chiffres et de lettres. Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d de caractères et une combinaison de chiffres et de lettres. Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractères et une combinaison de chiffres et de lettres. Échec de la création du compte avec une adresse e-mail Conditions d\'utilisation Règles de confidentialité Vous avez déjà un compte Bienvenue ! Vous avez déjà un compte\n - Vous avez déjà utilisé l\'adresse e-mail %1$s. Connectez-vous avec %2$s pour continuer. - Vous avez déjà utilisé l\'adresse e-mail %1$s pour vous connecter. Saisissez le mot de passe du compte. - Vous avez déjà utilisé l\'adresse %1$s. Vous pouvez associer votre compte %2$s à l\'adresse %1$s en vous connectant via le lien envoyé par e-mail ci-dessous.\n\nPour associer votre compte %2$s à cette adresse e-mail, vous devez ouvrir le lien sur le même appareil ou dans le même navigateur. - Connexion… Vous ne parvenez pas à vous connecter ? Récupérer votre mot de passe Consultez votre boîte de réception Les instructions relatives à la réinitialisation de votre mot de passe seront envoyées à cette adresse e-mail. Envoyer Suivez les instructions envoyées à l\'adresse %1$s pour récupérer votre mot de passe. - Envoi… Cette adresse e-mail ne correspond à aucun compte existant Une erreur inconnue s\'est produite. Mot de passe incorrect. @@ -90,25 +81,20 @@ Saisissez un numéro de téléphone valide Saisissez le code à six chiffres envoyé au Renvoyer le code dans %1$s - Valider votre numéro de téléphone Validation… Code erroné. Veuillez réessayer. Ce numéro de téléphone a été utilisé un trop grand nombre de fois - Un problème est survenu lors de la validation de votre numéro de téléphone Ce code n\'est plus valide - Connectez-vous avec votre numéro de téléphone - Numéro de téléphone validé automatiquement Renvoyer le code Valider le numéro de téléphone - Use a different phone number + Utiliser un autre numéro de téléphone En appuyant sur “%1$s”, vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. - En appuyant sur “%1$s”, vous acceptez les %2$s et les %3$s. Vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Erreur d\'authentification + Réessayer + Vérification supplémentaire requise. Veuillez compléter l\'authentification à plusieurs facteurs. + Le compte doit être lié. Veuillez essayer une méthode de connexion différente. + L\'authentification a été annulée. Veuillez réessayer quand vous serez prêt. Choisir la méthode d\'authentification @@ -166,25 +152,20 @@ Vérifier Veuillez vérifier %1$s pour continuer. - Choisissez une méthode de vérification - Ajoutez une couche de sécurité supplémentaire - SMS - Application d\'authentification - Ce numéro de téléphone est associé à un autre compte - Vérification requise - Scannez le code QR avec votre application d\'authentification - L\'application d\'authentification est déjà configurée - Êtes-vous sûr de vouloir supprimer cette méthode ? - Impossible de supprimer la méthode - Méthode supprimée - Entrez le code de vérification - Impossible de mettre à jour le profil - Profil mis à jour - Confirmez votre identité pour continuer - Réauthentification requise - Réauthentification réussie - Se réauthentifier L\'authentification multifacteur est actuellement désactivée + Cet e-mail ou ce mot de passe est incorrect + Cette session de vérification n\'est plus valide. Demandez un nouveau code. + La vérification du numéro de téléphone n\'a pas abouti. Veuillez réessayer. + Ces identifiants appartiennent à un autre compte. + Ce numéro de téléphone n\'est pas configuré comme méthode de vérification sur ce compte. + Votre session de connexion a expiré. Reconnectez-vous pour continuer. + Ce lien n\'est plus valide. Demandez-en un nouveau. + Veuillez vérifier votre adresse e-mail avant de continuer. + Cette méthode de vérification est déjà configurée sur ce compte. + Vous avez atteint la limite de méthodes de vérification pour ce compte. + Votre mot de passe ne respecte pas les exigences. Essayez-en un autre. + Le mot de passe est trop long. La longueur maximale est de %1$d. + Aucune clé d\'accès n\'a été trouvée pour ce compte. Connectez-vous d\'une autre manière. diff --git a/auth/src/main/res/values-fr/strings.xml b/auth/src/main/res/values-fr/strings.xml index e6e2d773ca..4de849c1bd 100755 --- a/auth/src/main/res/values-fr/strings.xml +++ b/auth/src/main/res/values-fr/strings.xml @@ -3,7 +3,6 @@ Se connecter Continuer En continuant, vous acceptez les %1$s et les %2$s. - %1$s   %2$s Erreur de réseau, vérifiez votre connexion Internet. Google Facebook @@ -41,30 +40,22 @@ Ce champ est obligatoire. Cette adresse e-mail n\'est pas valide Saisissez votre adresse e-mail pour continuer - Veuillez saisir un prénom et un nom. - Vérification des comptes existants… S\'inscrire Nom et prénom Enregistrer - Inscription… - Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractère et une combinaison de chiffres et de lettres. Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractères et une combinaison de chiffres et de lettres. + Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractère et une combinaison de chiffres et de lettres. Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d de caractères et une combinaison de chiffres et de lettres. Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractères et une combinaison de chiffres et de lettres. Échec de la création du compte avec une adresse e-mail Conditions d\'utilisation Règles de confidentialité Vous avez déjà un compte Bienvenue ! Vous avez déjà un compte\n - Vous avez déjà utilisé l\'adresse e-mail %1$s. Connectez-vous avec %2$s pour continuer. - Vous avez déjà utilisé l\'adresse e-mail %1$s pour vous connecter. Saisissez le mot de passe du compte. - Vous avez déjà utilisé l\'adresse %1$s. Vous pouvez associer votre compte %2$s à l\'adresse %1$s en vous connectant via le lien envoyé par e-mail ci-dessous.\n\nPour associer votre compte %2$s à cette adresse e-mail, vous devez ouvrir le lien sur le même appareil ou dans le même navigateur. - Connexion… Vous ne parvenez pas à vous connecter ? Récupérer votre mot de passe Consultez votre boîte de réception Les instructions relatives à la réinitialisation de votre mot de passe seront envoyées à cette adresse e-mail. Envoyer Suivez les instructions envoyées à l\'adresse %1$s pour récupérer votre mot de passe. - Envoi… Cette adresse e-mail ne correspond à aucun compte existant Une erreur inconnue s\'est produite. Mot de passe incorrect. @@ -90,19 +81,14 @@ Saisissez un numéro de téléphone valide Saisissez le code à six chiffres envoyé au Renvoyer le code dans %1$s - Valider votre numéro de téléphone Validation… Code erroné. Veuillez réessayer. Ce numéro de téléphone a été utilisé un trop grand nombre de fois - Un problème est survenu lors de la validation de votre numéro de téléphone Ce code n\'est plus valide - Connectez-vous avec votre numéro de téléphone - Numéro de téléphone validé automatiquement Renvoyer le code Valider le numéro de téléphone - Use a different phone number + Utiliser un autre numéro de téléphone En appuyant sur “%1$s”, vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. - En appuyant sur "%1$s", vous acceptez les %2$s et les %3$s. Vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. Erreur d\'authentification Réessayer Vérification supplémentaire requise. Veuillez compléter l\'authentification à plusieurs facteurs. @@ -172,25 +158,20 @@ Vérifier Veuillez vérifier %1$s pour continuer. - Choisissez une méthode de vérification - Ajoutez une couche de sécurité supplémentaire - SMS - Application d\'authentification - Ce numéro de téléphone est associé à un autre compte - Vérification requise - Scannez le code QR avec votre application d\'authentification - L\'application d\'authentification est déjà configurée - Êtes-vous sûr de vouloir supprimer cette méthode ? - Impossible de supprimer la méthode - Méthode supprimée - Entrez le code de vérification - Impossible de mettre à jour le profil - Profil mis à jour - Confirmez votre identité pour continuer - Réauthentification requise - Réauthentification réussie - Se réauthentifier L\'authentification multifacteur est actuellement désactivée + L\'adresse e-mail ou le mot de passe est incorrect + Cette session de vérification n\'est plus valide. Demandez un nouveau code. + La vérification du numéro de téléphone n\'a pas abouti. Réessayez. + Ces identifiants appartiennent à un autre compte. + Ce numéro de téléphone n\'est pas configuré pour la vérification sur ce compte. + Votre session de connexion a expiré. Reconnectez-vous pour continuer. + Ce lien n\'est plus valide. Demandez-en un nouveau. + Vérifiez votre adresse e-mail avant de continuer. + Cette méthode de vérification est déjà configurée sur ce compte. + Vous avez atteint la limite de méthodes de vérification pour ce compte. + Votre mot de passe ne respecte pas les exigences. Essayez-en un autre. + Le mot de passe est trop long. La longueur maximale est de %1$d. + Aucune clé d\'accès n\'a été trouvée pour ce compte. Connectez-vous d\'une autre manière. diff --git a/auth/src/main/res/values-gsw/strings.xml b/auth/src/main/res/values-gsw/strings.xml index ca2068e978..472ef905ca 100755 --- a/auth/src/main/res/values-gsw/strings.xml +++ b/auth/src/main/res/values-gsw/strings.xml @@ -3,7 +3,6 @@ Anmelden Weiter Indem Sie fortfahren, stimmen Sie unseren %1$s und unserer %2$s zu. - %1$s \u00A0 \u00A0 %2$s Netzwerkfehler. Überprüfen Sie Ihre Internetverbindung. Google Facebook @@ -41,12 +40,9 @@ Pflichtfeld. Ungültige E-Mail-Adresse E-Mail-Adresse eingeben, um fortzufahren - Geben Sie einen Vor- und Nachnamen ein. - Auf vorhandene Konten wird geprüft… Registrieren Vor- und Nachname Speichern - Registrierung wird durchgeführt… Das Passwort ist zu schwach. Verwenden Sie mindestens %1$d Zeichen und eine Mischung aus Buchstaben und Ziffern. Das Passwort ist zu schwach. Verwenden Sie mindestens %1$d Zeichen und eine Mischung aus Buchstaben und Ziffern. Registrierung des E-Mail-Kontos fehlgeschlagen Nutzungsbedingungen @@ -54,17 +50,12 @@ Sie haben bereits ein Konto Willkommen zurück! Sie haben schon ein Konto\n - Sie haben %1$s bereits verwendet. Melden Sie sich mit %2$s an, um fortzufahren. - Sie haben %1$s bereits zur Anmeldung verwendet. Geben Sie das Passwort für dieses Konto ein. - Sie haben %1$s schon benutzt. Sie können Ihr %2$s-Konto mit %1$s verbinden, indem Sie sich über den folgenden E-Mail-Link anmelden.\n\nÖffnen Sie den Link mit demselben Gerät oder Browser, um Ihr %2$s-Konto mit dieser E-Mail-Adresse zu verbinden. - Anmeldung wird durchgeführt… Probleme bei der Anmeldung? Passwort zurücksetzen Im Posteingang nachsehen In der an diese E-Mail-Adresse gesendeten Anleitung wird beschrieben, wie Sie Ihr Passwort zurücksetzen können. Senden Folgen Sie der an %1$s gesendeten Anleitung, um Ihr Passwort zurückzusetzen. - Wird gesendet… Diese E-Mail-Adresse passt zu keinem Konto Ein unbekannter Fehler ist aufgetreten. Falsches Passwort @@ -90,19 +81,14 @@ Geben Sie eine gültige Telefonnummer ein Geben Sie den 6-stelligen Code ein, der gesendet wurde an Code in %1$s erneut senden - Telefonnummer bestätigen Wird verifiziert… Falscher Code. Versuchen Sie es noch einmal. Diese Telefonnummer wurde schon zu oft verwendet - Bei der Bestätigung Ihrer Telefonnummer ist ein Problem aufgetreten Dieser Code ist nicht mehr gültig - Mit einer Telefonnummer anmelden - Telefonnummer wurde automatisch bestätigt Code erneut senden Telefonnummer bestätigen - Use a different phone number + E anderi Telefonnummere verwände Wenn Sie auf “%1$s” tippen, erhalten Sie möglicherweise eine SMS. Es können Gebühren für SMS und Datenübertragung anfallen. - Indem Sie auf “%1$s” tippen, stimmen Sie unseren %2$s und unserer %3$s zu. Sie erhalten möglicherweise eine SMS und es können Gebühren für die Nachricht und die Datenübertragung anfallen. Authentifizierungsfehler Erneut versuchen Zusätzliche Verifizierung erforderlich. Bitte schließen Sie die Multi-Faktor-Authentifizierung ab. @@ -143,29 +129,11 @@ Identität verifiziert. Probier dini Aktion no mal. Zwei-Faktor-Authentifizierig verwalte Falsches Passwort - Wähl e Verifizierigsmethode - Füeg e zuesätzlichi Sicherheitsschicht hinzu - SMS - Authentifizierigs-App - Die Telefonnummer isch mit emne andere Konto verbunde - Verifizierig erforderlech - Scann de QR-Code mit dinere Authentifizierigs-App - Authentifizierigs-App isch scho iigrichtet - Bisch sicher, dass du die Methode entferne möchtisch? - Methode chan nöd entfernt wärde - Methode entfernt - Gib de Verifizierigs-Code ii - Profil chan nöd aktualisiert wärde - Profil aktualisiert - Bestätig dini Identität zum Fortfahre - Erneuti Authentifizierig erforderlech - Erneuti Authentifizierig erfolgriich - Erneut authentifiziere Entferne Verifizierigs-E-Mail erneut sende Gheime Schlüssel Abmelde - Agmeldet als + Agmeldet als %1$s Überspringe E anderi Methode verwände Verifizierigscode @@ -175,4 +143,17 @@ D\'Multi-Faktor-Authentifizierig isch zurziit deaktiviert + Die E-Mail-Adrässe oder s Passwort isch nöd richtig + Die Bestätigungssitzig isch nüme gültig. Fordere Sie e neue Code aa. + D\'Telefonbestätigung isch nöd abgschlosse worde. Versuche Sie es erneut. + Die Aamäldedate ghöre zunemene andere Konto. + Die Telefonnummere isch uf däm Konto nöd als Bestätigungsmethode iigrichtet. + Ihri Aamäldig isch abglaufe. Mälde Sie sich erneut aa, zum wiiterzmache. + De Link isch nüme gültig. Fordere Sie e neue aa. + Bestätige Sie Ihri E-Mail-Adrässe, bevor Sie wiitermache. + Die Bestätigungsmethode isch scho uf däm Konto iigrichtet. + Sie händ d\'Grenze für Bestätigungsmethode uf däm Konto erreicht. + Ihres Passwort erfüllt d Aaforderige nöd. Probiere Sie es mit eme andere. + S Passwort isch z lang. D maximali Läng isch %1$d. + Für das Konto isch kein Passkey gfunde worde. Mälde Sie sich uf en anderi Art aa. diff --git a/auth/src/main/res/values-gu/strings.xml b/auth/src/main/res/values-gu/strings.xml index 38258f327a..0c98f47224 100755 --- a/auth/src/main/res/values-gu/strings.xml +++ b/auth/src/main/res/values-gu/strings.xml @@ -3,7 +3,6 @@ સાઇન ઇન કરો આગળ વધો ચાલુ રાખીને, તમે સૂચવી રહ્યાં છો કે તમે અમારી %1$s અને %2$sને સ્વીકારો છો. - %1$s \u00A0 \u00A0 %2$s નેટવર્કમાં ભૂલ, તમારું ઇન્ટરનેટ કનેક્શન ચેક કરો. Google Facebook @@ -41,12 +40,9 @@ તમારે આ ફીલ્ડ ભરવું આવશ્યક છે. તે ઇમેઇલ ઍડ્રેસ સાચું નથી આગળ વધવા માટે તમારું ઇમેઇલ ઍડ્રેસ દાખલ કરો - કૃપા કરીને તમારું નામ અને અટક દાખલ કરો. - વર્તમાન એકાઉન્ટ તપાસી રહ્યાં છીએ… સાઇન અપ નામ અને અટક સાચવો - સાઇન અપ કરી રહ્યાં છીએ… પાસવર્ડ પૂરતો સશક્ત નથી. ઓછામાં ઓછા %1$d અક્ષર અને વર્ણ તથા સંખ્યાઓના સંયોજનનો ઉપયોગ કરો પાસવર્ડ પૂરતો સશક્ત નથી. ઓછામાં ઓછા %1$d અક્ષર અને વર્ણ તથા સંખ્યાઓના સંયોજનનો ઉપયોગ કરો ઇમેઇલ એકાઉન્ટની નોંધણી અસફળ સેવાની શરતો @@ -54,17 +50,12 @@ તમારી પાસે પહેલેથી જ એક એકાઉન્ટ છે ફરી સ્વાગત છે! તમારી પાસે પહેલેથી જ એકાઉન્ટ છે\n - તમે પહેલાંથી જ %1$sનો ઉપયોગ કરી લીધો છે. આગળ વધવા માટે %2$s વડે સાઇન ઇન કરો. - તમે સાઇન ઇન કરવા માટે પહેલેથી %1$sનો ઉપયોગ કરી લીધો છે. તે એકાઉન્ટ માટેનો તમારો પાસવર્ડ દાખલ કરો. - તમે પહેલેથી જ %1$sનો ઉપયોગ કરી લીધો છે. તમે નીચે આપેલ ઇમેઇલ લિંક વડે સાઇન ઇન કરીને તમારા %2$s એકાઉન્ટને %1$s સાથે કનેક્ટ કરી શકો છો.\n\nતમારા %2$s એકાઉન્ટને આ ઇમેઇલ સાથે સફળતાપૂર્વક કનેક્ટ કરવાની આ ક્રિયા માટે, તમારે આ લિંકને તે જ ડિવાઇસ અથવા બ્રાઉઝર પર ખોલવી પડશે. - સાઇન ઇન કરી રહ્યાં છીએ… સાઇન ઇન કરવામાં સમસ્યા આવી રહી છે? પાસવર્ડ પુનઃપ્રાપ્ત કરો તમારું ઇમેઇલ ઍડ્રેસ તપાસો આ ઇમેઇલમાં મોકલવામાં આવેલ સૂચનાઓ મેળવો જે તમારો પાસવર્ડ કેવી રીતે રીસેટ કરવો તે સમજાવે છે. મોકલો તમારો પાસવર્ડ પુનઃપ્રાપ્ત કરવા માટે %1$s પર મોકલેલ સૂચનાઓનું પાલન કરો. - મોકલી રહ્યાં છીએ… તે ઇમેઇલ ઍડ્રેસ અસ્તિત્વમાં છે તે એકાઉન્ટ સાથે મેળ ખાતું નથી કોઈ અજ્ઞાત ભૂલ આવી છે. ખોટો પાસવર્ડ. @@ -90,25 +81,20 @@ એક માન્ય ફોન નંબર દાખલ કરો અમે આ ફોન નંબર પર મોકલેલ 6-અંકનો કોડ દાખલ કરો 0 માં કોડ ફરીથી મોકલો:%1$s - તમારો ફોન નંબર ચકાસો ચકાસી રહ્યાં છીએ… કોડ ખોટો છે. ફરી પ્રયાસ કરો. આ ફોન નંબરનો ઉપયોગ ઘણી બધી વખત થઇ ગયો છે - તમારો ફોન નંબર ચકાસવામાં સમસ્યા આવી હતી આ કોડ હવે માન્ય નથી - ફોન નંબર વડે સાઇન ઇન કરો - ફોન નંબર આપમેળે ચકાસવામાં આવ્યો કોડ ફરીથી મોકલો ફોન નંબર ચકાસો - Use a different phone number + અલગ ફોન નંબરનો ઉપયોગ કરો “%1$s”ને ટૅપ કરવાથી, કદાચ એક SMS મોકલવામાં આવી શકે છે. સંદેશ અને ડેટા શુલ્ક લાગુ થઈ શકે છે. - “%1$s” ટૅપ કરીને, તમે સૂચવી રહ્યાં છો કે તમે અમારી %2$s અને %3$sને સ્વીકારો છો. SMS મોકલવામાં આવી શકે છે. સંદેશ અને ડેટા શુલ્ક લાગુ થઈ શકે છે. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + પ્રમાણીકરણ ભૂલ + ફરી પ્રયાસ કરો + વધારાની ચકાસણી જરૂરી છે. કૃપા કરીને મલ્ટિ-ફૅક્ટર પ્રમાણીકરણ પૂર્ણ કરો. + એકાઉન્ટ લિંક કરવાની જરૂર છે. કૃપા કરીને કોઈ અલગ સાઇન ઇન પદ્ધતિ અજમાવો. + પ્રમાણીકરણ રદ કરવામાં આવ્યું. તમે તૈયાર હો ત્યારે ફરી પ્રયાસ કરો. પ્રમાણીકરણ પદ્ધતિ પસંદ કરો @@ -144,29 +130,11 @@ ઓળખ ચકાસાઈ. તમારી ક્રિયા ફરી પ્રયાસ કરો. બે-પરિબળ પ્રમાણીકરણ સંચાલિત કરો ખોટો પાસવર્ડ - ચકાસણી પદ્ધતિ પસંદ કરો - સુરક્ષાનું વધારાનું સ્તર ઉમેરો - SMS - પ્રમાણીકરણ એપ - આ ફોન નંબર અન્ય એકાઉન્ટ સાથે સંકળાયેલ છે - ચકાસણી આવશ્યક છે - તમારી પ્રમાણીકરણ એપથી QR કોડ સ્કેન કરો - પ્રમાણીકરણ એપ પહેલાથી સેટઅપ છે - શું તમે ખરેખર આ પદ્ધતિ દૂર કરવા માગો છો? - પદ્ધતિ દૂર કરવામાં અસમર્થ - પદ્ધતિ દૂર કરી - ચકાસણી કોડ દાખલ કરો - પ્રોફાઇલ અપડેટ કરવામાં અસમર્થ - પ્રોફાઇલ અપડેટ થઈ - ચાલુ રાખવા માટે તમારી ઓળખ પુષ્ટિ કરો - ફરીથી પ્રમાણીકરણ આવશ્યક છે - ફરીથી પ્રમાણીકરણ સફળ - ફરીથી પ્રમાણીકરણ દૂર કરો ચકાસણી ઇમેઇલ ફરી મોકલો ગુપ્ત કી સાઇન આઉટ - તરીકે સાઇન ઇન કર્યું + %1$s તરીકે સાઇન ઇન કર્યું છોડો એક અલગ પદ્ધતિ વાપરો ચકાસણી કોડ @@ -176,4 +144,17 @@ મલ્ટિ-ફેક્ટર પ્રમાણીકરણ હાલમાં અક્ષમ છે + તે ઇમેઇલ અથવા પાસવર્ડ સાચો નથી + તે ચકાસણી સત્ર હવે માન્ય નથી. નવા કોડની વિનંતી કરો. + ફોનની ચકાસણી પૂર્ણ થઈ નથી. ફરી પ્રયાસ કરો. + તે ઓળખપત્રો કોઈ અલગ એકાઉન્ટનાં છે. + તે ફોન નંબર આ એકાઉન્ટ પર ચકાસણી માટે સેટ કરેલો નથી. + તમારું સાઇન ઇન સત્ર સમાપ્ત થઈ ગયું છે. ચાલુ રાખવા માટે ફરી સાઇન ઇન કરો. + તે લિંક હવે માન્ય નથી. નવી લિંકની વિનંતી કરો. + તમે આગળ વધો તે પહેલાં તમારું ઇમેઇલ ઍડ્રેસ ચકાસો. + તે ચકાસણી પદ્ધતિ આ એકાઉન્ટ પર પહેલેથી જ સેટ કરેલી છે. + તમે આ એકાઉન્ટ પર ચકાસણી પદ્ધતિઓની મર્યાદા પર પહોંચી ગયા છો. + તમારો પાસવર્ડ જરૂરિયાતો પૂરી કરતો નથી. બીજો પાસવર્ડ અજમાવો. + પાસવર્ડ ઘણો લાંબો છે. મહત્તમ લંબાઈ %1$d છે. + આ એકાઉન્ટ માટે કોઈ પાસકી મળી નથી. બીજી રીતે સાઇન ઇન કરો. diff --git a/auth/src/main/res/values-hi/strings.xml b/auth/src/main/res/values-hi/strings.xml index 45173de19c..7fe37f4720 100755 --- a/auth/src/main/res/values-hi/strings.xml +++ b/auth/src/main/res/values-hi/strings.xml @@ -3,7 +3,6 @@ प्रवेश करें जारी रखें जारी रखकर, आप यह बताते हैं कि आप हमारी %1$s और %2$s को मंज़ूर करते हैं. - %1$s \u00A0 \u00A0 %2$s नेटवर्क में गड़बड़ी है. देखें कि इंटरनेट काम कर रहा है या नहीं. Google Facebook @@ -41,12 +40,9 @@ आप इसे खाली नहीं छोड़ सकते. वह ईमेल पता सही नहीं है जारी रखने के लिए अपना ईमेल पता डालें - कृपया नाम और उपनाम डालें. - मौजूदा खातों की जाँच की जा रही है… साइन अप करें नाम और उपनाम सेव करें - साइन अप करना… पासवर्ड काफ़ी रूप से मजबूत नहीं है. कम से कम %1$d वर्णों का उपयोग करें जो अक्षरों और नंबरों दोनों को मिलाकर बना हो पासवर्ड काफ़ी रूप से मजबूत नहीं है. कम से कम %1$d वर्णों का उपयोग करें जो अक्षरों और नंबरों दोनों को मिलाकर बना हो ईमेल खाता पंजीकरण असफल रहा सेवा की शर्तें @@ -54,17 +50,12 @@ आपके पास पहले से ही एक खाता है आपका फिर से स्वागत है! आपके पास पहले से ही एक खाता है\n - आपने पहले ही %1$s का उपयोग कर लिया है. जारी रखने के लिए %2$s से प्रवेश करें. - आपने पहले ही प्रवेश करने के लिए %1$s का उपयोग कर लिया है. उस खाते के लिए अपना पासवर्ड डालें. - आप पहले ही %1$s का इस्तेमाल कर चुके हैं. आप नीचे दिए गए ईमेल लिंक के ज़रिए साइन इन करके अपना %2$s खाता %1$s के साथ जोड़ सकते हैं.\n\n अपने %2$s खाते को इस ईमेल पते से सफलतापूर्वक जोड़ने के लिए आपको उसी डिवाइस या ब्राउज़र पर लिंक खोलना होगा, जिससे आपने साइन-इन किया था. - प्रवेश हो रहा है… प्रवेश करने में समस्या? पासवर्ड फिर से पाएं अपना ईमेल जाँचें इस ईमेल पर भेजे गए निर्देश पाएं जो आपको अपना पासवर्ड रीसेट करना बताएंगे. भेजें अपना पासवर्ड फिर से पाने के लिए %1$s पर भेजे गए निर्देशों का पालन करें. - भेज रहा है… वह ईमेल पता किसी मौजूदा खाते से मेल नहीं खाता है ऐसी गड़बड़ी हुई है, जिसके बारे में पता नहीं है. गलत पासवर्ड. @@ -90,25 +81,20 @@ कोई मान्य फ़ोन नंबर डालें हमारी ओर से भेजा गया 6-अंकों वाला कोड डालें %1$s में कोड फिर से भेजें - अपने फ़ोन नंबर की पुष्टि करें पुष्टि की जा रही है… गलत कोड. फिर से कोशिश करें. इस फ़ोन नंबर का उपयोग कई बार किया गया है - आपके फ़ोन नंबर की पुष्टि करने में कोई समस्या हुई यह कोड अब मान्य नहीं है - फ़ोन नंबर के साथ प्रवेश करें - फ़ोन नंबर की अपने आप पुष्टि की गई कोड फिर से भेजें फ़ोन नंबर की पुष्टि करें - Use a different phone number + दूसरे फ़ोन नंबर का इस्तेमाल करें “%1$s” पर टैप करने पर, एक मैसेज (एसएमएस) भेजा जा सकता है. मैसेज और डेटा दरें लागू हो सकती हैं. - “%1$s” पर टैप करके, आप यह बताते हैं कि आप हमारी %2$s और %3$s को मंज़ूर करते हैं. एक मैसेज (एसएमएस) भेजा जा सकता है. मैसेज और डेटा दरें लागू हो सकती हैं. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + प्रमाणीकरण में गड़बड़ी + फिर से कोशिश करें + अतिरिक्त सत्यापन ज़रूरी है. कृपया मल्टी-फ़ैक्टर प्रमाणीकरण पूरा करें. + खाते को लिंक करना ज़रूरी है. कृपया प्रवेश करने का कोई दूसरा तरीका आज़माएँ. + प्रमाणीकरण रद्द कर दिया गया. तैयार होने पर फिर से कोशिश करें. प्रमाणीकरण विधि चुनें @@ -144,29 +130,11 @@ पहचान सत्यापित हुई। अपनी कार्रवाई फिर से आज़माएं। दो-कारक प्रमाणीकरण प्रबंधित करें गलत पासवर्ड - सत्यापन विधि चुनें - सुरक्षा की एक अतिरिक्त परत जोड़ें - SMS - प्रमाणीकरण ऐप - यह फ़ोन नंबर किसी अन्य खाते से जुड़ा है - सत्यापन आवश्यक है - अपने प्रमाणीकरण ऐप से QR कोड स्कैन करें - प्रमाणीकरण ऐप पहले से सेट अप है - क्या आप वाकई इस विधि को हटाना चाहते हैं? - विधि हटाने में असमर्थ - विधि हटाई गई - सत्यापन कोड दर्ज करें - प्रोफ़ाइल अपडेट करने में असमर्थ - प्रोफ़ाइल अपडेट हुआ - जारी रखने के लिए अपनी पहचान की पुष्टि करें - पुनः प्रमाणीकरण आवश्यक है - पुनः प्रमाणीकरण सफल - पुनः प्रमाणीकरण हटाएं सत्यापन ईमेल फिर से भेजें गुप्त कुंजी साइन आउट - इस रूप में साइन इन किया + %1$s के रूप में साइन इन किया छोड़ें एक अलग विधि उपयोग करें सत्यापन कोड @@ -176,4 +144,17 @@ मल्टी-फैक्टर प्रमाणीकरण वर्तमान में अक्षम है + वह ईमेल या पासवर्ड सही नहीं है + वह पुष्टि करने वाला सेशन अब मान्य नहीं है. नया कोड पाने का अनुरोध करें. + फ़ोन की पुष्टि पूरी नहीं हुई. कृपया फिर से कोशिश करें. + वे क्रेडेंशियल किसी दूसरे खाते के हैं. + वह फ़ोन नंबर इस खाते पर पुष्टि के लिए सेट अप नहीं है. + आपका साइन इन सेशन खत्म हो गया है. जारी रखने के लिए फिर से साइन इन करें. + वह लिंक अब मान्य नहीं है. नया लिंक पाने का अनुरोध करें. + आगे बढ़ने से पहले अपने ईमेल पते की पुष्टि करें. + पुष्टि करने का वह तरीका इस खाते पर पहले से सेट अप है. + आपने इस खाते पर पुष्टि करने के तरीकों की सीमा पूरी कर ली है. + आपका पासवर्ड ज़रूरी शर्तें पूरी नहीं करता. कोई दूसरा पासवर्ड आज़माएं. + पासवर्ड बहुत लंबा है. ज़्यादा से ज़्यादा लंबाई %1$d है. + इस खाते के लिए कोई पासकी नहीं मिली. किसी दूसरे तरीके से साइन इन करें. diff --git a/auth/src/main/res/values-hr/strings.xml b/auth/src/main/res/values-hr/strings.xml index b6fef2393d..2ff571733e 100755 --- a/auth/src/main/res/values-hr/strings.xml +++ b/auth/src/main/res/values-hr/strings.xml @@ -3,7 +3,6 @@ Prijava Nastavi Ako nastavite, potvrđujete da prihvaćate odredbe koje sadrže %1$s i %2$s. - %1$s \u00A0 \u00A0 %2$s Pogreška na mreži; provjerite internetsku vezu. Google Facebook @@ -41,12 +40,9 @@ Ne možete to ostaviti prazno. Ta e-adresa nije točna Unesite svoju e-adresu za nastavak - Unesite ime i prezime. - Traženje postojećih računa… Registracija Ime i prezime Spremi - Registriranje…. Zaporka nije dovoljno jaka. Upotrijebite najmanje %1$d znak i koristite kombinaciju slova i znamenki. Zaporka nije dovoljno jaka. Upotrijebite najmanje %1$d znaka i koristite kombinaciju slova i znamenki. Zaporka nije dovoljno jaka. Upotrijebite najmanje %1$d znakova i koristite kombinaciju slova i znamenki. Registracija računa putem e-adrese nije uspjela Uvjeti pružanja usluge @@ -54,17 +50,12 @@ Već imate račun Dobro došli natrag! Već imate račun\n - Već ste upotrijebili e-adresu %1$s. Za nastavak se prijavite putem usluge %2$s. - Za prijavu ste već upotrijebili e-adresu %1$s. Unesite zaporku za taj račun. - Već ste upotrebljavali adresu %1$s. Svoj %2$s račun možete povezati s adresom %1$s tako da se prijavite putem ispod navedene veze u e-poruci.\n\nDa biste ovim postupkom povezali svoj %2$s račun s tom e-adresom, trebate otvoriti vezu na istom uređaju ili pregledniku. - Prijava… Problem s prijavom? Ponovno postavljanje zaporke Provjerite e-poštu Provjerite upute za ponovno postavljanje zaporke koje smo poslali na vašu e-adresu. Pošalji Slijedite upute za ponovno postavljanje zaporke koje smo poslali na e-adresu %1$s. - Slanje… Ta e-adresa ne pripada postojećem računu Došlo je do nepoznate pogreške. Pogrešna zaporka. @@ -90,19 +81,14 @@ Unesite važeći telefonski broj Unesite 6-znamenkasti kôd koji smo poslali na broj Ponovno slanje koda za %1$s - Potvrda telefonskog broja Potvrđivanje… Pogrešan kôd. Pokušajte ponovo. Taj telefonski broj upotrijebljen je previše puta - Došlo je do problema s potvrdom vašeg telefonskog broja Kôd više nije važeći - Prijava putem telefonskog broja - Telefonski je broj automatski potvrđen Ponovo pošalji kôd Potvrda telefonskog broja - Use a different phone number + Upotrijebi drugi telefonski broj Dodirivanje gumba “%1$s” može dovesti do slanja SMS poruke. Mogu se primijeniti naknade za slanje poruka i podatkovni promet. - Ako dodirnete "%1$s", potvrđujete da prihvaćate odredbe koje sadrže %2$s i %3$s. Možda ćemo vam poslati SMS. Moguća je naplata poruke i podatkovnog prometa. Greška provjere identiteta Pokušaj ponovno Potrebna je dodatna provjera. Molimo dovršite višefaktorsku provjeru identiteta. @@ -143,29 +129,11 @@ Identitet potvrđen. Pokušajte ponovno svoju radnju. Upravljaj dvofaktorskom autentifikacijom Netočna lozinka - Odaberite metodu provjere - Dodajte dodatni sigurnosni sloj - SMS - Aplikacija za autentifikaciju - Ovaj telefonski broj povezan je s drugim računom - Potrebna je provjera - Skenirajte QR kod aplikacijom za autentifikaciju - Aplikacija za autentifikaciju već je postavljena - Jeste li sigurni da želite ukloniti ovu metodu? - Nije moguće ukloniti metodu - Metoda uklonjena - Unesite kod za provjeru - Nije moguće ažurirati profil - Profil ažuriran - Potvrdite svoj identitet za nastavak - Potrebna je ponovna autentifikacija - Ponovna autentifikacija uspješna - Ponovno autentificiraj Ukloni Ponovno pošalji e-poštu za provjeru Tajni ključ Odjava - Prijavljen kao + Prijavljen kao %1$s Preskoči Koristi drugu metodu Kod za provjeru @@ -175,4 +143,17 @@ Višefaktorska autentifikacija trenutno je onemogućena + Ta e-adresa ili zaporka nije točna + Ta sesija potvrde više nije važeća. Zatražite novi kôd. + Potvrda telefonskog broja nije dovršena. Pokušajte ponovno. + Te vjerodajnice pripadaju drugom računu. + Taj telefonski broj nije postavljen za potvrdu na ovom računu. + Vaša je sesija prijave istekla. Prijavite se ponovno da biste nastavili. + Ta veza više nije važeća. Zatražite novu. + Potvrdite svoju e-adresu prije nego što nastavite. + Taj je način potvrde već postavljen na ovom računu. + Dosegnuli ste ograničenje broja načina potvrde na ovom računu. + Vaša zaporka ne ispunjava uvjete. Pokušajte s drugom. + Zaporka je predugačka. Najveća duljina je %1$d. + Nismo pronašli pristupni ključ za ovaj račun. Prijavite se na drugi način. diff --git a/auth/src/main/res/values-hu/strings.xml b/auth/src/main/res/values-hu/strings.xml index 90e888d74f..f31e44d4ae 100755 --- a/auth/src/main/res/values-hu/strings.xml +++ b/auth/src/main/res/values-hu/strings.xml @@ -3,7 +3,6 @@ Bejelentkezés Tovább A folytatással elfogadja a következő dokumentumokat: %1$s és %2$s. - %1$s \u00A0 \u00A0 %2$s Hálózati hiba; ellenőrizze az internetkapcsolatot. Google Facebook @@ -41,12 +40,9 @@ Ezt nem hagyhatja üresen. Az e-mail-cím helytelen. A folytatáshoz adja meg e-mail-címét. - Írja be utónevét és vezetéknevét. - Meglévő fiókok ellenőrzése… Regisztráció Utónév és vezetéknév Mentés - Regisztráció… A jelszó nem elég erős. Legalább %1$d karaktert használjon, amelyben betűk és számok is szerepeljenek. A jelszó nem elég erős. Legalább %1$d karaktert használjon, amelyben betűk és számok is szerepeljenek. Az e-mail-fiók regisztrációja sikertelen volt. Általános Szerződési Feltételek @@ -54,17 +50,12 @@ Már rendelkezik fiókkal Örülünk, hogy visszatért! Már rendelkezik fiókkal\n - Már használta a következő e-mail-címet: %1$s. A folytatáshoz jelentkezzen be %2$s-fiókjával. - Ezt az e-mail-fiókot (%1$s) már használta bejelentkezésre. Adja meg a fiókhoz tartozó jelszót. - Már használta a következő e-mail-címet: %1$s. Most már összekapcsolhatja %2$s-fiókját a(z) %1$s e-mail-címmel, ha az alábbi e-mail-linkkel jelentkezik be.\n %2$s-fiókjának és az e-mail-címnek az összekapcsolásához a linket ugyanazon az eszközön vagy ugyanabban a böngészőben kell megnyitni. - Bejelentkezés… Problémái vannak a bejelentkezéssel? Jelszó visszaállítása Ellenőrizze az e-mailjeit. Olvassa el az e-mailben elküldött utasításokat, amelyekből megtudhatja, hogyan állíthatja vissza a jelszavát. Küldés A jelszó visszaállításához kövesse az ide (%1$s) elküldött utasításokat. - Küldés… Ehhez az e-mail-címhez egyetlen meglévő fiók sem tartozik. Ismeretlen hiba történt. Helytelen jelszó @@ -90,19 +81,14 @@ Érvényes telefonszámot adjon meg. Adja meg a telefonszámra elküldött 6 számjegyű kódot Kód újraküldése ennyi idő elteltével: %1$s - Telefonszám igazolása Ellenőrzés… Hibás kód. Próbálja újra. Ezt a telefonszámot már túl sokszor használták. - Hiba történt a telefonszám ellenőrzésekor. Ez a kód már nem érvényes. - Bejelentkezés telefonszámmal - A telefonszám automatikusan ellenőrizve Kód újraküldése Telefonszám igazolása - Use a different phone number + Másik telefonszám használata Ha a(z) „%1$s” gombra koppint, a rendszer SMS-t küldhet Önnek. A szolgáltató ezért üzenet- és adatforgalmi díjat számíthat fel. - A(z) „%1$s” gombra való koppintással elfogadja a következő dokumentumokat: %2$s és %3$s. A rendszer SMS-t küldhet Önnek. A szolgáltató ezért üzenet- és adatforgalmi díjat számíthat fel. Hitelesítési hiba Próbáld újra További ellenőrzés szükséges. Kérjük, fejezze be a többtényezős hitelesítést. @@ -143,29 +129,11 @@ Személyazonosság ellenőrizve. Próbálja újra a műveletet. Kétfaktoros hitelesítés kezelése Helytelen jelszó - Válasszon ellenőrzési módszert - Adjon hozzá egy extra biztonsági réteget - SMS - Hitelesítő alkalmazás - Ez a telefonszám másik fiókhoz van társítva - Ellenőrzés szükséges - Olvassa be a QR-kódot a hitelesítő alkalmazással - A hitelesítő alkalmazás már be van állítva - Biztosan eltávolítja ezt a módszert? - A módszer nem távolítható el - Módszer eltávolítva - Adja meg az ellenőrző kódot - A profil nem frissíthető - Profil frissítve - Erősítse meg személyazonosságát a folytatáshoz - Újrahitelesítés szükséges - Újrahitelesítés sikeres - Újrahitelesítés Eltávolítás Ellenőrző e-mail újraküldése Titkos kulcs Kijelentkezés - Bejelentkezve mint + Bejelentkezve mint %1$s Kihagyás Másik módszer használata Ellenőrző kód @@ -175,4 +143,17 @@ A többfaktoros hitelesítés jelenleg le van tiltva + Az e-mail-cím vagy a jelszó nem helyes + Ez az ellenőrzési munkamenet már nem érvényes. Kérjen új kódot. + A telefonszám ellenőrzése nem fejeződött be. Kérjük, próbálja újra. + Ezek a hitelesítő adatok egy másik fiókhoz tartoznak. + Ez a telefonszám nincs beállítva ellenőrzésre ebben a fiókban. + A bejelentkezési munkamenet lejárt. A folytatáshoz jelentkezzen be újra. + Ez a link már nem érvényes. Kérjen újat. + A folytatás előtt erősítse meg az e-mail-címét. + Ez az ellenőrzési módszer már be van állítva ebben a fiókban. + Elérte az ebben a fiókban beállítható ellenőrzési módszerek felső határát. + A jelszava nem felel meg a követelményeknek. Próbáljon meg egy másikat. + A jelszó túl hosszú. A maximális hossz %1$d. + Nem találtunk azonosítókulcsot ehhez a fiókhoz. Jelentkezzen be másik módon. diff --git a/auth/src/main/res/values-in/strings.xml b/auth/src/main/res/values-in/strings.xml index ef5093027a..6aa0f7c35e 100755 --- a/auth/src/main/res/values-in/strings.xml +++ b/auth/src/main/res/values-in/strings.xml @@ -3,14 +3,13 @@ Login Lanjutkan Dengan melanjutkan, Anda menyatakan bahwa Anda menyetujui %1$s dan %2$s kami. - %1$s \u00A0 \u00A0 %2$s Error jaringan, periksa koneksi internet Anda. Google Facebook Twitter GitHub Ponsel - Email + Alamat email Login dengan Google Login dengan Google Login dengan Facebook @@ -31,7 +30,7 @@ Login dengan Yahoo Login dengan Yahoo Berikutnya - Email + Alamat email Nomor Telepon Negara Pilih negara @@ -41,12 +40,9 @@ Anda wajib mengisinya. Alamat email yang dimasukkan salah Masukkan alamat email Anda untuk melanjutkan - Masukkan nama depan dan belakang. - Memeriksa akun yang ada… Daftar Nama depan & belakang Simpan - Mendaftar… Sandi tidak cukup kuat. Gunakan minimal %1$d karakter dan merupakan kombinasi huruf dan angka Sandi tidak cukup kuat. Gunakan minimal %1$d karakter dan merupakan kombinasi huruf dan angka Pendaftaran akun email tidak berhasil Persyaratan Layanan @@ -54,17 +50,12 @@ Anda sudah memiliki akun Selamat datang kembali! Anda sudah memiliki akun\n - Anda telah menggunakan %1$s. Login dengan %2$s untuk melanjutkan. - Anda telah menggunakan %1$s untuk login. Masukkan sandi untuk akun tersebut. - Anda sudah menggunakan %1$s. Anda dapat menghubungkan akun %2$s Anda ke %1$s dengan login menggunakan link email di bawah.\n\nAgar alur ini dapat menghubungkan akun %2$s Anda dengan email ini, Anda harus membuka link tersebut di perangkat atau browser yang sama. - Login… Terjadi masalah saat login? Pulihkan sandi Periksa email Anda Dapatkan petunjuk yang dikirimkan ke email ini yang menjelaskan cara menyetel ulang sandi Anda. Kirim Ikuti petunjuk yang dikirimkan ke %1$s untuk memulihkan sandi Anda. - Mengirim… Alamat email tidak cocok dengan akun yang ada Terjadi error yang tidak diketahui. Sandi salah. @@ -90,25 +81,20 @@ Masukkan nomor telepon yang valid Masukkan kode 6 digit yang kami kirimkan ke Kirimkan ulang kode dalam 0.%1$s - Verifikasi nomor telepon Anda Memverifikasi… Kode salah. Coba lagi. Nomor telepon ini sudah terlalu sering digunakan - Ada masalah saat memverifikasi nomor telepon Anda Kode ini sudah tidak valid - Login dengan nomor telepon - Nomor telepon terverifikasi secara otomatis Kirim Ulang Kode Verifikasi Nomor Telepon - Use a different phone number + Gunakan nomor telepon lain Dengan mengetuk “%1$s\", SMS mungkin akan dikirim. Mungkin dikenakan biaya pesan & data. - Dengan mengetuk “%1$s”, Anda menyatakan bahwa Anda menyetujui %2$s dan %3$s kami. SMS mungkin akan dikirim. Mungkin dikenakan biaya pesan & data. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Error Autentikasi + Coba lagi + Verifikasi tambahan diperlukan. Harap selesaikan autentikasi multi-faktor. + Akun perlu ditautkan. Harap coba metode login yang lain. + Autentikasi dibatalkan. Harap coba lagi saat Anda siap. Pilih Metode Autentikasi @@ -144,29 +130,11 @@ Identitas terverifikasi. Coba lagi tindakan Anda. Kelola autentikasi dua faktor Kata sandi salah - Pilih metode verifikasi - Tambahkan lapisan keamanan ekstra - SMS - Aplikasi autentikator - Nomor telepon ini terkait dengan akun lain - Verifikasi diperlukan - Pindai kode QR dengan aplikasi autentikator Anda - Aplikasi autentikator sudah diatur - Yakin ingin menghapus metode ini? - Tidak dapat menghapus metode - Metode dihapus - Masukkan kode verifikasi - Tidak dapat memperbarui profil - Profil diperbarui - Konfirmasi identitas Anda untuk melanjutkan - Autentikasi ulang diperlukan - Autentikasi ulang berhasil - Autentikasi ulang Hapus Kirim ulang email verifikasi Kunci rahasia Keluar - Masuk sebagai + Masuk sebagai %1$s Lewati Gunakan metode lain Kode verifikasi @@ -176,4 +144,17 @@ Autentikasi multifaktor saat ini dinonaktifkan + Email atau sandi tersebut salah + Sesi verifikasi tersebut sudah tidak valid. Minta kode baru. + Verifikasi telepon tidak selesai. Harap coba lagi. + Kredensial tersebut milik akun lain. + Nomor telepon tersebut tidak disiapkan untuk verifikasi di akun ini. + Sesi login Anda sudah berakhir. Login lagi untuk melanjutkan. + Link tersebut sudah tidak valid. Minta link baru. + Verifikasi alamat email Anda sebelum melanjutkan. + Metode verifikasi tersebut sudah disiapkan di akun ini. + Anda telah mencapai batas metode verifikasi di akun ini. + Sandi Anda tidak memenuhi persyaratan. Coba sandi lain. + Sandi terlalu panjang. Panjang maksimumnya adalah %1$d. + Kami tidak menemukan kunci sandi untuk akun ini. Login dengan cara lain. diff --git a/auth/src/main/res/values-it/strings.xml b/auth/src/main/res/values-it/strings.xml index da7f3a1a6d..307cbaf320 100755 --- a/auth/src/main/res/values-it/strings.xml +++ b/auth/src/main/res/values-it/strings.xml @@ -3,14 +3,13 @@ Accedi Continua Se continui, accetti i nostri %1$s e le nostre %2$s. - %1$s \u00A0 \u00A0 %2$s Errore di rete. Controlla la connessione a Internet. Google Facebook Twitter GitHub Telefono - Email + Email Accedi con Google Accedi con Google Accedi con Facebook @@ -36,35 +35,27 @@ Paese Seleziona un paese Cerca paese ad es. +1, "US" - Password + Password Nuova password Questo campo non può restare vuoto. L\'indirizzo email non è corretto Inserisci il tuo indirizzo email per continuare - Inserisci un nome e un cognome. - Verifica degli account esistenti in corso… Registrati Nome e cognome Salva - Registrazione in corso… - La password non è abbastanza efficace. Utilizza almeno %1$d carattere e una combinazione di lettere e numeri. La password non è abbastanza efficace. Utilizza almeno %1$d caratteri e una combinazione di lettere e numeri. + La password non è abbastanza efficace. Utilizza almeno %1$d carattere e una combinazione di lettere e numeri. La password non è abbastanza efficace. Utilizza almeno %1$d di caratteri e una combinazione di lettere e numeri. La password non è abbastanza efficace. Utilizza almeno %1$d caratteri e una combinazione di lettere e numeri. Registrazione account di posta elettronica non riuscita Termini di servizio Norme sulla privacy Hai già un account Bentornato. Hai già un account\n - Hai già utilizzato %1$s. Accedi con %2$s per continuare. - Hai già utilizzato %1$s per accedere. Inserisci la password di questo account. - Hai già utilizzato l\'indirizzo %1$s. Puoi collegare l\'account %2$s all\'indirizzo %1$s effettuando l\'accesso con il link di seguito.\n\nPer collegare correttamente l\'account %2$s a questo indirizzo email, devi aprire il link sullo stesso dispositivo o browser. - Accesso in corso… Problemi di accesso? Recupera la password Controlla la tua casella di posta A questo indirizzo email verranno inviate le istruzioni che spiegano come reimpostare la password. Invia Seguire le istruzioni inviate all\'indirizzo email %1$s per recuperare la password. - Invio in corso… L\'indirizzo email non corrisponde a un account esistente Si è verificato un errore sconosciuto. Password non corretta. @@ -90,19 +81,14 @@ Inserisci un numero di telefono valido Inserisci il codice a 6 cifre che abbiamo inviato al numero Invia di nuovo il codice tra %1$s - Verifica il numero di telefono Verifica… Codice errato. Riprova. Questo numero di telefono è stato usato troppe volte - Si è verificato un problema durante la verifica del tuo numero di telefono Questo codice non è più valido - Accedi con il numero di telefono - Numero di telefono verificato automaticamente Invia di nuovo il codice Verifica numero di telefono - Use a different phone number + Usa un altro numero di telefono Se tocchi “%1$s”, è possibile che venga inviato un SMS. Potrebbero essere applicate le tariffe per l\'invio dei messaggi e per il traffico dati. - Se tocchi "%1$s", accetti i nostri %2$s e le nostre %3$s. È possibile che venga inviato un SMS. Potrebbero essere applicate le tariffe per l\'invio dei messaggi e per il traffico dati. Errore di autenticazione Riprova È richiesta una verifica aggiuntiva. Completa l\'autenticazione a più fattori. @@ -143,29 +129,11 @@ Identità verificata. Riprova l\'azione. Gestisci autenticazione a due fattori Password non corretta - Scegli un metodo di verifica - Aggiungi un livello di sicurezza extra - SMS - App di autenticazione - Questo numero di telefono è già associato a un altro account - Verifica richiesta - Scansiona il codice QR con la tua app di autenticazione - L\'app di autenticazione è già configurata - Sei sicuro di voler rimuovere questo metodo? - Impossibile rimuovere il metodo - Metodo rimosso - Inserisci il codice di verifica - Impossibile aggiornare il profilo - Profilo aggiornato - Conferma la tua identità per continuare - Riautenticazione richiesta - Riautenticazione riuscita - Riautentica Rimuovi Invia nuovamente email di verifica Chiave segreta Esci - Connesso come + Connesso come %1$s Salta Usa un metodo diverso Codice di verifica @@ -175,4 +143,17 @@ L\'autenticazione a più fattori è attualmente disabilitata + L\'email o la password non sono corretti + Questa sessione di verifica non è più valida. Richiedi un nuovo codice. + La verifica del telefono non è stata completata. Riprova. + Queste credenziali appartengono a un altro account. + Questo numero di telefono non è configurato per la verifica su questo account. + La tua sessione di accesso è scaduta. Accedi di nuovo per continuare. + Questo link non è più valido. Richiedine uno nuovo. + Verifica il tuo indirizzo email prima di continuare. + Questo metodo di verifica è già configurato su questo account. + Hai raggiunto il limite di metodi di verifica per questo account. + La tua password non soddisfa i requisiti. Provane un\'altra. + La password è troppo lunga. La lunghezza massima è %1$d. + Non abbiamo trovato una passkey per questo account. Accedi in un altro modo. diff --git a/auth/src/main/res/values-iw/strings.xml b/auth/src/main/res/values-iw/strings.xml index af4bc1c8c9..cc8dec1448 100755 --- a/auth/src/main/res/values-iw/strings.xml +++ b/auth/src/main/res/values-iw/strings.xml @@ -3,7 +3,6 @@ כניסה המשך המשך התהליך יפורש כהסכמתך ל%1$s ול%2$s. - %1$s \u00A0 \u00A0 %2$s אירעה שגיאת רשת. יש לבדוק את החיבור לאינטרנט. Google Facebook @@ -41,12 +40,9 @@ אי אפשר להשאיר את השדה הזה ריק. כתובת האימייל שגויה כדי להמשיך, עליך להזין כתובת אימייל - יש להזין שם פרטי ושם משפחה. - מחפש חשבונות קיימים… הרשמה שם מלא שמור - אנחנו רושמים אותך… הסיסמה חלשה מדי. סיסמה חזקה היא סיסמה באורך תו אחד (%1$d) לפחות שמכילה אותיות ומספרים הסיסמה חלשה מדי. סיסמה חזקה היא סיסמה באורך %1$d תווים לפחות שמכילה אותיות ומספרים הסיסמה חלשה מדי. סיסמה חזקה היא סיסמה באורך %1$d תווים לפחות שמכילה אותיות ומספרים הסיסמה חלשה מדי. סיסמה חזקה היא סיסמה באורך %1$d תווים לפחות שמכילה אותיות ומספרים לא הצלחנו להשלים את הרישום לחשבון באמצעות כתובת האימייל תנאים והגבלות @@ -54,17 +50,12 @@ כבר יש לך חשבון ברוכים השבים! כבר יצרת חשבון\n - כבר השתמשת בכתובת %1$s. כדי להמשיך, עליך להיכנס באמצעות %2$s. - כבר השתמשת בכתובת %1$s. כדי להיכנס. עליך להזין את הסיסמה לחשבון המשויך אליה. - כבר השתמשת בכתובת האימייל %1$s. כדי לקשר את חשבון %2$s לכתובת האימייל %1$s, עליך להיכנס לחשבון באמצעות הקישור לאימייל שבהמשך.\n\nצריך לפתוח את הקישור באותו מכשיר או דפדפן כדי לקשר את חשבון %2$s לכתובת האימייל הזו. - נכנס… לא מצליח להיכנס? שחזור הסיסמה שלחנו לך אימייל אנחנו יכולים לשלוח לכתובת הזו הוראות לאיפוס הסיסמה. שליחה כדי לשחזר את הסיסמה, עליך לפעול לפי ההוראות ששלחנו לכתובת %1$s. - מתבצעת שליחה… אין חשבון המשויך לכתובת האימייל הזו אירעה שגיאה לא ידועה. סיסמה שגויה. @@ -90,25 +81,20 @@ מספר הטלפון שהזנת לא תקין הזן את הקוד בן 6 הספרות ששלחנו אל שולח קוד חדש בעוד %1$s:0 - אמת את מספר הטלפון מאמת… הקוד שגוי. נסה שוב. למספר הטלפון הזה כבר נשלחו יותר מדי קודים - אירעה בעיה באימות של מספר הטלפון הקוד הזה כבר לא בתוקף - כניסה באמצעות מספר טלפון - מספר הטלפון אומת באופן אוטומטי שלח קוד חדש אמת את מספר הטלפון - Use a different phone number + שימוש במספר טלפון אחר הקשה על “%1$s” עשויה לגרום לשליחה של הודעת SMS. ייתכן שיחולו תעריפי הודעות והעברת נתונים. - הקשה על “%1$s”, תפורש כהסכמתך ל%2$s ול%3$s. ייתכן שתישלח הודעת SMS. ייתכנו חיובים בגין שליחת הודעות ושימוש בנתונים. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + שגיאת אימות + נסה שוב + נדרש אימות נוסף. יש להשלים אימות רב-שלבי. + יש לקשר את החשבון. יש לנסות שיטת כניסה אחרת. + האימות בוטל. יש לנסות שוב כשתהיו מוכנים. בחר שיטת אימות @@ -144,29 +130,11 @@ הזהות אומתה. נסה שוב את הפעולה. ניהול אימות דו-שלבי סיסמה שגויה - בחר שיטת אימות - הוסף שכבת אבטחה נוספת - SMS - אפליקציית אימות - מספר הטלפון הזה משויך לחשבון אחר - נדרש אימות - סרוק את קוד ה-QR באפליקציית האימות שלך - אפליקציית האימות כבר מוגדרת - האם אתה בטוח שברצונך להסיר את שיטת האימות הזו? - לא ניתן להסיר את השיטה - השיטה הוסרה - הזן את קוד האימות - לא ניתן לעדכן את הפרופיל - הפרופיל עודכן - אמת את זהותך כדי להמשיך - נדרש אימות מחדש - אימות מחדש בוצע בהצלחה - אמת מחדש הסר שלח שוב אימייל אימות מפתח סודי התנתק - מחובר בתור + מחובר בתור %1$s דלג השתמש בשיטה אחרת קוד אימות @@ -176,4 +144,17 @@ אימות רב-גורמי מושבת כעת + האימייל או הסיסמה שגויים + הפעלת האימות הזו כבר לא בתוקף. יש לבקש קוד חדש. + אימות הטלפון לא הושלם. יש לנסות שוב. + פרטי הכניסה האלה שייכים לחשבון אחר. + מספר הטלפון הזה לא מוגדר לאימות בחשבון הזה. + הפעלת הכניסה שלך פגה. יש להיכנס שוב כדי להמשיך. + הקישור הזה כבר לא בתוקף. יש לבקש קישור חדש. + יש לאמת את כתובת האימייל שלך לפני שממשיכים. + שיטת האימות הזו כבר מוגדרת בחשבון הזה. + הגעת למגבלה של שיטות אימות בחשבון הזה. + הסיסמה שלך לא עומדת בדרישות. אפשר לנסות סיסמה אחרת. + הסיסמה ארוכה מדי. האורך המקסימלי הוא %1$d. + לא נמצא מפתח גישה לחשבון הזה. אפשר להיכנס בדרך אחרת. diff --git a/auth/src/main/res/values-ja/strings.xml b/auth/src/main/res/values-ja/strings.xml index d540d4edae..6031da033d 100755 --- a/auth/src/main/res/values-ja/strings.xml +++ b/auth/src/main/res/values-ja/strings.xml @@ -3,7 +3,6 @@ ログイン 続行 続行すると、%1$s と %2$s に同意したことになります。 - %1$s \u00A0 \u00A0 %2$s ネットワーク エラー。インターネット接続を確認してください。 Google Facebook @@ -41,12 +40,9 @@ 入力必須項目です。 メールアドレスが正しくありません 続行するにはメールアドレスを入力してください - 姓と名を入力してください。 - 既存のアカウントを確認しています… 登録 あなたの名前 保存 - 登録しています… パスワードが十分に安全ではありません。%1$d 文字以上で、文字と数字を組み合わせたパスワードを使用してください パスワードが十分に安全ではありません。%1$d 文字以上で、文字と数字を組み合わせたパスワードを使用してください メール アカウントの登録に失敗しました 利用規約 @@ -54,17 +50,12 @@ アカウントをすでにお持ちです こんにちは すでにアカウントをお持ちです\n - %1$s をすでに使用しています。%2$s でログインして続行してください。 - すでに %1$s を使用してログインしています。このアカウントのパスワードを入力してください。 - すでに %1$s をお使いです。以下のメールリンクを使用してログインすると、%2$s アカウントを %1$s に接続できます。\n\nこのフローで %2$s アカウントをこのメールアドレスに正常に接続するには、同じデバイスまたはブラウザでリンクを開く必要があります。 - ログインしています… ログインできない場合 パスワードの復元 メールをご確認ください このメールアドレスに送信された、パスワードの再設定方法をご確認ください。 送信 %1$s に送信された手順に沿ってパスワードを復元してください - 送信しています… メールアドレスが既存のアカウントと一致しません 不明なエラーが発生しました。 パスワードが正しくありません。 @@ -90,19 +81,14 @@ 有効な電話番号を入力してください 送信された 6 桁のコードを入力してください %1$s 秒後にコードを再送信します - 電話番号を確認 確認しています… コードが間違っています。もう一度お試しください。 この電話番号は何度も使用されています - 電話番号の確認中に問題が発生しました このコードは無効になりました - 電話番号でログイン - 電話番号は自動的に確認されました コードを再送信 電話番号を確認 - Use a different phone number + 別の電話番号を使用 [%1$s] をタップすると、SMS が送信されます。データ通信料がかかることがあります。 - [%1$s] をタップすると、%2$s と %3$s に同意したことになり、SMS が送信されます。データ通信料がかかることがあります。 認証エラー 再試行 追加の認証が必要です。多要素認証を完了してください。 @@ -143,29 +129,11 @@ 本人確認が完了しました。操作を再試行してください。 2段階認証を管理 パスワードが正しくありません - 確認方法を選択してください - セキュリティをさらに強化 - SMS - 認証アプリ - この電話番号は別のアカウントに関連付けられています - 確認が必要です - 認証アプリでQRコードをスキャンしてください - 認証アプリは既に設定されています - この確認方法を削除してもよろしいですか? - 確認方法を削除できません - 確認方法を削除しました - 確認コードを入力してください - プロフィールを更新できません - プロフィールを更新しました - 本人確認を行って続行してください - 再認証が必要です - 再認証に成功しました - 再認証 削除 確認メールを再送信 シークレットキー ログアウト - ログイン中 + %1$s としてログイン中 スキップ 別の方法を使用 確認コード @@ -175,4 +143,17 @@ 多要素認証は現在無効になっています + メールアドレスまたはパスワードが正しくありません + この確認セッションは無効になりました。新しいコードをリクエストしてください。 + 電話番号の確認が完了しませんでした。もう一度お試しください。 + この認証情報は別のアカウントのものです。 + この電話番号は、このアカウントの確認方法として設定されていません。 + ログインセッションの有効期限が切れました。続行するには、もう一度ログインしてください。 + このリンクは無効になりました。新しいリンクをリクエストしてください。 + 続行する前にメールアドレスを確認してください。 + この確認方法はこのアカウントですでに設定されています。 + このアカウントで設定できる確認方法の上限に達しました。 + パスワードが要件を満たしていません。別のパスワードをお試しください。 + パスワードが長すぎます。最大文字数は %1$d です。 + このアカウントのパスキーが見つかりませんでした。別の方法でログインしてください。 diff --git a/auth/src/main/res/values-kn/strings.xml b/auth/src/main/res/values-kn/strings.xml index 2be1d35d14..e0de55d3be 100755 --- a/auth/src/main/res/values-kn/strings.xml +++ b/auth/src/main/res/values-kn/strings.xml @@ -3,7 +3,6 @@ ಸೈನ್ ಇನ್‌ ಮುಂದುವರಿಸಿ ಮುಂದುವರಿಸುವ ಮೂಲಕ, ನೀವು ನಮ್ಮ %1$s ಮತ್ತು %2$s ಸ್ವೀಕರಿಸುತ್ತೀರಿ ಎಂದು ನೀವು ಸೂಚಿಸುತ್ತಿರುವಿರಿ - %1$s \u00A0 \u00A0 %2$s ನೆಟ್‌ವರ್ಕ್‌ನಲ್ಲಿ ದೋಷ, ನಿಮ್ಮ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಪರಿಶೀಲಿಸಿ. Google Facebook @@ -41,12 +40,9 @@ ನೀವು ಇದನ್ನು ಖಾಲಿ ಬಿಡುವಂತಿಲ್ಲ. ಆ ಇಮೇಲ್ ವಿಳಾಸ ಸರಿಯಾಗಿಲ್ಲ ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ನಮೂದಿಸಿ - ಮೊದಲ ಮತ್ತು ಕೊನೆಯ ಹೆಸರನ್ನು ನಮೂದಿಸಿ. - ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಖಾತೆಗಳಿಗಾಗಿ ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ… ಸೈನ್ ಅಪ್ ಮೊದಲ ಮತ್ತು ಕೊನೆಯ ಹೆಸರು ಉಳಿಸಿ - ಸೈನ್ ಅಪ್ ಮಾಡಲಾಗುತ್ತಿದೆ… ಪಾಸ್‍‌ವರ್ಡ್‌ ಸಾಕಷ್ಟು ಪ್ರಬಲವಾಗಿಲ್ಲ. ಕನಿಷ್ಠ %1$d ಅಕ್ಷರಗಳಿರಲಿ ಹಾಗೂ ಅದರಲ್ಲಿ ಅಕ್ಷರಗಳು ಮತ್ತು ಸಂಖ್ಯೆಗಳ ಮಿಕ್ಸ್‌ ಬಳಸಿ ಪಾಸ್‍‌ವರ್ಡ್‌ ಸಾಕಷ್ಟು ಪ್ರಬಲವಾಗಿಲ್ಲ. ಕನಿಷ್ಠ %1$d ಅಕ್ಷರಗಳಿರಲಿ ಹಾಗೂ ಅದರಲ್ಲಿ ಅಕ್ಷರಗಳು ಮತ್ತು ಸಂಖ್ಯೆಗಳ ಮಿಕ್ಸ್‌ ಬಳಸಿ ಇಮೇಲ್ ಖಾತೆ ನೋಂದಣಿ ಮಾಡುವಿಕೆಯು ವಿಫಲವಾಗಿದೆ ಸೇವಾ ನಿಯಮಗಳು @@ -54,17 +50,12 @@ ನೀವು ಈಗಾಗಲೇ ಖಾತೆಯನ್ನು ಹೊಂದಿರುವಿರಿ ಮರಳಿ ಸುಸ್ವಾಗತ! ನೀವು ಈಗಾಗಲೇ ಖಾತೆಯನ್ನು ಹೊಂದಿರುವಿರಿ\n - ನೀವು ಈಗಾಗಲೇ %1$s ಅನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ. ಮುಂದುವರಿಸಲು %2$s ಜೊತೆ ಸೈನ್ ಇನ್ ಮಾಡಿ. - ನೀವು ಈಗಾಗಲೇ ಸೈನ್ ಇನ್ ಮಾಡಲು %1$sನ್ನು ಬಳಸಿದ್ದೀರಿ. ಆ ಖಾತೆಗೆ ಸೈನ್ ಇನ್ ಆಗಲು ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ನಮೂದಿಸಿ. - ನೀವು ಈಗಾಗಲೇ %1$s ಇಮೇಲ್ ಅನ್ನು ಬಳಸಿರುವಿರಿ. ಕೆಳಗಿನ ಇಮೇಲ್ ಲಿಂಕ್ ಬಳಸಿಕೊಂಡು ಸೈನ್ ಇನ್ ಮಾಡುವ ಮೂಲಕ ನಿಮ್ಮ %2$s ಖಾತೆಯನ್ನು %1$s ಜೊತೆಗೆ ನೀವು ಸಂಪರ್ಕಿಸಬಹುದು.\n\nಇದು ಯಶಸ್ವಿಯಾಗಿ ನೆರವೇರಲು ಈ ಇಮೇಲ್ ಜೊತೆಗೆ ನಿಮ್ಮ %2$s ಖಾತೆಯನ್ನು ಸಂಪರ್ಕಿಸಿ, ನೀವು ಒಂದೇ ಸಾಧನ ಅಥವಾ ಬ್ರೌಸರ್‌ನಲ್ಲಿ ಲಿಂಕ್ ಅನ್ನು ತೆರೆಯಬೇಕು. - ಸೈನ್ ಇನ್ ಮಾಡಲಾಗುತ್ತಿದೆ… ಸೈನ್ ಇನ್ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ ಕಂಡುಬಂದಿದೆಯೇ? ಪಾಸ್‌ವರ್ಡ್ ಹಿಂಪಡೆಯಿರಿ ನಿಮ್ಮ ಇಮೇಲ್‌ ಅನ್ನು ಪರಿಶೀಲಿಸಿ ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಮರುಹೊಂದಿಸುವುದು ಹೇಗೆ ಎಂದು ತಿಳಿಸುವ ಸೂಚನೆಗಳನ್ನು ಈ ಇಮೇಲ್‌ ಮೂಲಕ ಪಡೆಯಿರಿ. ಕಳುಹಿಸಿ ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಮರುಪಡೆಯಲು %1$s ಗೆ ಕಳುಹಿಸಲಾದ ಸೂಚನೆಗಳನ್ನು ಅನುಸರಿಸಿ. - ಕಳುಹಿಸಲಾಗುತ್ತಿದೆ… ಆ ಇಮೇಲ್‌ ವಿಳಾಸವು ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಖಾತೆಗೆ ಹೊಂದಾಣಿಕೆಯಾಗುವುದಿಲ್ಲ ಅಪರಿಚಿತ ದೋಷವೊಂದು ಸಂಭವಿಸಿದೆ. ತಪ್ಪಾದ ಪಾಸ್‌ವರ್ಡ್. @@ -90,25 +81,20 @@ ಮಾನ್ಯವಾದ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ನಮೂದಿಸಿ ನಾವು ಕಳುಹಿಸಿರುವ 6 ಅಂಕಿಯ ಕೋಡ್‌ ನಮೂದಿಸಿ ಇಷ್ಟರ ಒಳಗೆ ಕೋಡ್ ಮತ್ತೆ ಕಳುಹಿಸಿ %1$s - ನಿಮ್ಮ ಪೋನ್‌ ಸಂಖ್ಯೆಯನ್ನು ಪರಿಶೀಲಿಸಿ ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ… ಕೋಡ್ ತಪ್ಪಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. ಈ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಈಗಾಗಲೇ ಹಲವಾರು ಬಾರಿ ಬಳಸಲಾಗಿದೆ - ನಿಮ್ಮ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಪರಿಶೀಲಿಸುವಲ್ಲಿ ತೊಂದರೆ ಇದೆ ಈ ಕೋಡ್ ಇನ್ನು ಮುಂದೆ ಮಾನ್ಯವಾಗಿರುವುದಿಲ್ಲ - ಫೋನ್‌ ಸಂಖ್ಯೆಯ ಮೂಲಕ ಸೈನ್‌ ಇನ್‌ ಮಾಡಿ - ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಪರಿಶೀಲಿಸಲಾಗಿದೆ ಕೋಡ್ ಪುನಃ ಕಳುಹಿಸಿ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಪರಿಶೀಲಿಸಿ - Use a different phone number + ಬೇರೆ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಬಳಸಿ “%1$s” ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡುವ ಮೂಲಕ, ಎಸ್‌ಎಂಎಸ್‌ ಅನ್ನು ಕಳುಹಿಸಬಹುದಾಗಿದೆ. ಸಂದೇಶ ಮತ್ತು ಡೇಟಾ ದರಗಳು ಅನ್ವಯಿಸಬಹುದು. - “%1$s” ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡುವ ಮೂಲಕ, ನೀವು ನಮ್ಮ %2$s ಮತ್ತು %3$s ಸ್ವೀಕರಿಸುತ್ತೀರಿ ಎಂದು ನೀವು ಸೂಚಿಸುತ್ತಿರುವಿರಿ. ಎಸ್‌ಎಂಎಸ್‌ ಅನ್ನು ಕಳುಹಿಸಬಹುದಾಗಿದೆ. ಸಂದೇಶ ಮತ್ತು ಡೇಟಾ ದರಗಳು ಅನ್ವಯಿಸಬಹುದು. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + ಪ್ರಮಾಣೀಕರಣ ದೋಷ + ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ + ಹೆಚ್ಚುವರಿ ಪರಿಶೀಲನೆ ಅಗತ್ಯವಿದೆ. ದಯವಿಟ್ಟು ಬಹು-ಅಂಶ ಪ್ರಮಾಣೀಕರಣವನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ. + ಖಾತೆಯನ್ನು ಲಿಂಕ್ ಮಾಡಬೇಕಾಗಿದೆ. ದಯವಿಟ್ಟು ಬೇರೆ ಸೈನ್ ಇನ್ ವಿಧಾನವನ್ನು ಪ್ರಯತ್ನಿಸಿ. + ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ. ನೀವು ಸಿದ್ಧರಾದಾಗ ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. ದೃಢೀಕರಣ ವಿಧಾನವನ್ನು ಆಯ್ಕೆಮಾಡಿ @@ -144,29 +130,11 @@ ಗುರುತನ್ನು ಪರಿಶೀಲಿಸಲಾಗಿದೆ. ನಿಮ್ಮ ಕ್ರಿಯೆಯನ್ನು ಮರುಪ್ರಯತ್ನಿಸಿ. ಎರಡು-ಅಂಶ ಪ್ರಮಾಣೀಕರಣವನ್ನು ನಿರ್ವಹಿಸಿ ತಪ್ಪಾದ ಪಾಸ್‌ವರ್ಡ್ - ಪರಿಶೀಲನೆ ವಿಧಾನವನ್ನು ಆರಿಸಿ - ಹೆಚ್ಚುವರಿ ಭದ್ರತಾ ಪದರವನ್ನು ಸೇರಿಸಿ - SMS - ಪ್ರಮಾಣೀಕರಣ ಅಪ್ಲಿಕೇಶನ್ - ಈ ಫೋನ್ ಸಂಖ್ಯೆ ಮತ್ತೊಂದು ಖಾತೆಯೊಂದಿಗೆ ಸಂಬಂಧ ಹೊಂದಿದೆ - ಪರಿಶೀಲನೆ ಅಗತ್ಯವಿದೆ - ನಿಮ್ಮ ಪ್ರಮಾಣೀಕರಣ ಅಪ್ಲಿಕೇಶನ್‌ನಲ್ಲಿ QR ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಿ - ಪ್ರಮಾಣೀಕರಣ ಅಪ್ಲಿಕೇಶನ್ ಈಗಾಗಲೇ ಸೆಟಪ್ ಆಗಿದೆ - ನೀವು ಈ ವಿಧಾನವನ್ನು ತೆಗೆದುಹಾಕಲು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ? - ವಿಧಾನವನ್ನು ತೆಗೆದುಹಾಕಲು ಸಾಧ್ಯವಿಲ್ಲ - ವಿಧಾನ ತೆಗೆದುಹಾಕಲಾಗಿದೆ - ಪರಿಶೀಲನೆ ಕೋಡ್ ನಮೂದಿಸಿ - ಪ್ರೊಫೈಲ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ - ಪ್ರೊಫೈಲ್ ಅಪ್‌ಡೇಟ್ ಆಗಿದೆ - ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಗುರುತನ್ನು ದೃಢೀಕರಿಸಿ - ಮರು-ಪ್ರಮಾಣೀಕರಣ ಅಗತ್ಯವಿದೆ - ಮರು-ಪ್ರಮಾಣೀಕರಣ ಯಶಸ್ವಿಯಾಗಿದೆ - ಮರು-ಪ್ರಮಾಣೀಕರಣ ತೆಗೆದುಹಾಕಿ ಪರಿಶೀಲನೆ ಇಮೇಲ್ ಮರುಕಳುಹಿಸಿ ರಹಸ್ಯ ಕೀ ಸೈನ್ ಔಟ್ - ಹೀಗೆ ಸೈನ್ ಇನ್ ಆಗಿದ್ದೀರಿ + %1$s ಆಗಿ ಸೈನ್ ಇನ್ ಆಗಿದ್ದೀರಿ ಸ್ಕಿಪ್ ಮಾಡಿ ವಿಭಿನ್ನ ವಿಧಾನವನ್ನು ಬಳಸಿ ಪರಿಶೀಲನೆ ಕೋಡ್ @@ -176,4 +144,17 @@ ಮಲ್ಟಿ-ಫ್ಯಾಕ್ಟರ್ ದೃಢೀಕರಣವು ಪ್ರಸ್ತುತ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ + ಆ ಇಮೇಲ್ ಅಥವಾ ಪಾಸ್‌ವರ್ಡ್ ಸರಿಯಾಗಿಲ್ಲ + ಆ ಪರಿಶೀಲನಾ ಸೆಶನ್ ಇನ್ನು ಮುಂದೆ ಮಾನ್ಯವಾಗಿಲ್ಲ. ಹೊಸ ಕೋಡ್‌ಗೆ ವಿನಂತಿಸಿ. + ಫೋನ್ ಪರಿಶೀಲನೆ ಪೂರ್ಣಗೊಂಡಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. + ಆ ರುಜುವಾತುಗಳು ಬೇರೆ ಖಾತೆಗೆ ಸೇರಿವೆ. + ಆ ಫೋನ್ ಸಂಖ್ಯೆಯನ್ನು ಈ ಖಾತೆಯಲ್ಲಿ ಪರಿಶೀಲನೆಗಾಗಿ ಹೊಂದಿಸಲಾಗಿಲ್ಲ. + ನಿಮ್ಮ ಸೈನ್ ಇನ್ ಸೆಶನ್‌ನ ಅವಧಿ ಮುಗಿದಿದೆ. ಮುಂದುವರಿಸಲು ಮತ್ತೆ ಸೈನ್ ಇನ್ ಮಾಡಿ. + ಆ ಲಿಂಕ್ ಇನ್ನು ಮುಂದೆ ಮಾನ್ಯವಾಗಿಲ್ಲ. ಹೊಸ ಲಿಂಕ್‌ಗೆ ವಿನಂತಿಸಿ. + ಮುಂದುವರಿಯುವ ಮೊದಲು ನಿಮ್ಮ ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ಪರಿಶೀಲಿಸಿ. + ಆ ಪರಿಶೀಲನಾ ವಿಧಾನವನ್ನು ಈ ಖಾತೆಯಲ್ಲಿ ಈಗಾಗಲೇ ಹೊಂದಿಸಲಾಗಿದೆ. + ಈ ಖಾತೆಯಲ್ಲಿನ ಪರಿಶೀಲನಾ ವಿಧಾನಗಳ ಮಿತಿಯನ್ನು ನೀವು ತಲುಪಿದ್ದೀರಿ. + ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅಗತ್ಯತೆಗಳನ್ನು ಪೂರೈಸುತ್ತಿಲ್ಲ. ಬೇರೊಂದನ್ನು ಪ್ರಯತ್ನಿಸಿ. + ಪಾಸ್‌ವರ್ಡ್ ತುಂಬಾ ಉದ್ದವಾಗಿದೆ. ಗರಿಷ್ಠ ಉದ್ದ %1$d ಆಗಿದೆ. + ಈ ಖಾತೆಗೆ ಪಾಸ್‌ಕೀ ಕಂಡುಬಂದಿಲ್ಲ. ಬೇರೆ ವಿಧಾನದಲ್ಲಿ ಸೈನ್ ಇನ್ ಮಾಡಿ. diff --git a/auth/src/main/res/values-ko/strings.xml b/auth/src/main/res/values-ko/strings.xml index abcbfc82e9..0bc794c7e6 100755 --- a/auth/src/main/res/values-ko/strings.xml +++ b/auth/src/main/res/values-ko/strings.xml @@ -3,7 +3,6 @@ 로그인 계속 계속 진행하면 %1$s 및 %2$s에 동의하는 것으로 간주됩니다. - %1$s \u00A0 \u00A0 %2$s 네트워크 오류가 발생했습니다. 인터넷 연결을 확인하세요. Google Facebook @@ -41,12 +40,9 @@ 이 입력란을 비워둘 수 없습니다. 이메일 주소가 잘못되었습니다. 계속하려면 이메일 주소를 입력하세요. - 성과 이름을 입력하세요. - 기존 계정 확인 중… 가입 이름 및 성 저장 - 가입하는 중… 안전한 비밀번호가 아닙니다. 길이가 %1$d자 이상이고 문자와 숫자가 조합되어야 합니다. 안전한 비밀번호가 아닙니다. 길이가 %1$d자 이상이고 문자와 숫자가 조합되어야 합니다. 이메일 계정 등록 실패 서비스 약관 @@ -54,17 +50,12 @@ 계정이 이미 있음 환영합니다. 이미 계정이 있습니다.\n - %1$s을(를) 이미 사용하고 있습니다. 계속하려면 %2$s(으)로 로그인하세요. - 이미 %1$s을(를) 사용하여 로그인했습니다. 해당 계정의 비밀번호를 입력하세요. - 이미 %1$s을(를) 사용했습니다. 아래 이메일 링크로 로그인하면 %1$s에 %2$s 계정을 연결할 수 있습니다.\n\n이 이메일에 %2$s 계정을 연결하는 과정을 완료하려면 동일한 기기나 브라우저에서 링크를 열어야 합니다. - 로그인 중… 로그인하는 데 문제가 있나요? 비밀번호 복구 이메일 확인 비밀번호 재설정 방법을 안내하는 이메일을 보내 드립니다. 보내기 %1$s(으)로 발송된 이메일의 안내에 따라 비밀번호를 복구하세요. - 전송 중… 이메일 주소가 기존 계정과 일치하지 않습니다. 알 수 없는 오류가 발생했습니다. 비밀번호가 잘못되었습니다. @@ -90,17 +81,13 @@ 올바른 전화번호를 입력하세요. 전송된 6자리 코드를 입력하세요. %1$s 후에 코드 재전송 - 전화번호 인증 인증 중… 코드가 잘못되었습니다. 다시 시도하세요. 이 전화번호로 전송 시도를 너무 많이 했습니다. - 전화번호를 인증하는 중에 문제가 발생했습니다. 더 이상 유효하지 않은 코드입니다. - 전화번호로 로그인 - 전화번호가 자동으로 확인되었습니다. 코드 재전송 전화번호 인증 - Use a different phone number + 다른 전화번호 사용 “%1$s” 버튼을 탭하면 SMS가 발송될 수 있으며, 메시지 및 데이터 요금이 부과될 수 있습니다. 인증 오류 다시 시도 @@ -142,29 +129,11 @@ 신원이 확인되었습니다. 작업을 다시 시도하세요. 2단계 인증 관리 비밀번호가 올바르지 않습니다 - 확인 방법을 선택하세요 - 추가 보안 계층 추가 - SMS - 인증 앱 - 이 전화번호는 이미 다른 계정과 연결되어 있습니다 - 확인이 필요합니다 - 인증 앱으로 QR 코드를 스캔하세요 - 인증 앱이 이미 설정되어 있습니다 - 이 확인 방법을 삭제하시겠습니까? - 확인 방법을 삭제할 수 없습니다 - 확인 방법이 삭제되었습니다 - 확인 코드를 입력하세요 - 프로필을 업데이트할 수 없습니다 - 프로필이 업데이트되었습니다 - 계속하려면 본인 확인을 진행하세요 - 재인증이 필요합니다 - 재인증에 성공했습니다 - 재인증 삭제 확인 이메일 다시 보내기 비밀 키 로그아웃 - 로그인 상태 + %1$s(으)로 로그인 상태 건너뛰기 다른 방법 사용 확인 코드 @@ -174,4 +143,17 @@ 다단계 인증이 현재 비활성화되어 있습니다 + 이메일 또는 비밀번호가 올바르지 않습니다. + 이 인증 세션은 더 이상 유효하지 않습니다. 새 코드를 요청하세요. + 전화번호 인증이 완료되지 않았습니다. 다시 시도하세요. + 이 사용자 인증 정보는 다른 계정에 속해 있습니다. + 이 전화번호는 이 계정의 인증 수단으로 설정되어 있지 않습니다. + 로그인 세션이 만료되었습니다. 계속하려면 다시 로그인하세요. + 이 링크는 더 이상 유효하지 않습니다. 새 링크를 요청하세요. + 계속하기 전에 이메일 주소를 인증하세요. + 이 인증 수단은 이 계정에 이미 설정되어 있습니다. + 이 계정에서 설정할 수 있는 인증 수단 한도에 도달했습니다. + 비밀번호가 요건을 충족하지 않습니다. 다른 비밀번호를 사용해 보세요. + 비밀번호가 너무 깁니다. 최대 길이는 %1$d입니다. + 이 계정의 패스키를 찾을 수 없습니다. 다른 방법으로 로그인하세요. diff --git a/auth/src/main/res/values-ln/strings.xml b/auth/src/main/res/values-ln/strings.xml index 1931ef3766..ef4f9c3976 100755 --- a/auth/src/main/res/values-ln/strings.xml +++ b/auth/src/main/res/values-ln/strings.xml @@ -3,7 +3,6 @@ Se connecter Continuer En continuant, vous acceptez les %1$s et les %2$s. - %1$s   %2$s Erreur de réseau, vérifiez votre connexion Internet. Google Facebook @@ -41,12 +40,9 @@ Ce champ est obligatoire. Cette adresse e-mail n\'est pas valide Saisissez votre adresse e-mail pour continuer - Veuillez saisir un prénom et un nom. - Vérification des comptes existants… S\'inscrire Nom et prénom Enregistrer - Inscription… Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractère et une combinaison de chiffres et de lettres. Le mot de passe n\'est pas assez sécurisé. Utilisez au moins %1$d caractères et une combinaison de chiffres et de lettres. Échec de la création du compte avec une adresse e-mail Conditions d\'utilisation @@ -54,17 +50,12 @@ Vous avez déjà un compte Bienvenue ! Vous avez déjà un compte\n - Vous avez déjà utilisé l\'adresse e-mail %1$s. Connectez-vous avec %2$s pour continuer. - Vous avez déjà utilisé l\'adresse e-mail %1$s pour vous connecter. Saisissez le mot de passe du compte. - Vous avez déjà utilisé l\'adresse %1$s. Vous pouvez associer votre compte %2$s à l\'adresse %1$s en vous connectant via le lien envoyé par e-mail ci-dessous.\n\nPour associer votre compte %2$s à cette adresse e-mail, vous devez ouvrir le lien sur le même appareil ou dans le même navigateur. - Connexion… Vous ne parvenez pas à vous connecter ? Récupérer votre mot de passe Consultez votre boîte de réception Les instructions relatives à la réinitialisation de votre mot de passe seront envoyées à cette adresse e-mail. Envoyer Suivez les instructions envoyées à l\'adresse %1$s pour récupérer votre mot de passe. - Envoi… Cette adresse e-mail ne correspond à aucun compte existant Une erreur inconnue s\'est produite. Mot de passe incorrect. @@ -90,25 +81,20 @@ Saisissez un numéro de téléphone valide Saisissez le code à six chiffres envoyé au Renvoyer le code dans %1$s - Valider votre numéro de téléphone Validation… Code erroné. Veuillez réessayer. Ce numéro de téléphone a été utilisé un trop grand nombre de fois - Un problème est survenu lors de la validation de votre numéro de téléphone Ce code n\'est plus valide - Connectez-vous avec votre numéro de téléphone - Numéro de téléphone validé automatiquement Renvoyer le code Valider le numéro de téléphone - Use a different phone number + Salelá nimero mosusu ya telefone En appuyant sur “%1$s”, vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. - En appuyant sur “%1$s”, vous acceptez les %2$s et les %3$s. Vous déclencherez peut-être l\'envoi d\'un SMS. Des frais de messages et de données peuvent être facturés. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Libunga ya bondimi + Meka lisusu + Bondimi mosusu esengeli. Tosɛngi osilisa bondimisami ya makambo mingi. + Konte esengeli kokangisama. Tosɛngi omeka lolenge mosusu ya kokɔta. + Bondimi e-annulé. Tosɛngi omeka lisusu ntango okozala pene. Pona lolenge ya bondimi @@ -144,29 +130,11 @@ Identité esalemi. Meka lisusu. Kotambwisa authentification ya facteur mibale Mot de passe ezali malamu te - Pona méthode ya vérification - Bakisa couche ya sécurité mosusu - SMS - Application ya authentification - Numéro oyo ya téléphone ezali na compte mosusu - Vérification esengeli - Scanner code QR na application ya authentification na yo - Application ya authentification etii nanu - Ozali na ntembe olinga kolongola méthode oyo? - Ekoki kolongola méthode te - Méthode elongwami - Kotisa code ya vérification - Ekoki ko-update profil te - Profil e-update - Ndima identité na yo mpo na kokoba - Re-authentification esengeli - Re-authentification elongi - Re-authentifier Longola Tinda lisusu e-mail ya vérification Clé secrète Kobima - Okoti lokola + Okoti lokola %1$s Leka Salelá méthode mosusu Code ya vérification @@ -176,4 +144,17 @@ Bondimisami ya makambo mingi ezali sikoyo te + Email to password wana ezali sembo te + Session wana ya bondimi ezali lisusu na ntina te. Sɛnga kode ya sika. + Bondimi ya telefone esili te. Meka lisusu. + Ba code wana ya kokɔta ezali ya konte mosusu. + Nimero wana ya telefone ebongisami te mpo na bondimi na konte oyo. + Session na yo ya kokɔta esili. Kɔta lisusu mpo na kokoba. + Lien wana ezali lisusu malamu te. Sɛnga lien ya sika. + Ndimisa adrɛsɛ na yo ya email liboso ya kokoba. + Lolenge wana ya bondimi ebongisami déjà na konte oyo. + Okómi na ndelo ya balolenge ya bondimi na konte oyo. + Mot de passe na yo ekokisi te makambo esengeli. Meka mosusu. + Mot de passe ezali molayi mingi. Molayi ya likolo ezali %1$d. + Tomonaki te clé ya kokɔta mpo na compte oyo. Kɔta na ndenge mosusu. diff --git a/auth/src/main/res/values-lt/strings.xml b/auth/src/main/res/values-lt/strings.xml index b68b5aabb2..c4c40658c6 100755 --- a/auth/src/main/res/values-lt/strings.xml +++ b/auth/src/main/res/values-lt/strings.xml @@ -3,7 +3,6 @@ Prisijungti Tęsti Tęsdami nurodote, kad sutinkate su %1$s ir %2$s. - %1$s \u00A0 \u00A0 %2$s Tinklo klaida: patikrinkite interneto ryšį. Google Facebook @@ -41,12 +40,9 @@ Šį lauką būtina užpildyti. Šis el. pašto adresas netinkamas Norėdami tęsti įveskite el. pašto adresą - Įveskite vardą ir pavardę. - Tikrinama, ar yra esamų paskyrų… Prisiregistruoti Vardas ir pavardė Išsaugoti - Prisiregistruojama… Slaptažodis nepakankamai sudėtingas. Naudokite bent %1$d simbolį ir raidžių bei skaičių derinį. Slaptažodis nepakankamai sudėtingas. Naudokite bent %1$d simbolius ir raidžių bei skaičių derinį. Slaptažodis nepakankamai sudėtingas. Naudokite bent %1$d simbolio ir raidžių bei skaičių derinį. Slaptažodis nepakankamai sudėtingas. Naudokite bent %1$d simbolių ir raidžių bei skaičių derinį. El. pašto paskyros užregistruoti nepavyko Paslaugų teikimo sąlygomis @@ -54,17 +50,12 @@ Jūs jau turite paskyrą Sveiki sugrįžę! Jau turite paskyrą\n - Jau panaudojote %1$s.Jei norite tęsti, prisijunkite per „%2$s“. - Jūs jau prisijungėte naudodami %1$s. Įveskite tos paskyros slaptažodį. - Jau esate naudoję %1$s. Galite susieti „%2$s“ paskyrą su %1$s: prisijunkite naudodami el. laiško nuorodą toliau.\n\nJei norite vykdydami šį procesą sėkmingai susieti „%2$s“ paskyrą naudodami šį el. laišką, turite atidaryti nuorodą tame pačiame įrenginyje ar naršyklėje. - Prisijungiama… Kyla problemų prisijungiant? Atkurti slaptažodį Patikrinkite el. paštą Gaukite instrukcijas su nurodymais, kaip pakeisti slaptažodį, šiuo el. pašto adresu. Siųsti Vadovaudamiesi el. pašto adresu %1$s išsiųstais nurodymais atkurkite slaptažodį. - Siunčiama… Šis el. pašto adresas neatitinka esamos paskyros Įvyko nežinoma klaida. Netinkamas slaptažodis. @@ -90,25 +81,20 @@ Įveskite tinkamą telefono numerį Įveskite 6 skaitmenų kodą, kurį išsiuntėme Siųsti kodą iš naujo po %1$s - Patvirtinti telefono numerį Patvirtinama… Klaidingas kodas. Bandykite dar kartą. Šis telefono numeris panaudotas per daug kartų - Patvirtinant telefono numerį kilo problema Šis kodas nebegalioja - Prisijungti nurodant telefono numerį - Telefono numeris patvirtintas automatiškai Siųsti kodą iš naujo Patvirtinti telefono numerį - Use a different phone number + Naudoti kitą telefono numerį Palietus „%1$s“ gali būti išsiųstas SMS pranešimas. Gali būti taikomi pranešimų ir duomenų įkainiai. - Paliesdami „%1$s“ nurodote, kad sutinkate su %2$s ir %3$s. Gali būti išsiųstas SMS pranešimas, taip pat – taikomi pranešimų ir duomenų įkainiai. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Autentifikavimo klaida + Bandykite dar kartą + Reikalingas papildomas patvirtinimas. Užbaikite kelių veiksnių autentifikavimą. + Paskyrą reikia susieti. Išbandykite kitą prisijungimo būdą. + Autentifikavimas buvo atšauktas. Kai būsite pasirengę, bandykite dar kartą. Pasirinkite autentifikavimo metodą @@ -144,29 +130,11 @@ Tapatybė patvirtinta. Bandykite dar kartą. Tvarkyti dviejų veiksnių autentifikavimą Neteisingas slaptažodis - Pasirinkite patvirtinimo metodą - Pridėkite papildomą saugumo sluoksnį - SMS - Autentifikavimo programa - Šis telefono numeris susietas su kita paskyra - Reikalingas patvirtinimas - Nuskaitykite QR kodą autentifikavimo programa - Autentifikavimo programa jau nustatyta - Ar tikrai norite pašalinti šį metodą? - Nepavyko pašalinti metodo - Metodas pašalintas - Įveskite patvirtinimo kodą - Nepavyko atnaujinti profilio - Profilis atnaujintas - Patvirtinkite savo tapatybę, kad tęstumėte - Reikalingas pakartotinis autentifikavimas - Pakartotinis autentifikavimas sėkmingas - Autentifikuoti iš naujo Pašalinti Siųsti patvirtinimo el. laišką iš naujo Slaptasis raktas Atsijungti - Prisijungta kaip + Prisijungta kaip %1$s Praleisti Naudoti kitą metodą Patvirtinimo kodas @@ -176,4 +144,17 @@ Daugiafaktoris tapatybės nustatymas šiuo metu išjungtas + Šis el. pašto adresas arba slaptažodis neteisingas + Šis patvirtinimo seansas nebegalioja. Paprašykite naujo kodo. + Telefono numerio patvirtinimas nebuvo baigtas. Bandykite dar kartą. + Šie prisijungimo duomenys priklauso kitai paskyrai. + Šis telefono numeris nenustatytas kaip patvirtinimo būdas šioje paskyroje. + Jūsų prisijungimo seansas baigėsi. Norėdami tęsti, prisijunkite dar kartą. + Ši nuoroda nebegalioja. Paprašykite naujos. + Prieš tęsdami patvirtinkite savo el. pašto adresą. + Šis patvirtinimo būdas šioje paskyroje jau nustatytas. + Pasiekėte šios paskyros patvirtinimo būdų ribą. + Jūsų slaptažodis neatitinka reikalavimų. Išbandykite kitą. + Slaptažodis per ilgas. Didžiausias ilgis yra %1$d. + Nepavyko rasti šios paskyros prieigos rakto. Prisijunkite kitu būdu. diff --git a/auth/src/main/res/values-lv/strings.xml b/auth/src/main/res/values-lv/strings.xml index fb0f2ca26e..9e06c57ac1 100755 --- a/auth/src/main/res/values-lv/strings.xml +++ b/auth/src/main/res/values-lv/strings.xml @@ -3,7 +3,6 @@ Pierakstīties Tālāk Turpinot jūs norādāt, ka piekrītat šādiem dokumentiem: %1$s un %2$s. - %1$s \u00A0 \u00A0 %2$s Tīkla kļūda. Pārbaudiet interneta savienojumu. Google Facebook @@ -41,12 +40,9 @@ Šo laukumu nedrīkst atstāt tukšu. Šī e-pasta adrese nav pareiza Lai turpinātu, ievadiet e-pasta adresi - Lūdzu, ievadiet vārdu un uzvārdu. - Tiek pārbaudīti esošie konti… Reģistrācija Vārds un uzvārds Saglabāt - Notiek reģistrācija… Parole nav pietiekami droša. Izmantojiet vismaz %1$d rakstzīmes un burtu un ciparu kombināciju Parole nav pietiekami droša. Izmantojiet vismaz %1$d rakstzīmi un burtu un ciparu kombināciju Parole nav pietiekami droša. Izmantojiet vismaz %1$d rakstzīmes un burtu un ciparu kombināciju Nesekmīga e-pasta konta reģistrācija Pakalpojumu sniegšanas noteikumi @@ -54,17 +50,12 @@ Jums jau ir konts Laipni lūdzam atpakaļ! Jums jau ir konts\n - Esat jau izmantojis e-pasta adresi %1$s. Pierakstieties pakalpojumā %2$s, lai turpinātu. - Esat jau izmantojis e-pasta adresi %1$s, lai pierakstītos. Ievadiet šī konta paroli. - Esat jau izmantojis e-pasta adresi %1$s. Varat saistīt savu %2$s kontu ar e-pasta adresi %1$s, pierakstoties ar tālāk norādīto e-pasta saiti.\n\nLai šī plūsma varētu sekmīgi saistīt jūsu %2$s kontu ar šo e-pasta adresi, jums ir jāatver saite tajā pašā ierīcē vai pārlūkprogrammā. - Notiek pierakstīšanās… Vai jums ir problēmas ar pierakstīšanos? Paroles atgūšana Pārbaudiet savu e-pastu Saņemiet uz šo e-pasta adresi nosūtītus norādījumus ar skaidrojumu, kā atiestatīt paroli. Sūtīt Izpildiet uz e-pasta adresi %1$s nosūtītos norādījumus, lai atgūtu paroli. - Notiek sūtīšana… E-pasta adrese neatbilst esošam kontam Radās nezināma kļūda. Nepareiza parole. @@ -90,25 +81,20 @@ Ievadiet derīgu tālruņa numuru Ievadiet 6 ciparu kodu, ko nosūtījām Vēlreiz nosūtīt kodu pēc %1$s - Verificēt tālruņa numuru Notiek verifikācija… Nepareizs kods. Mēģiniet vēlreiz. Šis tālruņa numurs ir izmantots pārāk daudz reižu - Verificējot jūsu tālruņa numuru, radās problēma Šis kods vairs nav derīgs - Pierakstieties ar tālruņa numuru - Tālruņa numurs tika automātiski verificēts Vēlreiz nosūtīt kodu Verificēt tālruņa numuru - Use a different phone number + Izmantot citu tālruņa numuru Pieskaroties pogai %1$s, var tikt nosūtīta īsziņa. Var tikt piemērota maksa par ziņojumiem un datu pārsūtīšanu. - Pieskaroties pogai “%1$s”, jūs norādāt, ka piekrītat šādiem dokumentiem: %2$s un %3$s. Var tikt nosūtīta īsziņa. Var tikt piemērota maksa par ziņojumiem un datu pārsūtīšanu. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Autentifikācijas kļūda + Mēģiniet vēlreiz + Nepieciešama papildu verifikācija. Lūdzu, pabeidziet vairāku faktoru autentifikāciju. + Konts ir jāsaista. Lūdzu, izmēģiniet citu pierakstīšanās metodi. + Autentifikācija tika atcelta. Lūdzu, mēģiniet vēlreiz, kad būsiet gatavs. Izvēlieties autentifikācijas metodi @@ -144,29 +130,11 @@ Identitāte apstiprināta. Mēģiniet vēlreiz. Pārvaldīt divfaktoru autentifikāciju Nepareiza parole - Izvēlieties verifikācijas metodi - Pievienojiet papildu drošības slāni - SMS - Autentifikācijas lietotne - Šis tālruņa numurs ir saistīts ar citu kontu - Nepieciešama verifikācija - Skenējiet QR kodu ar autentifikācijas lietotni - Autentifikācijas lietotne jau ir iestatīta - Vai tiešām vēlaties noņemt šo metodi? - Nevar noņemt metodi - Metode noņemta - Ievadiet verifikācijas kodu - Nevar atjaunināt profilu - Profils atjaunināts - Apstipriniet savu identitāti, lai turpinātu - Nepieciešama atkārtota autentifikācija - Atkārtota autentifikācija veiksmīga - Autentificēt atkārtoti Noņemt Atkārtoti nosūtīt verifikācijas e-pastu Slepenā atslēga Izrakstīties - Pierakstījies kā + Pierakstījies kā %1$s Izlaist Izmantot citu metodi Verifikācijas kods @@ -176,4 +144,17 @@ Daudzfaktoru autentifikācija pašlaik ir atspējota + E-pasta adrese vai parole nav pareiza + Šī verifikācijas sesija vairs nav derīga. Pieprasiet jaunu kodu. + Tālruņa numura verifikācija netika pabeigta. Lūdzu, mēģiniet vēlreiz. + Šie akreditācijas dati pieder citam kontam. + Šis tālruņa numurs šajā kontā nav iestatīts verifikācijai. + Jūsu pierakstīšanās sesijai ir beidzies derīguma termiņš. Lai turpinātu, piesakieties vēlreiz. + Šī saite vairs nav derīga. Pieprasiet jaunu. + Pirms turpināt, verificējiet savu e-pasta adresi. + Šī verifikācijas metode šajā kontā jau ir iestatīta. + Jūs esat sasniedzis šī konta verifikācijas metožu ierobežojumu. + Jūsu parole neatbilst prasībām. Mēģiniet citu. + Parole ir pārāk gara. Maksimālais garums ir %1$d. + Neatradām šī konta piekļuves atslēgu. Piesakieties citā veidā. diff --git a/auth/src/main/res/values-mo/strings.xml b/auth/src/main/res/values-mo/strings.xml index f96444e777..49fdbfb490 100755 --- a/auth/src/main/res/values-mo/strings.xml +++ b/auth/src/main/res/values-mo/strings.xml @@ -3,7 +3,6 @@ Conectați-vă Continuați Dacă alegeți să continuați, sunteți de acord cu %1$s și cu %2$s. - %1$s \u00A0 \u00A0 %2$s Eroare de rețea, verificați conexiunea la internet. Google Facebook @@ -41,12 +40,9 @@ Câmpul este obligatoriu. Adresa de e-mail nu este corectă Introduceți adresa de e-mail pentru a continua - Introduceți numele și prenumele. - Se caută conturi existente… Înscrieți-vă Prenume și nume de familie Salvați - Se creează un cont… Parola nu este suficient de puternică. Folosiți cel puțin %1$d caracter și o combinație de litere și cifre. Parola nu este suficient de puternică. Folosiți cel puțin %1$d caractere și o combinație de litere și cifre. Parola nu este suficient de puternică. Folosiți cel puțin %1$d de caractere și o combinație de litere și cifre. Contul de e-mail nu a fost înregistrat Termeni și condițiile @@ -54,17 +50,12 @@ Aveți deja un cont Bine ați revenit! Aveți deja un cont\n - Ați folosit deja %1$s. Pentru a continua, conectați-vă cu %2$s. - Ați folosit deja %1$s pentru a vă conecta. Introduceți parola acestui cont. - Ați folosit deja %1$s. Vă puteți conecta contul %2$s la %1$s folosind linkul din e-mail, aflat mai jos.\n\nPentru ca acest flux să conecteze contul %2$s la această adresă de e-mail, trebuie să accesați linkul pe același dispozitiv sau browser. - Se conectează… Nu reușiți să vă conectați? Recuperați parola Verificați căsuța de e-mail Primiți instrucțiuni de resetare a parolei la această adresă de e-mail. Trimiteți Urmați instrucțiunile trimise la %1$s pentru a vă recupera parola. - Se trimite… Adresa de e-mail nu corespunde unui cont existent A apărut o eroare necunoscută. Parolă incorectă @@ -90,25 +81,20 @@ Introduceți un număr valid de telefon Introduceți codul din 6 cifre pe care l-am trimis la Retrimiteți codul în 0%1$s - Confirmați numărul de telefon Se verifică… Cod greșit. Încercați din nou. Acest număr de telefon a fost folosit de prea multe ori - A apărut o problemă la confirmarea numărului de telefon Codul nu mai este valid - Conectați-vă cu numărul de telefon - Numărul de telefon este verificat automat Retrimiteți codul Confirmați numărul de telefon - Use a different phone number + Folosiți alt număr de telefon Dacă atingeți „%1$s”, poate fi trimis un SMS. Se pot aplica tarife pentru mesaje și date. - Dacă atingeți „%1$s”, sunteți de acord cu %2$s și cu %3$s. Poate fi trimis un SMS. Se pot aplica tarife pentru mesaje și date. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Eroare de autentificare + Încercați din nou + Este necesară o verificare suplimentară. Finalizați autentificarea cu mai mulți factori. + Contul trebuie asociat. Încercați altă metodă de conectare. + Autentificarea a fost anulată. Încercați din nou când sunteți gata. Alegeți metoda de autentificare @@ -144,29 +130,11 @@ Identitate verificată. Reîncercați acțiunea. Gestionați autentificarea în doi pași Parolă incorectă - Alegeți o metodă de verificare - Adăugați un strat suplimentar de securitate - SMS - Aplicație de autentificare - Acest număr de telefon este asociat cu alt cont - Verificare necesară - Scanați codul QR cu aplicația de autentificare - Aplicația de autentificare este deja configurată - Sigur doriți să eliminați această metodă? - Nu se poate elimina metoda - Metodă eliminată - Introduceți codul de verificare - Nu se poate actualiza profilul - Profil actualizat - Confirmați-vă identitatea pentru a continua - Este necesară reautentificarea - Reautentificare reușită - Reautentificare Eliminare Retrimite e-mailul de verificare Cheie secretă Deconectare - Conectat ca + Conectat ca %1$s Omite Folosiți o altă metodă Cod de verificare @@ -176,4 +144,17 @@ Autentificarea cu mai mulți factori este dezactivată în prezent + Adresa de e-mail sau parola nu este corectă + Sesiunea de verificare nu mai este validă. Solicitați un cod nou. + Verificarea numărului de telefon nu s-a finalizat. Încercați din nou. + Aceste date de conectare aparțin altui cont. + Acest număr de telefon nu este configurat pentru verificare în acest cont. + Sesiunea de conectare a expirat. Conectați-vă din nou pentru a continua. + Acest link nu mai este valid. Solicitați unul nou. + Confirmați adresa de e-mail înainte de a continua. + Această metodă de verificare este deja configurată în acest cont. + Ați atins limita de metode de verificare pentru acest cont. + Parola nu îndeplinește cerințele. Încercați alta. + Parola este prea lungă. Lungimea maximă este %1$d. + Nu am găsit o cheie de acces pentru acest cont. Conectați-vă în alt mod. diff --git a/auth/src/main/res/values-mr/strings.xml b/auth/src/main/res/values-mr/strings.xml index 38d2a9c8da..d7e33a72ed 100755 --- a/auth/src/main/res/values-mr/strings.xml +++ b/auth/src/main/res/values-mr/strings.xml @@ -3,7 +3,6 @@ साइन इन करा सुरू ठेवा पुढे सुरू ठेवून, तुम्ही सूचित करता की तुम्ही आमचे %1$s आणि %2$s स्वीकारता. - %1$s \u00A0 \u00A0 %2$s नेटवर्क एरर, तुमचे इंटरनेट कनेक्शन तपासा. Google Facebook @@ -41,12 +40,9 @@ तुम्ही हे रिक्त सोडू शकत नाही. तो ईमेल ॲड्रेस चुकीचा आहे सुरू ठेवण्यासाठी तुमचा ईमेल ॲड्रेस टाका - कृपया नाव आणि आडनाव एंटर करा. - सध्याच्या खात्यांसाठी तपासत आहे… साइन अप करा नाव आणि आडनाव सेव्ह करा - साइन अप करत आहे… पासवर्ड हवा तितका सक्षम नाही. कमीत कमी %1$d वर्ण आणि अंक व अक्षराचे मिश्रण वापरा पासवर्ड हवा तितका सक्षम नाही. कमीत कमी %1$d वर्ण आणि अंक अक्षरांचे मिश्रण वापरा ईमेल खात्याची नोंदणी अयशस्वी झाली सेवा अटी @@ -54,17 +50,12 @@ तुमचे आधीच एक खाते आहे पुन्हा एकदा स्वागत आहे! तुमच्याकडे आधीपासूनच खाते आहे\n - तुम्ही %1$s आधीच वापरला आहे. सुरू ठेवण्यासाठी %2$s ने साइन इन करा. - तुम्ही %1$s आधीच वापरला आहे. साइन इन करण्यासाठी तुमचा त्या खात्याचा पासवर्ड टाका. - तुम्ही आधीच %1$s वापरला आहे. तुम्ही खालील ईमेल लिंकसह साइन इन करून तुमचे %2$s खाते %1$s शी कनेक्ट करू शकता.\n\nतुमचे %2$s खाते या ईमेलसह यशस्वीरीत्या कनेक्ट करण्यासाठी, तुम्हाला लिंक समान डिव्हाइस किंवा ब्राउझरवर उघडणे आवश्यक आहे. - साइन इन करत आहे… साइन इन करताना समस्या आली? पासवर्ड पुन्हा मिळवा तुमचा ईमेल तपासा तुमचा पासवर्ड रीसेट कसा करावा यासंबंधित या ईमेलवर पाठवलेल्या सूचना मिळवा. पाठवा तुमचा पासवर्ड परत मिळवण्यासाठी %1$sवर पाठवलेल्या सूचनांचे पालन करा. - पाठवत आहे… तो ईमेल ॲड्रेस सध्याच्या खात्याशी जुळत नाही एक अज्ञात एरर आली. अयोग्य पासवर्ड @@ -90,25 +81,20 @@ कृपया वैध फोन नंबर टाका वर आम्ही पाठवलेला 6 अंकी कोड टाका कोड %1$sमध्ये पुन्हा पाठवा - तुमच्या फोन नंबरची पडताळणी करा पडताळणी करत आहे… कोड चुकीचा आहे. पुन्हा प्रयत्न करा. हा फोन नंबर बर्‍याच वेळा वापरण्‍यात आला आहे - तुमच्‍या फोन नंबरची पडताळणी करण्‍यात एक समस्या होती हा कोड यापुढे वैध नसेल - फोन नंबरने साइन इन करा - फोन नंबरची अपोआप पडताळणी केली आहे कोड पुन्हा पाठवा फोन नंबरची पडताळणी करा - Use a different phone number + वेगळा फोन नंबर वापरा “%1$s“ वर टॅप केल्याने, एक एसएमएस पाठवला जाऊ शकतो. मेसेज आणि डेटा शुल्क लागू होऊ शकते. - “%1$s” वर टॅप करून, तुम्ही सूचित करता की तुम्ही आमचे %2$s आणि %3$s स्वीकारता. एसएमएस पाठवला जाऊ शकतो. मेसेज आणि डेटा दर लागू केले जाऊ शकते. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + प्रमाणीकरण एरर + पुन्हा प्रयत्न करा + अतिरिक्त पडताळणी आवश्यक आहे. कृपया मल्टी-फॅक्टर प्रमाणीकरण पूर्ण करा. + खाते लिंक करणे आवश्यक आहे. कृपया वेगळी साइन इन पद्धत वापरून पहा. + प्रमाणीकरण रद्द केले गेले. तुम्ही तयार असाल तेव्हा कृपया पुन्हा प्रयत्न करा. प्रमाणीकरण पद्धत निवडा @@ -144,29 +130,11 @@ ओळख सत्यापित झाली. तुमची क्रिया पुन्हा प्रयत्न करा. दोन-घटक प्रमाणीकरण व्यवस्थापित करा चुकीचा पासवर्ड - सत्यापन पद्धत निवडा - सुरक्षेचा अतिरिक्त स्तर जोडा - SMS - प्रमाणीकरण ॲप - हा फोन नंबर दुसऱ्या खात्याशी संबंधित आहे - सत्यापन आवश्यक आहे - तुमच्या प्रमाणीकरण ॲपसह QR कोड स्कॅन करा - प्रमाणीकरण ॲप आधीच सेट अप आहे - तुमची खात्री आहे की तुम्हाला ही पद्धत काढायची आहे? - पद्धत काढू शकत नाही - पद्धत काढली - सत्यापन कोड प्रविष्ट करा - प्रोफाइल अपडेट करू शकत नाही - प्रोफाइल अपडेट केले - सुरू ठेवण्यासाठी तुमची ओळख पुष्टी करा - पुन्हा प्रमाणीकरण आवश्यक आहे - पुन्हा प्रमाणीकरण यशस्वी - पुन्हा प्रमाणीकरण काढा सत्यापन ईमेल पुन्हा पाठवा गुप्त की साइन आउट - म्हणून साइन इन केले + %1$s म्हणून साइन इन केले वगळा वेगळी पद्धत वापरा सत्यापन कोड @@ -176,4 +144,17 @@ मल्टी-फॅक्टर ऑथेंटिकेशन सध्या अक्षम आहे + ते ईमेल किंवा पासवर्ड बरोबर नाही + ते पडताळणी सत्र यापुढे वैध नाही. नवीन कोडची विनंती करा. + फोनची पडताळणी पूर्ण झाली नाही. कृपया पुन्हा प्रयत्न करा. + ती क्रेडेन्शियल वेगळ्या खात्याची आहेत. + तो फोन नंबर या खात्यावर पडताळणीसाठी सेट केलेला नाही. + तुमचे साइन इन सत्र एक्स्पायर झाले आहे. सुरू ठेवण्यासाठी पुन्हा साइन इन करा. + ती लिंक यापुढे वैध नाही. नवीन लिंकची विनंती करा. + तुम्ही पुढे सुरू ठेवण्यापूर्वी तुमच्या ईमेल ॲड्रेसची पडताळणी करा. + ती पडताळणी पद्धत या खात्यावर आधीपासून सेट केलेली आहे. + तुम्ही या खात्यावरील पडताळणी पद्धतींची मर्यादा गाठली आहे. + तुमचा पासवर्ड आवश्यकता पूर्ण करत नाही. दुसरा पासवर्ड वापरून पहा. + पासवर्ड खूप लांब आहे. कमाल लांबी %1$d आहे. + या खात्यासाठी पासकी सापडली नाही. दुसऱ्या पद्धतीने साइन इन करा. diff --git a/auth/src/main/res/values-ms/strings.xml b/auth/src/main/res/values-ms/strings.xml index f06075d773..69d44c4f87 100755 --- a/auth/src/main/res/values-ms/strings.xml +++ b/auth/src/main/res/values-ms/strings.xml @@ -3,7 +3,6 @@ Log masuk Teruskan Dengan meneruskan, anda menyatakan bahawa anda menerima %1$s dan %2$s kami. - %1$s \u00A0 \u00A0 %2$s Ralat rangkaian, semak sambungan Internet anda. Google Facebook @@ -41,12 +40,9 @@ Anda tidak boleh membiarkan ruang ini kosong. Alamat e-mel itu tidak betul Masukkan alamat e-mel anda untuk teruskan - Sila masukkan nama pertama dan nama keluarga. - Menyemak akaun sedia ada… Daftar Nama pertama & keluarga Simpan - Mendaftar… Kata laluan tidak kukuh. Gunakan sekuang-kurangnya %1$d aksara dan gabungkan huruf dengan nombor Kata laluan tidak kukuh. Gunakan sekuang-kurangnya %1$d aksara dan gabungkan huruf dengan nombor Pendaftaran akaun e-mel tidak berjaya Syarat Perkhidmatan @@ -54,17 +50,12 @@ Anda sudah mempunyai akaun Selamat kembali! Anda sudah mempunyai akaun\n - Anda sudah menggunakan %1$s. Log masuk dengan %2$s untuk teruskan. - Anda sudah menggunakan %1$s untuk log masuk. Masukkan kata laluan anda untuk akaun tersebut. - Anda sudah menggunakan %1$s. Anda boleh menyambungkan akaun %2$s anda dengan %1$s melalui log masuk menggunakan pautan e-mel di bawah.\n\nUntuk memastikan aliran ini disambungkan kepada akaun %2$s anda menggunakan e-mel ini, anda perlu membuka pautan pada peranti atau penyemak imbas yang sama. - Log masuk… Menghadapi masalah log masuk? Pulihkan kata laluan Semak e-mel anda Dapatkan arahan pada e-mel ini untuk mengetahui cara menetapkan semula kata laluan anda. Hantar Ikut arahan yang dihantar ke %1$s untuk memulihkan kata laluan anda. - Menghantar… Alamat e-mel tersebut tidak sepadan dengan akaun sedia ada Ralat tidak diketahui telah berlaku. Kata laluan tidak betul. @@ -90,25 +81,20 @@ Masukkan nombor telefon yang sah Masukkan kod 6 digit yang kami hantar ke Hantar semula kod dalam %1$s - Sahkan nombor telefon anda Mengesahkan… Kod salah. Cuba lagi. Nombor telefon ini terlalu kerap digunakan - Terdapat masalah sewaktu mengesahkan nombor telefon anda Kod ini tidak sah lagi - Log masuk dengan nombor telefon - Nombor telefon disahkan secara automatik Hantar Semula Kod Sahkan Nombor Telefon - Use a different phone number + Gunakan nombor telefon lain Dengan mengetik “%1$s”, SMS akan dihantar. Tertakluk pada kadar mesej & data. - Dengan mengetik “%1$s”, anda menyatakan bahawa anda menerima %2$s dan %3$s kami. SMS akan dihantar. Tertakluk pada kadar mesej & data. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Ralat Pengesahan + Cuba lagi + Pengesahan tambahan diperlukan. Sila lengkapkan pengesahan berbilang faktor. + Akaun perlu dipautkan. Sila cuba kaedah log masuk yang lain. + Pengesahan dibatalkan. Sila cuba lagi apabila anda bersedia. Pilih Kaedah Pengesahan @@ -144,29 +130,11 @@ Identiti disahkan. Cuba tindakan anda semula. Urus pengesahan dua faktor Kata laluan salah - Pilih kaedah pengesahan - Tambah lapisan keselamatan tambahan - SMS - Apl pengesahan - Nombor telefon ini dikaitkan dengan akaun lain - Pengesahan diperlukan - Imbas kod QR dengan apl pengesahan anda - Apl pengesahan telah disediakan - Adakah anda pasti mahu membuang kaedah ini? - Tidak dapat membuang kaedah - Kaedah dibuang - Masukkan kod pengesahan - Tidak dapat mengemas kini profil - Profil dikemas kini - Sahkan identiti anda untuk meneruskan - Pengesahan semula diperlukan - Pengesahan semula berjaya - Sahkan semula Buang Hantar semula e-mel pengesahan Kunci rahsia Log keluar - Log masuk sebagai + Log masuk sebagai %1$s Langkau Gunakan kaedah lain Kod pengesahan @@ -176,4 +144,17 @@ Pengesahan berbilang faktor dilumpuhkan buat masa ini + E-mel atau kata laluan tersebut tidak betul + Sesi pengesahan tersebut tidak sah lagi. Minta kod baharu. + Pengesahan telefon tidak selesai. Sila cuba lagi. + Kelayakan tersebut milik akaun lain. + Nombor telefon tersebut tidak disediakan untuk pengesahan pada akaun ini. + Sesi log masuk anda telah tamat tempoh. Log masuk semula untuk meneruskan. + Pautan tersebut tidak sah lagi. Minta pautan baharu. + Sahkan alamat e-mel anda sebelum anda meneruskan. + Kaedah pengesahan tersebut telah pun disediakan pada akaun ini. + Anda telah mencapai had kaedah pengesahan pada akaun ini. + Kata laluan anda tidak memenuhi keperluan. Cuba kata laluan lain. + Kata laluan terlalu panjang. Panjang maksimum ialah %1$d. + Kami tidak menemui kunci laluan untuk akaun ini. Log masuk dengan cara lain. diff --git a/auth/src/main/res/values-nb/strings.xml b/auth/src/main/res/values-nb/strings.xml index ac494d9cd6..e792dffd52 100755 --- a/auth/src/main/res/values-nb/strings.xml +++ b/auth/src/main/res/values-nb/strings.xml @@ -3,7 +3,6 @@ Logg på Fortsett Ved å fortsette godtar du %1$s og %2$s våre. - %1$s \u00A0 \u00A0 %2$s Nettverksfeil. Sjekk internettilkoblingen din. Google Facebook @@ -41,12 +40,9 @@ Du må fylle ut dette feltet. E-postadressen er ikke riktig Oppgi e-postadressen din for å fortsette - Skriv inn et for- og etternavn. - Ser etter eksisterende kontoer… Registrering Fornavn og etternavn Lagre - Registrerer… Passordet er ikke sterkt nok. Bruk minst %1$d tegn og en blanding av bokstaver og tall Passordet er ikke sterkt nok. Bruk minst %1$d tegn og en blanding av bokstaver og tall Kunne ikke registrere e-postkontoen vilkårene @@ -54,17 +50,13 @@ Du har allerede en konto Velkommen tilbake! Du har allerede en konto\n - Du har allerede brukt %1$s. Logg på med %2$s for å fortsette. - Du har allerede brukt %1$s for å logge på. Angi passordet ditt for denne kontoen. - Du har allerede brukt %1$s. Du kan koble %2$s-kontoen din til %1$s ved å logge på med e-postlinken nedenfor.\n\nDu må åpne linken med den samme enheten eller nettleseren for at %2$s-kontoen din skal bli koblet til denne e-postadressen. - Logger på… Har du problemer med å logge på? Gjenopprett passordet Sjekk e-posten din Få instruksjoner sendt til denne e-postadressen for hvordan du tilbakestiller passordet ditt. - Send + + Send Følg veiledningen som er sendt til %1$s, for å gjenopprette passordet ditt. - Sender… Denne e-postadressen samsvarer ikke med en eksisterende konto Det oppsto en ukjent feil. Feil passord. @@ -90,19 +82,14 @@ Oppgi et gyldig telefonnummer Oppgi den 6-sifrede koden vi sendte til Send koden på nytt om %1$s - Bekreft telefonnummeret ditt Bekrefter… Feil kode. Prøv på nytt. Dette telefonnummeret er brukt for mange ganger - Kunne ikke bekrefte telefonnummeret ditt Denne koden er ikke lenger gyldig - Logg på med telefonnummer - Telefonnummeret ble bekreftet automatisk Send koden på nytt Bekreft telefonnummeret - Use a different phone number + Bruk et annet telefonnummer Når du trykker på «%1$s», kan det bli sendt en SMS. Kostnader for meldinger og datatrafikk kan påløpe. - Ved å trykke på «%1$s» godtar du %2$s og %3$s våre. Du kan bli tilsendt en SMS. Kostnader for meldinger og datatrafikk kan påløpe. Godkjenningsfeil Prøv igjen Ytterligere verifisering kreves. Vennligst fullfør multifaktorgodkjenning. @@ -143,29 +130,11 @@ Identitet bekreftet. Prøv handlingen din på nytt. Administrer tofaktorautentisering Feil passord - Velg en bekreftelsesmetode - Legg til et ekstra sikkerhetslag - SMS - Autentiseringsapp - Dette telefonnummeret er knyttet til en annen konto - Bekreftelse påkrevd - Skann QR-koden med autentiseringsappen din - Autentiseringsappen er allerede konfigurert - Er du sikker på at du vil fjerne denne metoden? - Kan ikke fjerne metode - Metode fjernet - Angi bekreftelseskode - Kan ikke oppdatere profil - Profil oppdatert - Bekreft identiteten din for å fortsette - Ny autentisering påkrevd - Ny autentisering vellykket - Autentiser på nytt Fjern Send bekreftelsese-post på nytt Hemmelig nøkkel Logg ut - Logget inn som + Logget inn som %1$s Hopp over Bruk en annen metode Bekreftelseskode @@ -175,4 +144,17 @@ Flerfaktorautentisering er for øyeblikket deaktivert + E-postadressen eller passordet er feil + Denne bekreftelsesøkten er ikke lenger gyldig. Be om en ny kode. + Telefonbekreftelsen ble ikke fullført. Prøv igjen. + Denne påloggingsinformasjonen tilhører en annen konto. + Dette telefonnummeret er ikke satt opp for bekreftelse på denne kontoen. + Påloggingsøkten din er utløpt. Logg på igjen for å fortsette. + Denne lenken er ikke lenger gyldig. Be om en ny. + Bekreft e-postadressen din før du fortsetter. + Denne bekreftelsesmetoden er allerede satt opp på denne kontoen. + Du har nådd grensen for bekreftelsesmetoder på denne kontoen. + Passordet ditt oppfyller ikke kravene. Prøv et annet. + Passordet er for langt. Maksimal lengde er %1$d. + Vi fant ingen passnøkkel for denne kontoen. Logg på en annen måte. diff --git a/auth/src/main/res/values-nl/strings.xml b/auth/src/main/res/values-nl/strings.xml index 064497191c..8ce32e6522 100755 --- a/auth/src/main/res/values-nl/strings.xml +++ b/auth/src/main/res/values-nl/strings.xml @@ -3,7 +3,6 @@ Inloggen Doorgaan Als u doorgaat, geeft u aan dat u onze %1$s en ons %2$s accepteert. - %1$s \u00A0 \u00A0 %2$s Er is een netwerkfout opgetreden. Controleer uw internetverbinding. Google Facebook @@ -41,12 +40,9 @@ Dit veld mag niet leeg zijn. Dit e-mailadres is niet juist Voer uw e-mailadres in om verder te gaan - Voer een voor- en achternaam in. - Controleren op bestaande accounts… Aanmelden Voor- en achternaam Opslaan - Aanmelden… Uw wachtwoord is niet sterk genoeg. Gebruik ten minste %1$d teken en een combinatie van letters en cijfers. Uw wachtwoord is niet sterk genoeg. Gebruik ten minste %1$d tekens en een combinatie van letters en cijfers. Registratie van e-mailaccount is mislukt Servicevoorwaarden @@ -54,17 +50,12 @@ U heeft al een account Welkom terug. U heeft al een account\n - U heeft %1$s al gebruikt. Log in met %2$s om verder te gaan. - U heeft al ingelogd met %1$s. Voer uw wachtwoord in voor dit account. - U heeft %1$s al gebruikt. U kunt uw %2$s-account aan %1$s koppelen door in te loggen via de e-mail link hieronder.\n\nAls u uw %2$s-account aan dit e-mailadres wilt koppelen, moet u de link openen op hetzelfde apparaat of in dezelfde browser. - Inloggen… Problemen met inloggen? Wachtwoord herstellen Controleer uw inbox Er wordt een e-mail naar dit adres verzonden met instructies om uw wachtwoord te resetten. Verzenden Volg de instructies die we naar %1$s hebben verzonden om uw wachtwoord te herstellen. - Verzenden… Dit e-mailadres komt niet overeen met een bestaand account Er is een onbekende fout opgetreden. Onjuist wachtwoord. @@ -90,19 +81,14 @@ Voer een geldig telefoonnummer in Voer de zescijferige code in die we hebben verzonden naar Code opnieuw verzenden over %1$s - Uw telefoonnummer verifiëren Verifiëren… Onjuiste code. Probeer het opnieuw. Dit telefoonnummer is te vaak gebruikt - Er is een probleem met de verificatie van uw telefoonnummer Deze code is niet meer geldig - Inloggen met telefoonnummer - Telefoonnummer is automatisch geverifieerd Code opnieuw verzenden Telefoonnummer verifiëren - Use a different phone number + Gebruik een ander telefoonnummer Als u op “%1$s” tikt, ontvangt u mogelijk een sms. Er kunnen sms- en datakosten in rekening worden gebracht. - Als u op "%1$s" tikt, geeft u aan dat u onze %2$s en ons %3$s accepteert. Mogelijk ontvangt u een sms. Er kunnen sms- en datakosten in rekening worden gebracht. Authenticatiefout Opnieuw proberen Aanvullende verificatie vereist. Voltooi de multi-factor authenticatie. @@ -143,29 +129,11 @@ Identiteit geverifieerd. Probeer je actie opnieuw. Tweefactorauthenticatie beheren Onjuist wachtwoord - Kies een verificatiemethode - Voeg een extra beveiligingslaag toe - SMS - Authenticatie-app - Dit telefoonnummer is al gekoppeld aan een ander account - Verificatie vereist - Scan de QR-code met je authenticatie-app - Authenticatie-app is al ingesteld - Weet je zeker dat je deze methode wilt verwijderen? - Kan methode niet verwijderen - Methode verwijderd - Voer de verificatiecode in - Kan profiel niet bijwerken - Profiel bijgewerkt - Bevestig je identiteit om door te gaan - Herauthenticatie vereist - Herauthenticatie geslaagd - Opnieuw authenticeren Verwijderen Verificatie-e-mail opnieuw verzenden Geheime sleutel Uitloggen - Ingelogd als + Ingelogd als %1$s Overslaan Gebruik een andere methode Verificatiecode @@ -175,4 +143,17 @@ Multi-factorauthenticatie is momenteel uitgeschakeld + Dat e-mailadres of wachtwoord is onjuist + Deze verificatiesessie is niet meer geldig. Vraag een nieuwe code aan. + De telefoonverificatie is niet voltooid. Probeer het opnieuw. + Deze inloggegevens horen bij een ander account. + Dat telefoonnummer is niet ingesteld voor verificatie op dit account. + Uw inlogsessie is vervallen. Log opnieuw in om door te gaan. + Deze link is niet meer geldig. Vraag een nieuwe aan. + Bevestig uw e-mailadres voordat u doorgaat. + Deze verificatiemethode is al ingesteld op dit account. + U heeft de limiet voor verificatiemethoden op dit account bereikt. + Uw wachtwoord voldoet niet aan de vereisten. Probeer een ander wachtwoord. + Het wachtwoord is te lang. De maximale lengte is %1$d. + We hebben geen toegangssleutel voor dit account gevonden. Log op een andere manier in. diff --git a/auth/src/main/res/values-no/strings.xml b/auth/src/main/res/values-no/strings.xml index 7df53a222c..f6ac96bf73 100755 --- a/auth/src/main/res/values-no/strings.xml +++ b/auth/src/main/res/values-no/strings.xml @@ -3,7 +3,6 @@ Logg på Fortsett Ved å fortsette godtar du %1$s og %2$s våre. - %1$s \u00A0 \u00A0 %2$s Nettverksfeil. Sjekk internettilkoblingen din. Google Facebook @@ -41,12 +40,9 @@ Du må fylle ut dette feltet. E-postadressen er ikke riktig Oppgi e-postadressen din for å fortsette - Skriv inn et for- og etternavn. - Ser etter eksisterende kontoer… Registrering Fornavn og etternavn Lagre - Registrerer… Passordet er ikke sterkt nok. Bruk minst %1$d tegn og en blanding av bokstaver og tall Passordet er ikke sterkt nok. Bruk minst %1$d tegn og en blanding av bokstaver og tall Kunne ikke registrere e-postkontoen vilkårene @@ -54,17 +50,13 @@ Du har allerede en konto Velkommen tilbake! Du har allerede en konto\n - Du har allerede brukt %1$s. Logg på med %2$s for å fortsette. - Du har allerede brukt %1$s for å logge på. Angi passordet ditt for denne kontoen. - Du har allerede brukt %1$s. Du kan koble %2$s-kontoen din til %1$s ved å logge på med e-postlinken nedenfor.\n\nDu må åpne linken med den samme enheten eller nettleseren for at %2$s-kontoen din skal bli koblet til denne e-postadressen. - Logger på… Har du problemer med å logge på? Gjenopprett passordet Sjekk e-posten din Få instruksjoner sendt til denne e-postadressen for hvordan du tilbakestiller passordet ditt. - Send + + Send Følg veiledningen som er sendt til %1$s, for å gjenopprette passordet ditt. - Sender… Denne e-postadressen samsvarer ikke med en eksisterende konto Det oppsto en ukjent feil. Feil passord. @@ -90,25 +82,20 @@ Oppgi et gyldig telefonnummer Oppgi den 6-sifrede koden vi sendte til Send koden på nytt om %1$s - Bekreft telefonnummeret ditt Bekrefter… Feil kode. Prøv på nytt. Dette telefonnummeret er brukt for mange ganger - Kunne ikke bekrefte telefonnummeret ditt Denne koden er ikke lenger gyldig - Logg på med telefonnummer - Telefonnummeret ble bekreftet automatisk Send koden på nytt Bekreft telefonnummeret - Use a different phone number + Bruk et annet telefonnummer Når du trykker på «%1$s», kan det bli sendt en SMS. Kostnader for meldinger og datatrafikk kan påløpe. - Ved å trykke på «%1$s» godtar du %2$s og %3$s våre. Du kan bli tilsendt en SMS. Kostnader for meldinger og datatrafikk kan påløpe. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Godkjenningsfeil + Prøv igjen + Ytterligere verifisering kreves. Vennligst fullfør multifaktorgodkjenning. + Kontoen må kobles. Prøv en annen påloggingsmetode. + Godkjenning ble avbrutt. Prøv igjen når du er klar. Velg autentiseringsmetode @@ -144,29 +131,11 @@ Identitet bekreftet. Prøv handlingen din på nytt. Administrer tofaktorautentisering Feil passord - Velg en bekreftelsesmetode - Legg til et ekstra sikkerhetslag - SMS - Autentiseringsapp - Dette telefonnummeret er knyttet til en annen konto - Bekreftelse påkrevd - Skann QR-koden med autentiseringsappen din - Autentiseringsappen er allerede konfigurert - Er du sikker på at du vil fjerne denne metoden? - Kan ikke fjerne metode - Metode fjernet - Angi bekreftelseskode - Kan ikke oppdatere profil - Profil oppdatert - Bekreft identiteten din for å fortsette - Ny autentisering påkrevd - Ny autentisering vellykket - Autentiser på nytt Fjern Send bekreftelsese-post på nytt Hemmelig nøkkel Logg ut - Logget inn som + Logget inn som %1$s Hopp over Bruk en annen metode Bekreftelseskode @@ -176,4 +145,17 @@ Flerfaktorautentisering er for øyeblikket deaktivert + E-postadressen eller passordet er feil + Denne bekreftelsesøkten er ikke lenger gyldig. Be om en ny kode. + Telefonbekreftelsen ble ikke fullført. Prøv igjen. + Denne påloggingsinformasjonen tilhører en annen konto. + Dette telefonnummeret er ikke satt opp for bekreftelse på denne kontoen. + Påloggingsøkten din er utløpt. Logg på igjen for å fortsette. + Denne lenken er ikke lenger gyldig. Be om en ny. + Bekreft e-postadressen din før du fortsetter. + Denne bekreftelsesmetoden er allerede satt opp på denne kontoen. + Du har nådd grensen for bekreftelsesmetoder på denne kontoen. + Passordet ditt oppfyller ikke kravene. Prøv et annet. + Passordet er for langt. Maksimal lengde er %1$d. + Vi fant ingen passnøkkel for denne kontoen. Logg på en annen måte. diff --git a/auth/src/main/res/values-pl/strings.xml b/auth/src/main/res/values-pl/strings.xml index f651ddb899..f5309304f7 100755 --- a/auth/src/main/res/values-pl/strings.xml +++ b/auth/src/main/res/values-pl/strings.xml @@ -3,7 +3,6 @@ Zaloguj się Dalej Kontynuując, potwierdzasz, że akceptujesz te dokumenty: %1$s i %2$s. - %1$s     %2$s Błąd sieci. Sprawdź połączenie z internetem. Google Facebook @@ -41,12 +40,9 @@ Nie możesz zostawić tego pola pustego. Nieprawidłowy adres e-mail Aby kontynuować, wpisz adres e-mail - Wpisz imię i nazwisko. - Szukam istniejących kont… Rejestracja Imię i nazwisko Zapisz - Rejestruję… Hasło jest za słabe. Powinno mieć przynajmniej %1$d znak i być kombinacją liter i cyfr. Hasło jest za słabe. Powinno mieć przynajmniej %1$d znaki i być kombinacją liter i cyfr. Hasło jest za słabe. Powinno mieć przynajmniej %1$d znaków i być kombinacją liter i cyfr. Hasło jest za słabe. Powinno mieć przynajmniej %1$d znaku i być kombinacją liter i cyfr Rejestracja konta e-mail nie powiodła się Warunki korzystania z usługi @@ -54,17 +50,12 @@ Masz już konto Witamy z powrotem Masz już konto\n - Adres %1$s został już przez Ciebie użyty. Aby kontynuować, zaloguj się przez: %2$s. - Adres %1$s został już przez Ciebie użyty do logowania. Wpisz hasło tego konta. - Adres %1$s został już użyty. Możesz połączyć swoje konto w serwisie %2$s z adresem %1$s, logując się poniżej za pomocą e-maila z linkiem.\n\nAby procedura łączenia konta w serwisie %2$s z tym adresem powiodła się, link musisz otworzyć na tym samym urządzeniu i w tej samej przeglądarce. - Loguję… Masz problem z zalogowaniem się? Odzyskiwanie hasła Sprawdź pocztę Na ten adres e-mail zostaną wysłane instrukcje wyjaśniające, jak zresetować hasło. Wyślij Aby odzyskać hasło, wykonaj instrukcje, które wysłaliśmy Ci na adres %1$s. - Wysyłam… Nie istnieje konto, do którego pasuje ten adres e-mail. Wystąpił nieznany błąd. Nieprawidłowe hasło. @@ -90,19 +81,14 @@ Wpisz prawidłowy numer telefonu Wpisz sześciocyfrowy kod, który wysłaliśmy na numer Kod można ponownie wysłać za %1$s - Zweryfikuj swój numer telefonu Weryfikuję… Nieprawidłowy kod. Spróbuj jeszcze raz. Ten numer telefonu został użyty zbyt wiele razy. - Podczas weryfikacji Twojego numeru telefonu wystąpił problem. Ten kod stracił ważność. - Zaloguj się z użyciem numeru telefonu - Numer telefonu został automatycznie zweryfikowany Wyślij kod ponownie Zweryfikuj numer telefonu - Use a different phone number + Użyj innego numeru telefonu Gdy klikniesz „%1$s”, może zostać wysłany SMS. Może to skutkować pobraniem opłaty za przesłanie wiadomości i danych. - Klikając „%1$s", potwierdzasz, że akceptujesz te dokumenty: %2$s i %3$s. Może zostać wysłany SMS. Może to skutkować pobraniem opłat za przesłanie wiadomości i danych. Błąd uwierzytelniania Spróbuj ponownie Wymagana dodatkowa weryfikacja. Proszę ukończyć uwierzytelnianie wieloskładnikowe. @@ -143,29 +129,11 @@ Tożsamość zweryfikowana. Spróbuj ponownie wykonać czynność. Zarządzaj uwierzytelnianiem dwuskładnikowym Nieprawidłowe hasło - Wybierz metodę weryfikacji - Dodaj dodatkową warstwę zabezpieczeń - SMS - Aplikacja uwierzytelniająca - Ten numer telefonu jest powiązany z innym kontem - Wymagana weryfikacja - Zeskanuj kod QR za pomocą aplikacji uwierzytelniającej - Aplikacja uwierzytelniająca jest już skonfigurowana - Czy na pewno chcesz usunąć tę metodę? - Nie można usunąć metody - Metoda usunięta - Wprowadź kod weryfikacyjny - Nie można zaktualizować profilu - Profil zaktualizowany - Potwierdź swoją tożsamość, aby kontynuować - Wymagane ponowne uwierzytelnienie - Ponowne uwierzytelnienie zakończone sukcesem - Uwierzytelnij ponownie Usuń Wyślij ponownie e-mail weryfikacyjny Tajny klucz Wyloguj się - Zalogowano jako + Zalogowano jako %1$s Pomiń Użyj innej metody Kod weryfikacyjny @@ -175,4 +143,17 @@ Uwierzytelnianie wieloskładnikowe jest obecnie wyłączone + Ten adres e-mail lub hasło są nieprawidłowe + Ta sesja weryfikacji nie jest już ważna. Poproś o nowy kod. + Weryfikacja numeru telefonu nie została ukończona. Spróbuj ponownie. + Te dane logowania należą do innego konta. + Ten numer telefonu nie jest skonfigurowany do weryfikacji na tym koncie. + Sesja logowania wygasła. Zaloguj się ponownie, aby kontynuować. + Ten link nie jest już ważny. Poproś o nowy. + Zweryfikuj swój adres e-mail, zanim przejdziesz dalej. + Ta metoda weryfikacji jest już skonfigurowana na tym koncie. + Osiągnięto limit metod weryfikacji na tym koncie. + Twoje hasło nie spełnia wymagań. Spróbuj innego. + Hasło jest za długie. Maksymalna długość to %1$d. + Nie znaleziono klucza dostępu do tego konta. Zaloguj się w inny sposób. diff --git a/auth/src/main/res/values-pt-rBR/strings.xml b/auth/src/main/res/values-pt-rBR/strings.xml index 76704c93f6..364571920d 100755 --- a/auth/src/main/res/values-pt-rBR/strings.xml +++ b/auth/src/main/res/values-pt-rBR/strings.xml @@ -3,7 +3,6 @@ Fazer login Continuar Ao continuar, você concorda com nossos %1$s e a %2$s. - %1$s \u00A0 \u00A0 %2$s Erro de rede, confira sua conexão de Internet. Google Facebook @@ -41,30 +40,22 @@ Esse campo não pode ficar em branco. Endereço de e-mail inválido Digite seu endereço de e-mail para continuar - Digite um nome e um sobrenome. - Verificando contas existentes… Inscrever-se Nome e sobrenome Salvar - Concluindo sua inscrição… - A senha não é forte o suficiente. Use pelo menos %1$d caractere e combine letras e números. A senha não é forte o suficiente. Use pelo menos %1$d caracteres e combine letras e números. + A senha não é forte o suficiente. Use pelo menos %1$d caractere e combine letras e números. A senha não é forte o suficiente. Use pelo menos %1$d de caracteres e combine letras e números. A senha não é forte o suficiente. Use pelo menos %1$d caracteres e combine letras e números. Falha no registro da conta do e-mail Termos de Serviço Política de privacidade Você já tem uma conta É bom ver você novamente! Você já tem uma conta\n - Você já usou o e-mail %1$s. Faça login com o %2$s para continuar. - Você já usou o e-mail %1$s para fazer login. Insira a senha dessa conta. - Você já usou %1$s. Você pode conectar sua conta do %2$s com %1$s ao fazer login usando o link de e-mail abaixo.\n\nPara conectar sua conta do %2$s com este e-mail, é necessário abrir o link no mesmo dispositivo ou navegador. - Fazendo login… Problemas com login? Recuperar a senha Verifique seu e-mail Veja como redefinir sua senha nas instruções enviadas para este e-mail. Enviar Siga as instruções enviadas para %1$s para recuperar sua senha. - Enviando… O endereço de e-mail não corresponde a uma conta existente Ocorreu um erro desconhecido. A senha está incorreta. @@ -90,25 +81,20 @@ Insira um número de telefone válido Insira o código de 6 dígitos enviado para Reenviar código em %1$s - Confirmar seu número de telefone Verificando… Código incorreto. Tente novamente. Este número de telefone já foi usado muitas vezes - Ocorreu um problema na verificação do seu número de telefone Este código não é mais válido - Fazer login com o número de telefone - O número de telefone foi verificado automaticamente Reenviar código Confirmar número de telefone - Use a different phone number + Usar outro número de telefone Se você tocar em “%1$s”, um SMS poderá ser enviado e tarifas de mensagens e de dados serão cobradas. - Ao tocar em “%1$s”, você concorda com nossos %2$s e a %3$s. Um SMS poderá ser enviado e tarifas de mensagens e de dados poderão ser cobradas. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Erro de autenticação + Tentar novamente + Verificação adicional necessária. Conclua a autenticação de vários fatores. + A conta precisa ser vinculada. Tente um método de login diferente. + A autenticação foi cancelada. Tente novamente quando estiver pronto. Escolher método de autenticação @@ -173,25 +159,20 @@ Verificar Verifique %1$s para continuar. - Escolha um método de verificação - Adicione uma camada extra de segurança - SMS - Aplicativo autenticador - Este número de telefone está associado a outra conta - Verificação necessária - Escaneie o código QR com seu aplicativo autenticador - O aplicativo autenticador já está configurado - Tem certeza de que deseja remover este método? - Não foi possível remover o método - Método removido - Insira o código de verificação - Não foi possível atualizar o perfil - Perfil atualizado - Confirme sua identidade para continuar - Reautenticação necessária - Reautenticação bem-sucedida - Reautenticar A autenticação multifator está atualmente desativada + O e-mail ou a senha está incorreto + Esta sessão de verificação não é mais válida. Solicite um novo código. + A verificação por telefone não foi concluída. Tente novamente. + Essas credenciais pertencem a outra conta. + Esse número de telefone não está configurado para verificação nesta conta. + Sua sessão de login expirou. Faça login novamente para continuar. + Esse link não é mais válido. Solicite um novo. + Verifique seu endereço de e-mail antes de continuar. + Esse método de verificação já está configurado nesta conta. + Você atingiu o limite de métodos de verificação nesta conta. + Sua senha não atende aos requisitos. Tente outra. + A senha é muito longa. O tamanho máximo é %1$d. + Não encontramos uma chave de acesso para esta conta. Faça login de outra forma. diff --git a/auth/src/main/res/values-pt-rPT/strings.xml b/auth/src/main/res/values-pt-rPT/strings.xml index 8c7a2a173d..d4283dd99d 100755 --- a/auth/src/main/res/values-pt-rPT/strings.xml +++ b/auth/src/main/res/values-pt-rPT/strings.xml @@ -3,14 +3,13 @@ Iniciar sessão Continuar Ao continuar, indica que aceita os %1$s e a %2$s. - %1$s     %2$s Erro de rede. Verifique a sua ligação à Internet. Google Facebook Twitter GitHub Telemóvel - Email + Email Iniciar sessão com o Google Iniciar sessão com o Google Iniciar sessão com o Facebook @@ -31,7 +30,7 @@ Iniciar sessão com o Yahoo Iniciar sessão com o Yahoo Seguinte - Email + Email Número de telefone País Selecione um país @@ -41,30 +40,22 @@ Não pode deixar este campo em branco. O endereço de email não está correto Introduza o endereço de email para continuar - Introduza um nome próprio e um apelido. - A verificar a existência de contas… Inscrever-se Nome próprio e apelido Guardar - A realizar a inscrição… - A palavra-passe não é suficientemente forte. Utilize, pelo menos, %1$d caráter e uma combinação de letras e números A palavra-passe não é suficientemente forte. Utilize, pelo menos, %1$d caracteres e uma combinação de letras e números + A palavra-passe não é suficientemente forte. Utilize, pelo menos, %1$d caráter e uma combinação de letras e números A palavra-passe não é suficientemente forte. Utilize, pelo menos, %1$d de caracteres e uma combinação de letras e números A palavra-passe não é suficientemente forte. Utilize, pelo menos, %1$d caracteres e uma combinação de letras e números O registo da conta de email não foi bem-sucedido Termos de Utilização Política de Privacidade Já tem uma conta Bem-vindo de volta! Já possui uma conta\n - Já utilizou %1$s. Inicie sessão com %2$s para continuar. - Já utilizou %1$s para iniciar sessão. Introduza a palavra-passe da conta em questão. - Já utilizou o endereço %1$s. Pode associar a sua conta do %2$s ao endereço %1$s ao iniciar sessão com o link de email abaixo.\n\nPara que este fluxo associe com sucesso a sua conta do %2$s a este email, tem de abrir o link no mesmo dispositivo ou navegador. - A iniciar sessão… Está com problemas ao iniciar sessão? Recuperar a palavra-passe Verifique o seu email Utilize este endereço de email para obter instruções que explicam como repor a palavra-passe. Enviar Siga as instruções enviadas para %1$s para recuperar a palavra-passe. - A enviar… O endereço de email introduzido não corresponde a uma conta existente Ocorreu um erro desconhecido. Palavra-passe incorreta. @@ -90,25 +81,20 @@ Introduza um número de telefone válido Introduza o código de 6 dígitos que enviámos para Reenviar código em %1$s - Validar o número de telefone A validar… Código errado. Tente novamente. Este número de telefone foi utilizado demasiadas vezes - Ocorreu um problema ao validar o número de telefone Este código já não é válido - Iniciar sessão com o número de telefone - Número de telefone verificado automaticamente Reenviar código Validar número de telefone - Use a different phone number + Usar outro número de telefone Ao tocar em “%1$s”, pode gerar o envio de uma SMS. Podem aplicar-se tarifas de mensagens e dados. - Ao tocar em “%1$s”, indica que aceita os %2$s e a %3$s. Pode gerar o envio de uma SMS. Podem aplicar-se tarifas de dados e de mensagens. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Erro de autenticação + Tentar novamente + Verificação adicional necessária. Conclua a autenticação multifator. + A conta tem de ser associada. Experimente um método de início de sessão diferente. + A autenticação foi cancelada. Tente novamente quando estiver pronto. Escolher método de autenticação @@ -173,25 +159,20 @@ Verificar Verifique %1$s para continuar. - Escolha um método de verificação - Adicione uma camada extra de segurança - SMS - Aplicativo autenticador - Este número de telefone está associado a outra conta - Verificação necessária - Escaneie o código QR com seu aplicativo autenticador - O aplicativo autenticador já está configurado - Tem certeza de que deseja remover este método? - Não foi possível remover o método - Método removido - Insira o código de verificação - Não foi possível atualizar o perfil - Perfil atualizado - Confirme sua identidade para continuar - Reautenticação necessária - Reautenticação bem-sucedida - Reautenticar A autenticação multifator está atualmente desativada + O email ou a palavra-passe estão incorretos + Esta sessão de validação já não é válida. Solicite um novo código. + A validação do número de telefone não foi concluída. Tente novamente. + Estas credenciais pertencem a outra conta. + Este número de telefone não está configurado para validação nesta conta. + A sua sessão terminou. Inicie sessão novamente para continuar. + Este link já não é válido. Solicite um novo. + Valide o seu endereço de email antes de continuar. + Este método de validação já está configurado nesta conta. + Atingiu o limite de métodos de validação nesta conta. + A sua palavra-passe não cumpre os requisitos. Experimente outra. + A palavra-passe é demasiado longa. O comprimento máximo é %1$d. + Não encontrámos uma chave de acesso para esta conta. Inicie sessão de outra forma. diff --git a/auth/src/main/res/values-pt/strings.xml b/auth/src/main/res/values-pt/strings.xml index 5ee4a9aa8f..bb63be2f43 100755 --- a/auth/src/main/res/values-pt/strings.xml +++ b/auth/src/main/res/values-pt/strings.xml @@ -3,7 +3,6 @@ Fazer login Continuar Ao continuar, você concorda com nossos %1$s e a %2$s. - %1$s \u00A0 \u00A0 %2$s Erro de rede, confira sua conexão de Internet. Google Facebook @@ -41,30 +40,22 @@ Esse campo não pode ficar em branco. Endereço de e-mail inválido Digite seu endereço de e-mail para continuar - Digite um nome e um sobrenome. - Verificando contas existentes… Inscrever-se Nome e sobrenome Salvar - Concluindo sua inscrição… - A senha não é forte o suficiente. Use pelo menos %1$d caractere e combine letras e números. A senha não é forte o suficiente. Use pelo menos %1$d caracteres e combine letras e números. + A senha não é forte o suficiente. Use pelo menos %1$d caractere e combine letras e números. A senha não é forte o suficiente. Use pelo menos %1$d de caracteres e combine letras e números. A senha não é forte o suficiente. Use pelo menos %1$d caracteres e combine letras e números. Falha no registro da conta do e-mail Termos de Serviço Política de privacidade Você já tem uma conta É bom ver você novamente! Você já tem uma conta\n - Você já usou o e-mail %1$s. Faça login com o %2$s para continuar. - Você já usou o e-mail %1$s para fazer login. Insira a senha dessa conta. - Você já usou %1$s. Você pode conectar sua conta do %2$s com %1$s ao fazer login usando o link de e-mail abaixo.\n\nPara conectar sua conta do %2$s com este e-mail, é necessário abrir o link no mesmo dispositivo ou navegador. - Fazendo login… Problemas com login? Recuperar a senha Verifique seu e-mail Veja como redefinir sua senha nas instruções enviadas para este e-mail. Enviar Siga as instruções enviadas para %1$s para recuperar sua senha. - Enviando… O endereço de e-mail não corresponde a uma conta existente Ocorreu um erro desconhecido. A senha está incorreta. @@ -90,19 +81,14 @@ Insira um número de telefone válido Insira o código de 6 dígitos enviado para Reenviar código em %1$s - Confirmar seu número de telefone Verificando… Código incorreto. Tente novamente. Este número de telefone já foi usado muitas vezes - Ocorreu um problema na verificação do seu número de telefone Este código não é mais válido - Fazer login com o número de telefone - O número de telefone foi verificado automaticamente Reenviar código Confirmar número de telefone - Use a different phone number + Usar outro número de telefone Se você tocar em “%1$s”, um SMS poderá ser enviado e tarifas de mensagens e de dados serão cobradas. - Ao tocar em "%1$s", você concorda com nossos %2$s e a %3$s. Um SMS poderá ser enviado e tarifas de mensagens e de dados poderão ser cobradas. Erro de autenticação Tentar novamente Verificação adicional necessária. Conclua a autenticação de vários fatores. @@ -172,25 +158,20 @@ Verificar Verifique %1$s para continuar. - Escolha um método de verificação - Adicione uma camada extra de segurança - SMS - Aplicativo autenticador - Este número de telefone está associado a outra conta - Verificação necessária - Escaneie o código QR com seu aplicativo autenticador - O aplicativo autenticador já está configurado - Tem certeza de que deseja remover este método? - Não foi possível remover o método - Método removido - Insira o código de verificação - Não foi possível atualizar o perfil - Perfil atualizado - Confirme sua identidade para continuar - Reautenticação necessária - Reautenticação bem-sucedida - Reautenticar A autenticação multifator está atualmente desativada + O e-mail ou a senha está incorreto + Esta sessão de verificação não é mais válida. Solicite um novo código. + A verificação por telefone não foi concluída. Tente novamente. + Essas credenciais pertencem a outra conta. + Esse número de telefone não está configurado para verificação nesta conta. + Sua sessão de login expirou. Faça login novamente para continuar. + Esse link não é mais válido. Solicite um novo. + Verifique seu endereço de e-mail antes de continuar. + Esse método de verificação já está configurado nesta conta. + Você atingiu o limite de métodos de verificação nesta conta. + Sua senha não atende aos requisitos. Tente outra. + A senha é muito longa. O tamanho máximo é %1$d. + Não encontramos uma chave de acesso para esta conta. Faça login de outra forma. diff --git a/auth/src/main/res/values-ro/strings.xml b/auth/src/main/res/values-ro/strings.xml index 6b0d3a0486..4978f41233 100755 --- a/auth/src/main/res/values-ro/strings.xml +++ b/auth/src/main/res/values-ro/strings.xml @@ -3,7 +3,6 @@ Conectați-vă Continuați Dacă alegeți să continuați, sunteți de acord cu %1$s și cu %2$s. - %1$s \u00A0 \u00A0 %2$s Eroare de rețea, verificați conexiunea la internet. Google Facebook @@ -41,12 +40,9 @@ Câmpul este obligatoriu. Adresa de e-mail nu este corectă Introduceți adresa de e-mail pentru a continua - Introduceți numele și prenumele. - Se caută conturi existente… Înscrieți-vă Prenume și nume de familie Salvați - Se creează un cont… Parola nu este suficient de puternică. Folosiți cel puțin %1$d caracter și o combinație de litere și cifre. Parola nu este suficient de puternică. Folosiți cel puțin %1$d caractere și o combinație de litere și cifre. Parola nu este suficient de puternică. Folosiți cel puțin %1$d de caractere și o combinație de litere și cifre. Contul de e-mail nu a fost înregistrat Termeni și condițiile @@ -54,17 +50,12 @@ Aveți deja un cont Bine ați revenit! Aveți deja un cont\n - Ați folosit deja %1$s. Pentru a continua, conectați-vă cu %2$s. - Ați folosit deja %1$s pentru a vă conecta. Introduceți parola acestui cont. - Ați folosit deja %1$s. Vă puteți conecta contul %2$s la %1$s folosind linkul din e-mail, aflat mai jos.\n\nPentru ca acest flux să conecteze contul %2$s la această adresă de e-mail, trebuie să accesați linkul pe același dispozitiv sau browser. - Se conectează… Nu reușiți să vă conectați? Recuperați parola Verificați căsuța de e-mail Primiți instrucțiuni de resetare a parolei la această adresă de e-mail. Trimiteți Urmați instrucțiunile trimise la %1$s pentru a vă recupera parola. - Se trimite… Adresa de e-mail nu corespunde unui cont existent A apărut o eroare necunoscută. Parolă incorectă @@ -90,19 +81,14 @@ Introduceți un număr valid de telefon Introduceți codul din 6 cifre pe care l-am trimis la Retrimiteți codul în 0%1$s - Confirmați numărul de telefon Se verifică… Cod greșit. Încercați din nou. Acest număr de telefon a fost folosit de prea multe ori - A apărut o problemă la confirmarea numărului de telefon Codul nu mai este valid - Conectați-vă cu numărul de telefon - Numărul de telefon este verificat automat Retrimiteți codul Confirmați numărul de telefon - Use a different phone number + Folosiți alt număr de telefon Dacă atingeți „%1$s”, poate fi trimis un SMS. Se pot aplica tarife pentru mesaje și date. - Dacă atingeți „%1$s", sunteți de acord cu %2$s și cu %3$s. Poate fi trimis un SMS. Se pot aplica tarife pentru mesaje și date. Eroare de autentificare Încearcă din nou Este necesară verificarea suplimentară. Vă rugăm să completați autentificarea cu mai mulți factori. @@ -143,29 +129,11 @@ Identitate verificată. Reîncercați acțiunea. Gestionați autentificarea în doi pași Parolă incorectă - Alegeți o metodă de verificare - Adăugați un strat suplimentar de securitate - SMS - Aplicație de autentificare - Acest număr de telefon este asociat cu alt cont - Verificare necesară - Scanați codul QR cu aplicația de autentificare - Aplicația de autentificare este deja configurată - Sigur doriți să eliminați această metodă? - Nu se poate elimina metoda - Metodă eliminată - Introduceți codul de verificare - Nu se poate actualiza profilul - Profil actualizat - Confirmați-vă identitatea pentru a continua - Este necesară reautentificarea - Reautentificare reușită - Reautentificare Eliminare Retrimite e-mailul de verificare Cheie secretă Deconectare - Conectat ca + Conectat ca %1$s Omite Folosiți o altă metodă Cod de verificare @@ -175,4 +143,17 @@ Autentificarea cu mai mulți factori este dezactivată în prezent + Adresa de e-mail sau parola nu este corectă + Această sesiune de verificare nu mai este validă. Solicitați un cod nou. + Verificarea numărului de telefon nu a fost finalizată. Încercați din nou. + Aceste date de conectare aparțin altui cont. + Acest număr de telefon nu este configurat pentru verificare în acest cont. + Sesiunea de conectare a expirat. Conectați-vă din nou pentru a continua. + Acest link nu mai este valid. Solicitați unul nou. + Confirmați adresa de e-mail înainte de a continua. + Această metodă de verificare este deja configurată în acest cont. + Ați atins limita de metode de verificare pentru acest cont. + Parola nu îndeplinește cerințele. Încercați alta. + Parola este prea lungă. Lungimea maximă este %1$d. + Nu am găsit o cheie de acces pentru acest cont. Conectați-vă în alt mod. diff --git a/auth/src/main/res/values-ru/strings.xml b/auth/src/main/res/values-ru/strings.xml index ebf567549e..ea410b30a1 100755 --- a/auth/src/main/res/values-ru/strings.xml +++ b/auth/src/main/res/values-ru/strings.xml @@ -3,7 +3,6 @@ Войти Продолжить Продолжая, вы принимаете %1$s и %2$s. - %1$s \u00A0 \u00A0 %2$s Ошибка сети. Проверьте подключение к Интернету. Google Facebook @@ -41,12 +40,9 @@ Это поле должно быть заполнено. Неверный адрес электронной почты. Введите адрес электронной почты. - Введите имя и фамилию. - Проверка существующих аккаунтов… Регистрация Имя и фамилия Сохранить - Идет регистрация… Пароль должен состоять из букв и цифр и содержать не меньше %1$d символа. Пароль должен состоять из букв и цифр и содержать не меньше %1$d символов. Пароль должен состоять из букв и цифр и содержать не меньше %1$d символов. Пароль должен состоять из букв и цифр и содержать не меньше %1$d символа. Не удалось зарегистрировать аккаунт Условия использования @@ -54,17 +50,12 @@ У вас уже есть аккаунт Рады видеть вас снова! У вас уже есть аккаунт\n - Вы уже использовали адрес %1$s. Войдите через аккаунт %2$s. - Вы уже использовали адрес %1$s. Введите пароль для этого аккаунта. - Вы уже использовали адрес электронной почты %1$s для входа. Чтобы связать аккаунт сервиса \"%2$s\" с адресом %1$s, нажмите \"Войти\" и перейдите по ссылке, которую мы вам отправим.\n\nОбратите внимание, что открыть ссылку нужно в браузере или на устройстве, которые вы использовали. В противном случае связать аккаунт сервиса \"%2$s\" с указанным адресом электронной почты не получится. - Выполняется вход… Проблемы со входом в систему? Восстановление пароля Проверьте электронную почту На этот адрес будет отправлено письмо с инструкциями по сбросу пароля. Отправить На адрес %1$s отправлено письмо с инструкциями по сбросу пароля. - Отправка… Нет аккаунта с таким адресом электронной почты. Произошла неизвестная ошибка. Неправильный пароль. @@ -90,19 +81,14 @@ Введите действительный номер телефона. Введите 6-значный код, отправленный на номер Отправить код ещё раз можно будет через %1$s. - Подтвердить номер телефона Проверка… Неверный код. Повторите попытку. Этот номер телефона использовался слишком много раз. - Не удалось подтвердить номер телефона. Этот код уже неактивен. - Войти по номеру телефона - Номер телефона был подтвержден автоматически Отправить код ещё раз Подтвердить номер телефона - Use a different phone number + Использовать другой номер телефона Нажимая кнопку “%1$s”, вы соглашаетесь получить SMS. За его отправку и обмен данными может взиматься плата. - Нажимая кнопку "%1$s", вы принимаете %2$s и %3$s, а также соглашаетесь получить SMS. За его отправку и обмен данными может взиматься плата. Ошибка аутентификации Повторить Требуется дополнительная проверка. Пожалуйста, завершите многофакторную аутентификацию. @@ -143,29 +129,11 @@ Личность подтверждена. Повторите действие. Управление двухфакторной аутентификацией Неверный пароль - Выберите способ подтверждения - Добавьте дополнительный уровень безопасности - SMS - Приложение для аутентификации - Этот номер телефона уже привязан к другому аккаунту - Требуется подтверждение - Отсканируйте QR-код приложением для аутентификации - Приложение для аутентификации уже настроено - Вы уверены, что хотите удалить этот способ? - Не удалось удалить способ - Способ удален - Введите код подтверждения - Не удалось обновить профиль - Профиль обновлен - Подтвердите свою личность, чтобы продолжить - Требуется повторная аутентификация - Повторная аутентификация выполнена - Повторная аутентификация Удалить Отправить письмо подтверждения повторно Секретный ключ Выйти - Вы вошли как + Вы вошли как %1$s Пропустить Использовать другой способ Код подтверждения @@ -175,4 +143,17 @@ Многофакторная аутентификация в настоящее время отключена + Неправильный адрес электронной почты или пароль + Этот сеанс проверки больше не действителен. Запросите новый код. + Не удалось подтвердить номер телефона. Повторите попытку. + Эти учётные данные принадлежат другому аккаунту. + Этот номер телефона не настроен для подтверждения в этом аккаунте. + Сеанс входа истёк. Войдите ещё раз, чтобы продолжить. + Эта ссылка больше не действительна. Запросите новую. + Подтвердите адрес электронной почты, прежде чем продолжить. + Этот способ подтверждения уже настроен в этом аккаунте. + Достигнут лимит способов подтверждения для этого аккаунта. + Пароль не соответствует требованиям. Попробуйте другой. + Пароль слишком длинный. Максимальная длина – %1$d. + Не удалось найти ключ доступа для этого аккаунта. Войдите другим способом. diff --git a/auth/src/main/res/values-sk/strings.xml b/auth/src/main/res/values-sk/strings.xml index 0786594fd9..fea8932952 100755 --- a/auth/src/main/res/values-sk/strings.xml +++ b/auth/src/main/res/values-sk/strings.xml @@ -3,7 +3,6 @@ Prihlásiť sa Pokračovať Pokračovaním vyjadrujete súhlas s dokumentmi %1$s a %2$s. - %1$s \u00A0 \u00A0 %2$s Chyba siete. Skontrolujte internetové pripojenie. Google Facebook @@ -41,12 +40,9 @@ Toto pole nesmie byť prázdne. Táto e-mailová adresa nie je správna Zadajte svoju e-mailovú adresu, aby ste mohli pokračovať - Zadajte meno a priezvisko. - Kontrolujú sa existujúce účty… Registrovať sa Krstné meno a priezvisko Uložiť - Prebieha registrácia… Heslo nie je dostatočne silné. Použite aspoň %1$d znak a kombináciu písmen a čísel Heslo nie je dostatočne silné. Použite aspoň %1$d znaky a kombináciu písmen a čísel Heslo nie je dostatočne silné. Použite aspoň %1$d znaku a kombináciu písmen a čísel Heslo nie je dostatočne silné. Použite aspoň %1$d znakov a kombináciu písmen a čísel Registrácia e-mailového účtu nebola úspešná Zmluvné podmienky @@ -54,17 +50,12 @@ Už máte účet Vitajte späť! Už máte účet\n - Už ste použili adresu %1$s. Ak chcete pokračovať, prihláste sa pomocou účtu %2$s. - Na prihlásenie ste už použili adresu %1$s. Zadajte svoje heslo pre daný účet. - E‑mailovú adresu %1$s ste už použili. Prihlásením sa pomocou odkazu nižšie môžete svoj účet %2$s prepojiť s e-mailovou adresou %1$s.\n\nAby tento proces mohol úspešne prepojiť váš účet %2$s s týmto e‑mailom, musíte odkaz otvoriť v tom istom zariadení alebo prehliadači. - Prihlasovanie… Máte problémy s prihlásením? Obnovenie hesla Skontrolujte si e-mail Nechajte si na tento e-mail poslať pokyny s vysvetlením, ako obnoviť svoje heslo. Odoslať Obnovte si heslo podľa pokynov odoslaných na adresu %1$s. - Odosiela sa… Táto e-mailová adresa sa nezhoduje s existujúcim účtom Vyskytla sa neznáma chyba. Nesprávne heslo. @@ -90,19 +81,14 @@ Zadajte platné telefónne číslo Zadajte 6-ciferný kód, ktorý sme odoslali na adresu Kód sa znova odošle o %1$s - Overiť telefónne číslo Overuje sa… Nesprávny kód. Skúste to znova. Toto telefónne číslo bolo použité príliš veľakrát - Vyskytol sa problém s overením vášho telefónneho čísla Tento kód už nie je platný - Prihláste sa telefónnym číslom - Telefónne číslo bolo automaticky overené Znova odoslať kód Overiť telefónne číslo - Use a different phone number + Použiť iné telefónne číslo Klepnutím na tlačidlo %1$s možno odoslať SMS. Môžu sa účtovať poplatky za správy a dáta. - Klepnutím na tlačidlo %1$s vyjadrujete súhlas s dokumentmi %2$s a %3$s. Môže byť odoslaná SMS a môžu sa účtovať poplatky za správy a dáta. Chyba overenia Skúsiť znova Vyžaduje sa dodatočné overenie. Dokončite prosím viacfaktorové overenie. @@ -143,29 +129,11 @@ Identita overená. Skúste akciu znova. Spravovať dvojfaktorové overenie Nesprávne heslo - Vyberte metódu overenia - Pridajte ďalšiu vrstvu zabezpečenia - SMS - Overovacia aplikácia - Toto telefónne číslo je spojené s iným účtom - Vyžaduje sa overenie - Naskenujte QR kód pomocou overovacej aplikácie - Overovacia aplikácia je už nastavená - Naozaj chcete odstrániť túto metódu? - Metódu sa nepodarilo odstrániť - Metóda odstránená - Zadajte overovací kód - Profil sa nepodarilo aktualizovať - Profil aktualizovaný - Potvrďte svoju identitu a pokračujte - Vyžaduje sa opätovné overenie - Opätovné overenie bolo úspešné - Znova overiť Odstrániť Znova poslať overovací e-mail Tajný kľúč Odhlásiť sa - Prihlásený ako + Prihlásený ako %1$s Preskočiť Použiť inú metódu Overovací kód @@ -175,4 +143,17 @@ Viacfaktorové overovanie je momentálne zakázané + Tento e-mail alebo heslo nie je správne + Táto overovacia relácia už nie je platná. Vyžiadajte si nový kód. + Overenie telefónneho čísla sa nedokončilo. Skúste to znova. + Tieto prihlasovacie údaje patria inému účtu. + Toto telefónne číslo nie je v tomto účte nastavené na overovanie. + Vaša prihlasovacia relácia vypršala. Ak chcete pokračovať, prihláste sa znova. + Tento odkaz už nie je platný. Vyžiadajte si nový. + Skôr než budete pokračovať, overte svoju e-mailovú adresu. + Táto metóda overenia je v tomto účte už nastavená. + Dosiahli ste limit metód overenia pre tento účet. + Vaše heslo nespĺňa požiadavky. Skúste iné. + Heslo je príliš dlhé. Maximálna dĺžka je %1$d. + Pre tento účet sa nenašiel prístupový kľúč. Prihláste sa iným spôsobom. diff --git a/auth/src/main/res/values-sl/strings.xml b/auth/src/main/res/values-sl/strings.xml index 3375bd8c01..6daec7b7f6 100755 --- a/auth/src/main/res/values-sl/strings.xml +++ b/auth/src/main/res/values-sl/strings.xml @@ -3,7 +3,6 @@ Prijava Naprej Z nadaljevanjem potrjujete, da se strinjate z dokumentoma %1$s in %2$s. - %1$s \u00A0 \u00A0 %2$s Napaka v omrežju. Preverite internetno povezavo. Google Facebook @@ -41,12 +40,9 @@ To polje ne sme biti prazno. E-poštni naslov ni pravilen Če želite nadaljevati, vnesite svoj e-poštni naslov - Vnesite ime in priimek. - Preverjanje, ali že obstajajo računi… Registrirajte se Ime in priimek Shrani - Registriranje… Geslo ni dovolj zapleteno. Uporabite vsaj %1$d znak in kombinacijo črk ter številk. Geslo ni dovolj zapleteno. Uporabite vsaj %1$d znaka in kombinacijo črk ter številk. Geslo ni dovolj zapleteno. Uporabite vsaj %1$d znake in kombinacijo črk ter številk. Geslo ni dovolj zapleteno. Uporabite vsaj %1$d znakov in kombinacijo črk ter številk. Registracija e-poštnega računa ni bila uspešna pogoji storitve @@ -54,17 +50,12 @@ Račun že imate Dobrodošli nazaj! Račun že imate\n - E-poštni naslov %1$s ste že uporabili. Če želite nadaljevati, se prijavite z računom za %2$s. - E-poštni naslov %1$s ste že uporabili za prijavo. Vnesite geslo za ta račun. - %1$s ste že uporabili. Račun za %2$s lahko povežete z e-poštnim naslovom %1$s tako, da se prijavite na spodnji povezavi.\n\nČe želite račun za %2$s uspešno povezati s tem e-poštnim naslovom, morate povezavi odpreti v isti napravi ali brskalniku. - Prijavljanje… Ali imate težave pri prijavi? Obnovitev gesla Preverite e-pošto Na ta e-poštni naslov vam bomo poslali navodila za ponastavitev gesla. Pošlji Če želite obnoviti geslo, upoštevajte navodila, poslana na naslov %1$s. - Pošiljanje… E-poštni naslov se ne ujema z obstoječim računom Neznana napaka. Napačno geslo. @@ -90,25 +81,20 @@ Vnesite veljavno telefonsko številko Vnesite 6-mestno kodo, ki smo jo poslali na številko Ponovno pošlji kodo čez %1$s - Preverjanje telefonske številke Preverjanje… Napačna koda. Poskusite znova. Telefonska številka je bila uporabljena prevečkrat - Pri preverjanju telefonske številke je prišlo do težave Koda ni več veljavna - Prijava s telefonsko številko - Telefonska številka je bila samodejno preverjena Ponovno pošlji kodo Preverjanje telefonske številke - Use a different phone number + Uporabi drugo telefonsko številko Če se dotaknete možnosti »%1$s«, bo morda poslano sporočilo SMS. Pošiljanje sporočila in prenos podatkov boste morda morali plačati. - Če se dotaknete možnosti »%1$s«, potrjujete, da se strinjate z dokumentoma %2$s in %3$s. Morda bo poslano sporočilo SMS. Pošiljanje sporočila in prenos podatkov boste morda morali plačati. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Napaka pri preverjanju pristnosti + Poskusite znova + Potrebno je dodatno preverjanje. Dokončajte večstopenjsko preverjanje pristnosti. + Račun je treba povezati. Poskusite z drugim načinom prijave. + Preverjanje pristnosti je bilo preklicano. Ko boste pripravljeni, poskusite znova. Izberite način preverjanja pristnosti @@ -144,29 +130,11 @@ Identiteta preverjena. Poskusite znova izvesti dejanje. Upravljanje dvofaktorskega preverjanja Napačno geslo - Izberite način preverjanja - Dodajte dodatno raven varnosti - SMS - Aplikacija za preverjanje pristnosti - Ta telefonska številka je povezana z drugim računom - Preverjanje je obvezno - Skenirajte QR-kodo z aplikacijo za preverjanje pristnosti - Aplikacija za preverjanje pristnosti je že nastavljena - Ali ste prepričani, da želite odstraniti to metodo? - Metode ni mogoče odstraniti - Metoda odstranjena - Vnesite kodo za preverjanje - Profila ni mogoče posodobiti - Profil posodobljen - Potrdite svojo identiteto za nadaljevanje - Zahtevano je ponovno preverjanje pristnosti - Ponovno preverjanje pristnosti uspešno - Ponovno preveri pristnost Odstrani Znova pošlji e-sporočilo za preverjanje Skrivni ključ Odjava - Prijavljen kot + Prijavljen kot %1$s Preskoči Uporabi drugo metodo Koda za preverjanje @@ -176,4 +144,17 @@ Večfaktorska avtentikacija je trenutno onemogočena + E-poštni naslov ali geslo ni pravilno + Ta seja preverjanja ni več veljavna. Zahtevajte novo kodo. + Preverjanje telefonske številke ni bilo dokončano. Poskusite znova. + Ti poverilnici pripadata drugemu računu. + Ta telefonska številka v tem računu ni nastavljena za preverjanje. + Vaša prijavna seja je potekla. Za nadaljevanje se znova prijavite. + Ta povezava ni več veljavna. Zahtevajte novo. + Preden nadaljujete, preverite svoj e-poštni naslov. + Ta način preverjanja je v tem računu že nastavljen. + Dosegli ste omejitev števila načinov preverjanja v tem računu. + Vaše geslo ne izpolnjuje zahtev. Poskusite z drugim. + Geslo je predolgo. Največja dolžina je %1$d. + Za ta račun nismo našli ključa za dostop. Prijavite se na drug način. diff --git a/auth/src/main/res/values-sr/strings.xml b/auth/src/main/res/values-sr/strings.xml index 7266b80e4f..21103d53e8 100755 --- a/auth/src/main/res/values-sr/strings.xml +++ b/auth/src/main/res/values-sr/strings.xml @@ -3,7 +3,6 @@ Пријави ме Настави Ако наставите, потврђујете да прихватате документе %1$s и %2$s. - %1$s \u00A0 \u00A0 %2$s Дошло је до мрежне грешке, проверите интернет везу. Google Facebook @@ -41,12 +40,9 @@ Ово поље не може да буде празно. Имејл адреса није тачна Унесите имејл адресу да бисте наставили - Унесите име и презиме. - Траже се постојећи налози… Региструјте се Име и презиме Сачувај - Регистрација је у току… Лозинка није довољно јака. Користите бар %1$d знак, уз комбинацију бројева и слова Лозинка није довољно јака. Користите бар %1$d знака, уз комбинацију бројева и слова Лозинка није довољно јака. Користите бар %1$d знакова, уз комбинацију бројева и слова Регистрација налога преко имејла није успела Услови коришћења услуге @@ -54,17 +50,12 @@ Већ имате налог Добро дошли поново! Већ имате налог\n - Већ сте користили адресу %1$s. Пријавите се преко %2$s-а да бисте наставили. - Већ сте користили адресу %1$s за пријављивање. Унесите лозинку за тај налог. - Већ сте употребили %1$s. Можете да повежете %2$s налог са имејлом %1$s тако што ћете се пријавити путем линка у имејлу испод.\n\nДа би се тим путем повезао %2$s налог са овим имејлом, треба да отворите линк на истом уређају или из истог прегледача. - Пријављивање… Имате проблема при пријављивању? Повратите лозинку Проверите имејл На овај имејл ћете добити упутства како да ресетујете лозинку. Пошаљи Пратите упутства која су вам послана на %1$s да бисте повратили лозинку. - Шаље се… Имејл адреса не одговара ни једном налогу Дошло је до непознате грешке. Лозинка није тачна. @@ -90,25 +81,20 @@ Унесите важећи број телефона Унесите 6-цифрени кôд који смо послали на Кôд ће бити поново послат за %1$s - Верификујте број телефона Верификује се… Кôд није тачан. Пробајте поново. Овај број телефона је употребљен превише пута - Дошло је до проблема при верификацији броја телефона Тај кôд више не важи - Пријавите се помоћу броја телефона - Број телефона је аутоматски верификован Поново пошаљи кôд Верификуј број телефона - Use a different phone number + Користи други број телефона Ако додирнете „%1$s“, можда ћете послати SMS. Могу да вам буду наплаћени трошкови слања поруке и преноса података. - Ако додирнете „%1$s“, потврђујете да прихватате документе %2$s и %3$s. Можда ћете послати SMS. Могу да вам буду наплаћени трошкови слања поруке и преноса података. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Грешка при аутентификацији + Пробајте поново + Потребна је додатна верификација. Довршите аутентификацију са више фактора. + Налог треба да се повеже. Пробајте други метод пријављивања. + Аутентификација је отказана. Пробајте поново када будете спремни. Изаберите метод аутентификације @@ -144,29 +130,11 @@ Идентитет је верификован. Покушајте поново своју радњу. Управљај двофакторском аутентификацијом Нетачна лозинка - Изаберите метод верификације - Додајте додатни слој безбедности - СМС - Апликација за аутентификацију - Овај број телефона је повезан са другим налогом - Потребна је верификација - Скенирајте QR код апликацијом за аутентификацију - Апликација за аутентификацију је већ подешена - Да ли сте сигурни да желите да уклоните овај метод? - Не може се уклонити метод - Метод је уклоњен - Унесите код за верификацију - Не може се ажурирати профил - Профил је ажуриран - Потврдите свој идентитет да бисте наставили - Потребна је поновна аутентификација - Поновна аутентификација је успешна - Поновна аутентификација Уклони Поново пошаљи имејл за верификацију Тајни кључ Одјава - Пријављен као + Пријављен као %1$s Прескочи Користи други метод Код за верификацију @@ -176,4 +144,17 @@ Вишефакторска аутентификација је тренутно онемогућена + Та имејл адреса или лозинка није тачна + Та сесија верификације више не важи. Затражите нови кôд. + Верификација броја телефона није довршена. Пробајте поново. + Ти акредитиви припадају другом налогу. + Тај број телефона није подешен за верификацију на овом налогу. + Сесија пријављивања је истекла. Пријавите се поново да бисте наставили. + Та веза више не важи. Затражите нову. + Верификујте имејл адресу пре него што наставите. + Тај метод верификације је већ подешен на овом налогу. + Достигли сте ограничење броја метода верификације на овом налогу. + Лозинка не испуњава услове. Пробајте другу. + Лозинка је предугачка. Максимална дужина је %1$d. + Нисмо пронашли приступни кључ за овај налог. Пријавите се на други начин. diff --git a/auth/src/main/res/values-sv/strings.xml b/auth/src/main/res/values-sv/strings.xml index 2964dbcc79..140a7d333f 100755 --- a/auth/src/main/res/values-sv/strings.xml +++ b/auth/src/main/res/values-sv/strings.xml @@ -3,7 +3,6 @@ Logga in Fortsätt Genom att fortsätta godkänner du våra %1$s och vår %2$s. - %1$s \u00A0 \u00A0 %2$s Nätverksfel, kontrollera din internetanslutning. Google Facebook @@ -41,12 +40,9 @@ Du måste fylla i det här fältet. E-postadressen är ogiltig Ange din e-postadress om du vill fortsätta - Ange för- och efternamn. - Söker efter befintliga konton… Registrera dig För- och efternamn Spara - Registrerar… Lösenordet är inte tillräckligt starkt. Använd minst %1$d tecken och en blandning av bokstäver och siffror. Lösenordet är inte tillräckligt starkt. Använd minst %1$d tecken och en blandning av bokstäver och siffror. Det gick inte att registrera e-postkontot användarvillkoren @@ -54,17 +50,12 @@ Du har redan ett konto Välkommen tillbaka! Du har redan ett konto\n - Du har redan använt %1$s. Logga in med %2$s om du vill fortsätta. - Du har redan loggat in med %1$s. Ange lösenordet för det kontot. - Du har redan använt %1$s. Du kan ansluta %2$s-kontot med %1$s genom att logga in via länken nedan.\n\nDu måste öppna länken på samma enhet eller i samma webbläsare för att %2$s-kontot ska anslutas med den här e-postadressen. - Loggar in… Har du problem med att logga in? Återställa lösenord Kolla dina e-postmeddelanden Få ett e-postmeddelande med anvisningar för hur du återställer lösenordet. Skicka Följ anvisningarna som har skickats till %1$s för att återställa ditt lösenord. - Skickar… E-postadressen matchar inget befintligt konto Ett okänt fel uppstod. Felaktigt lösenord. @@ -90,19 +81,14 @@ Ange ett giltigt telefonnummer Ange den sexsiffriga koden vi skickade till Skicka kod igen om %1$s - Verifiera ditt telefonnummer Verifierar … Fel kod. Försök igen. Det här telefonnumret har använts för många gånger - Det gick inte att verifiera telefonnumret Koden är inte längre giltig - Logga in med telefonnummer - Telefonnumret verifierades automatiskt Skicka koden igen Verifiera telefonnummer - Use a different phone number + Använd ett annat telefonnummer Genom att trycka på %1$s skickas ett sms. Meddelande- och dataavgifter kan tillkomma. - Genom att trycka på %1$s godkänner du våra %2$s och vår %3$s. Ett sms kan skickas. Meddelande- och dataavgifter kan tillkomma. Autentiseringsfel Försök igen Ytterligare verifiering krävs. Vänligen slutför multifaktorautentisering. @@ -143,29 +129,11 @@ Identitet verifierad. Försök din åtgärd igen. Hantera tvåfaktorsautentisering Felaktigt lösenord - Välj en verifieringsmetod - Lägg till ett extra säkerhetslager - SMS - Autentiseringsapp - Detta telefonnummer är kopplat till ett annat konto - Verifiering krävs - Skanna QR-koden med din autentiseringsapp - Autentiseringsapp är redan konfigurerad - Är du säker på att du vill ta bort denna metod? - Kan inte ta bort metoden - Metod borttagen - Ange verifieringskod - Kan inte uppdatera profil - Profil uppdaterad - Bekräfta din identitet för att fortsätta - Omautentisering krävs - Omautentisering lyckades - Omautentisera Ta bort Skicka verifieringsmail igen Hemlig nyckel Logga ut - Inloggad som + Inloggad som %1$s Hoppa över Använd en annan metod Verifieringskod @@ -175,4 +143,17 @@ Multifaktorautentisering är för närvarande inaktiverad + E-postadressen eller lösenordet är felaktigt + Verifieringssessionen är inte längre giltig. Begär en ny kod. + Telefonverifieringen slutfördes inte. Försök igen. + De här inloggningsuppgifterna tillhör ett annat konto. + Det telefonnumret är inte konfigurerat för verifiering på det här kontot. + Din inloggningssession har upphört att gälla. Logga in igen för att fortsätta. + Länken är inte längre giltig. Begär en ny. + Verifiera din e-postadress innan du fortsätter. + Den verifieringsmetoden är redan konfigurerad på det här kontot. + Du har nått gränsen för antalet verifieringsmetoder på det här kontot. + Ditt lösenord uppfyller inte kraven. Prova ett annat. + Lösenordet är för långt. Den maximala längden är %1$d. + Vi hittade ingen nyckel för det här kontot. Logga in på ett annat sätt. diff --git a/auth/src/main/res/values-ta/strings.xml b/auth/src/main/res/values-ta/strings.xml index f268d402b8..2ab29e9e18 100755 --- a/auth/src/main/res/values-ta/strings.xml +++ b/auth/src/main/res/values-ta/strings.xml @@ -3,7 +3,6 @@ உள்நுழைக தொடர்க தொடர்வதன் மூலம், எங்கள் %1$s மற்றும் %2$sஐ ஏற்பதாகக் குறிப்பிடுகிறீர்கள். - %1$s \u00A0 \u00A0 %2$s நெட்வொர்க் பிழை, உங்கள் இணைய இணைப்பைச் சரிபார்க்கவும். Google Facebook @@ -41,12 +40,9 @@ இதை காலியாக விடக்கூடாது. மின்னஞ்சல் முகவரி தவறாக உள்ளது தொடர, மின்னஞ்சல் முகவரியை உள்ளிடவும் - பெயரின் முற்பகுதியையும் பிற்பகுதியையும் உள்ளிடவும். - ஏற்கனவே உள்ள கணக்குகளைச் சரிபார்க்கிறது… பதிவு செய்க பெயரின் முற்பகுதி மற்றும் பிற்பகுதி சேமி - பதிவு செய்கிறது… கடவுச்சொல் வலுவானதாக இல்லை. குறைந்தது %1$d எழுத்துக்குறியும், எழுத்துகளும் எண்களும் கலந்த கலவையும் இருக்க வேண்டும் கடவுச்சொல் வலுவானதாக இல்லை. குறைந்தது %1$d எழுத்துக்குறிகளும், எழுத்துகளும் எண்களும் கலந்த கலவையும் இருக்க வேண்டும் மின்னஞ்சல் கணக்கைப் பதிவு செய்ய முடியவில்லை சேவை விதிமுறைகள் @@ -54,17 +50,12 @@ ஏற்கனவே உங்களிடம் கணக்கு உள்ளது மீண்டும் வரவேற்கிறோம்! உங்களிடம் ஏற்கனவே ஒரு கணக்கு உள்ளது\n - ஏற்கனவே %1$sஐப் பயன்படுத்தியுள்ளீர்கள். தொடர, %2$s மூலம் உள்நுழையவும். - ஏற்கனவே %1$sஐப் பயன்படுத்தி உள்நுழைந்திருக்கிறீர்கள். அந்தக் கணக்கிற்கான கடவுச்சொல்லை உள்ளிடவும். - நீங்கள் ஏற்கனவே %1$s என்ற மின்னஞ்சல் முகவரியைப் பயன்படுத்தியுள்ளீர்கள். கீழேயுள்ள இணைப்பில் உள்நுழைவதன் மூலம் உங்கள் %2$s கணக்கை %1$s உடன் இணைக்கலாம்.\n\nஇந்த மின்னஞ்சலுடன் %2$s கணக்கை இணைப்பதற்கான இந்தச் செயல்முறையை நிறைவு செய்ய, நீங்கள் அதே சாதனம் அல்லது உலாவியில் இணைப்பைத் திறக்க வேண்டும். - உள்நுழைகிறீர்கள்… உள்நுழைவதில் சிக்கலா? கடவுச்சொல்லை மீட்டெடு உங்கள் மின்னஞ்சலைப் பார்க்கவும் கடவுச்சொல்லை மீட்டமைப்பது தொடர்பான வழிமுறைகளை இந்த மின்னஞ்சலில் பார்க்கவும். அனுப்பு கடவுச்சொல்லை மீட்டெடுக்க, %1$sக்கு அனுப்பிய வழிமுறைகளைப் பின்பற்றவும். - அனுப்புகிறது… மின்னஞ்சல் முகவரி தற்போதைய கணக்குடன் பொருந்தவில்லை அறியப்படாத பிழை ஏற்பட்டது. தவறான கடவுச்சொல். @@ -90,25 +81,20 @@ சரியான ஃபோன் எண்ணை உள்ளிடவும் இந்த எண்ணுக்கு நாங்கள் அனுப்பிய 6 இலக்கக் குறியீட்டை உள்ளிடவும்: குறியீட்டை %1$s நேரத்தில் மீண்டும் அனுப்பவும் - ஃபோன் எண்ணைச் சரிபார் சரிபார்க்கிறது… தவறான குறியீடு. மீண்டும் முயலவும். இந்த ஃபோன் எண் பலமுறை பயன்படுத்தப்பட்டுள்ளது - உங்கள் ஃபோன் எண்ணைச் சரிபார்ப்பதில் சிக்கல் ஏற்பட்டது இந்தக் குறியீடு இனி செல்லுபடியாகாது - ஃபோன் எண் மூலம் உள்நுழையவும் - ஃபோன் எண் தானாகவே சரிபார்க்கப்பட்டது குறியீட்டை மீண்டும் அனுப்பு ஃபோன் எண்ணைச் சரிபார் - Use a different phone number + வேறு ஃபோன் எண்ணைப் பயன்படுத்து “%1$s” என்பதைத் தட்டுவதன் மூலம், SMS அனுப்பப்படலாம். செய்தி மற்றும் தரவுக் கட்டணங்கள் விதிக்கப்படலாம். - “%1$s” என்பதைத் தட்டுவதன் மூலம், எங்கள் %2$s மற்றும் %3$sஐ ஏற்பதாகக் குறிப்பிடுகிறீர்கள். SMS அனுப்பப்படலாம். செய்தி மற்றும் தரவுக் கட்டணங்கள் விதிக்கப்படலாம். - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + அங்கீகாரப் பிழை + மீண்டும் முயலவும் + கூடுதல் சரிபார்ப்பு தேவை. பல்காரணி அங்கீகாரத்தை நிறைவுசெய்யவும். + கணக்கை இணைக்க வேண்டும். வேறு உள்நுழைவு முறையை முயலவும். + அங்கீகாரம் ரத்துசெய்யப்பட்டது. தயாரானதும் மீண்டும் முயலவும். அங்கீகார முறையைத் தேர்ந்தெடுக்கவும் @@ -144,29 +130,11 @@ அடையாளம் சரிபார்க்கப்பட்டது. உங்கள் செயலை மீண்டும் முயற்சிக்கவும். இரு-காரணி அங்கீகாரத்தை நிர்வகி தவறான கடவுச்சொல் - சரிபார்ப்பு முறையைத் தேர்ந்தெடுக்கவும் - கூடுதல் பாதுகாப்பு அடுக்கைச் சேர்க்கவும் - SMS - அங்கீகார பயன்பாடு - இந்த தொலைபேசி எண் மற்றொரு கணக்குடன் தொடர்புடையது - சரிபார்ப்பு தேவை - உங்கள் அங்கீகார பயன்பாட்டில் QR குறியீட்டை ஸ்கேன் செய்யவும் - அங்கீகார பயன்பாடு ஏற்கனவே அமைக்கப்பட்டுள்ளது - இந்த முறையை அகற்ற விரும்புகிறீர்களா? - முறையை அகற்ற முடியவில்லை - முறை அகற்றப்பட்டது - சரிபார்ப்பு குறியீட்டை உள்ளிடவும் - சுயவிவரத்தைப் புதுப்பிக்க முடியவில்லை - சுயவிவரம் புதுப்பிக்கப்பட்டது - தொடர உங்கள் அடையாளத்தை உறுதிப்படுத்தவும் - மீண்டும் அங்கீகாரம் தேவை - மீண்டும் அங்கீகாரம் வெற்றிகரமாக - மீண்டும் அங்கீகரி அகற்று சரிபார்ப்பு மின்னஞ்சலை மீண்டும் அனுப்பு ரகசிய திறவுகோல் வெளியேறு - இவராக உள்நுழைந்துள்ளது + %1$sஆக உள்நுழைந்துள்ளது தவிர் வேறு முறையைப் பயன்படுத்து சரிபார்ப்பு குறியீடு @@ -176,4 +144,17 @@ பல-காரணி அங்கீகாரம் தற்போது முடக்கப்பட்டுள்ளது + அந்த மின்னஞ்சல் முகவரியோ கடவுச்சொல்லோ சரியில்லை + அந்தச் சரிபார்ப்பு அமர்வு இனி செல்லுபடியாகாது. புதிய குறியீட்டைக் கோரவும். + ஃபோன் சரிபார்ப்பு நிறைவடையவில்லை. மீண்டும் முயலவும். + அந்த அனுமதிச் சான்றுகள் வேறொரு கணக்கிற்கு உரியவை. + அந்த ஃபோன் எண் இந்தக் கணக்கில் சரிபார்ப்பிற்காக அமைக்கப்படவில்லை. + உங்கள் உள்நுழைவு அமர்வு காலாவதியானது. தொடர, மீண்டும் உள்நுழையவும். + அந்த இணைப்பு இனி செல்லுபடியாகாது. புதியதொன்றைக் கோரவும். + தொடர்வதற்கு முன் உங்கள் மின்னஞ்சல் முகவரியைச் சரிபார்க்கவும். + அந்தச் சரிபார்ப்பு முறை இந்தக் கணக்கில் ஏற்கெனவே அமைக்கப்பட்டுள்ளது. + இந்தக் கணக்கிற்கான சரிபார்ப்பு முறைகளின் வரம்பை எட்டிவிட்டீர்கள். + உங்கள் கடவுச்சொல் தேவைகளைப் பூர்த்தி செய்யவில்லை. வேறொன்றை முயலவும். + கடவுச்சொல் மிக நீளமாக உள்ளது. அதிகபட்ச நீளம் %1$d ஆகும். + இந்தக் கணக்கிற்கான கடவுச்சாவி கிடைக்கவில்லை. வேறு முறையில் உள்நுழையவும். diff --git a/auth/src/main/res/values-th/strings.xml b/auth/src/main/res/values-th/strings.xml index ad9f71a6b4..1148a7bf44 100755 --- a/auth/src/main/res/values-th/strings.xml +++ b/auth/src/main/res/values-th/strings.xml @@ -3,7 +3,6 @@ ลงชื่อเข้าใช้ ต่อไป การดำเนินการต่อแสดงว่าคุณยอมรับ %1$s และ %2$s - %1$s \u00A0 \u00A0 %2$s เกิดข้อผิดพลาดของเครือข่าย โปรดตรวจสอบการเชื่อมต่ออินเทอร์เน็ต Google Facebook @@ -41,12 +40,9 @@ ต้องป้อนข้อมูลในช่องนี้ ที่อยู่อีเมลนี้ไม่ถูกต้อง ป้อนที่อยู่อีเมลของคุณเพื่อดำเนินการต่อ - โปรดป้อนชื่อและนามสกุล - กำลังตรวจสอบบัญชีที่มีอยู่… ลงชื่อสมัครใช้ ชื่อและนามสกุล บันทึก - กำลังลงชื่อสมัครใช้… รหัสผ่านไม่รัดกุมมากพอ ใช้อักขระอย่างน้อย %1$d ตัวและต้องเป็นตัวอักษรปนกับตัวเลข รหัสผ่านไม่รัดกุมมากพอ ใช้อักขระอย่างน้อย %1$d ตัวและต้องเป็นตัวอักษรปนกับตัวเลข การลงทะเบียนบัญชีอีเมลไม่สำเร็จ ข้อกำหนดในการให้บริการ @@ -54,17 +50,12 @@ คุณมีบัญชีอยู่แล้ว ยินดีต้อนรับอีกครั้ง! คุณมีบัญชีแล้ว\n - คุณใช้ %1$s อยู่แล้ว ลงชื่อเข้าใช้ด้วย %2$s เพื่อดำเนินการต่อ - คุณใช้ %1$s ในการลงชื่อเข้าใช้อยู่แล้ว โปรดป้อนรหัสผ่านสำหรับบัญชีดังกล่าว - คุณใช้ %1$s แล้ว คุณจะเชื่อมต่อบัญชี %2$s กับ %1$s ได้โดยลงชื่อเข้าใช้ด้วยลิงก์อีเมลด้านล่าง\n\nหากต้องการเชื่อมต่อบัญชี %2$s กับอีเมลนี้ให้สำเร็จ คุณต้องเปิดลิงก์บนอุปกรณ์หรือเบราว์เซอร์เดียวกัน - กำลังลงชื่อเข้าใช้… หากพบปัญหาในการลงชื่อเช้าใช้ กู้คืนรหัสผ่าน ตรวจสอบอีเมลของคุณ ดูวิธีการที่ส่งไปยังอีเมลนี้ซึ่งอธิบายเกี่ยวกับวิธีรีเซ็ตรหัสผ่านของคุณ ส่ง ทำตามวิธีการที่ส่งไปยัง %1$s เพื่อกู้คืนรหัสผ่านของคุณ - กำลังส่ง… ที่อยู่อีเมลไม่ตรงกับบัญชีที่มีอยู่ เกิดข้อผิดพลาดที่ไม่รู้จัก รหัสผ่านไม่ถูกต้อง @@ -90,25 +81,20 @@ ป้อนหมายเลขโทรศัพท์ที่ถูกต้อง ป้อนรหัส 6 หลักที่เราส่งไปยัง ส่งรหัสอีกครั้งใน %1$s - ยืนยันหมายเลขโทรศัพท์ กำลังยืนยัน… รหัสไม่ถูกต้อง โปรดลองอีกครั้ง มีการใช้หมายเลขโทรศัพท์นี้หลายครั้งเกินไป - เกิดปัญหาในการยืนยันหมายเลขโทรศัพท์ของคุณ ไม่สามารถใช้รหัสนี้ได้อีกต่อไป - ลงชื่อเข้าใช้ด้วยหมายเลขโทรศัพท์ - ยืนยันหมายเลขโทรศัพท์โดยอัตโนมัติแล้ว ส่งรหัสอีกครั้ง ยืนยันหมายเลขโทรศัพท์ - Use a different phone number + ใช้หมายเลขโทรศัพท์อื่น เมื่อคุณแตะ “%1$s” ระบบจะส่ง SMS ให้คุณ อาจมีค่าบริการรับส่งข้อความและค่าบริการอินเทอร์เน็ต - การแตะ “%1$s” แสดงว่าคุณยอมรับ %2$s และ %3$s ระบบจะส่ง SMS ให้คุณ อาจมีค่าบริการรับส่งข้อความและค่าบริการอินเทอร์เน็ต - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + ข้อผิดพลาดในการตรวจสอบสิทธิ์ + ลองอีกครั้ง + ต้องมีการยืนยันเพิ่มเติม โปรดดำเนินการยืนยันตัวตนแบบหลายปัจจัยให้เสร็จสมบูรณ์ + ต้องลิงก์บัญชี โปรดลองใช้วิธีลงชื่อเข้าใช้วิธีอื่น + การตรวจสอบสิทธิ์ถูกยกเลิก โปรดลองอีกครั้งเมื่อคุณพร้อม เลือกวิธีการตรวจสอบสิทธิ์ @@ -144,29 +130,11 @@ ยืนยันตัวตนแล้ว ลองดำเนินการอีกครั้ง จัดการการยืนยันตัวตนแบบสองปัจจัย รหัสผ่านไม่ถูกต้อง - เลือกวิธีการยืนยัน - เพิ่มชั้นความปลอดภัยเพิ่มเติม - SMS - แอปยืนยันตัวตน - หมายเลขโทรศัพท์นี้เชื่อมโยงกับบัญชีอื่นอยู่แล้ว - ต้องการการยืนยัน - สแกนรหัส QR ด้วยแอปยืนยันตัวตนของคุณ - แอปยืนยันตัวตนได้รับการตั้งค่าแล้ว - คุณแน่ใจหรือไม่ว่าต้องการลบวิธีนี้ - ไม่สามารถลบวิธีได้ - ลบวิธีแล้ว - ป้อนรหัสยืนยัน - ไม่สามารถอัปเดตโปรไฟล์ได้ - อัปเดตโปรไฟล์แล้ว - ยืนยันตัวตนของคุณเพื่อดำเนินการต่อ - ต้องการการยืนยันตัวตนอีกครั้ง - ยืนยันตัวตนอีกครั้งสำเร็จ - ยืนยันตัวตนอีกครั้ง ลบ ส่งอีเมลยืนยันอีกครั้ง คีย์ลับ ออกจากระบบ - ลงชื่อเข้าใช้ในฐานะ + ลงชื่อเข้าใช้ในฐานะ %1$s ข้าม ใช้วิธีอื่น รหัสยืนยัน @@ -176,4 +144,17 @@ การรับรองความถูกต้องแบบหลายปัจจัยถูกปิดใช้งานในขณะนี้ + อีเมลหรือรหัสผ่านไม่ถูกต้อง + เซสชันการยืนยันนี้ใช้ไม่ได้อีกต่อไป โปรดขอรหัสใหม่ + การยืนยันหมายเลขโทรศัพท์ไม่เสร็จสมบูรณ์ โปรดลองอีกครั้ง + ข้อมูลเข้าสู่ระบบนี้เป็นของบัญชีอื่น + หมายเลขโทรศัพท์นี้ไม่ได้ตั้งค่าไว้สำหรับการยืนยันในบัญชีนี้ + เซสชันการลงชื่อเข้าใช้หมดอายุแล้ว โปรดลงชื่อเข้าใช้อีกครั้งเพื่อดำเนินการต่อ + ลิงก์นี้ใช้ไม่ได้อีกต่อไป โปรดขอลิงก์ใหม่ + โปรดยืนยันที่อยู่อีเมลก่อนดำเนินการต่อ + วิธีการยืนยันนี้ตั้งค่าไว้ในบัญชีนี้แล้ว + คุณใช้วิธีการยืนยันถึงขีดจำกัดของบัญชีนี้แล้ว + รหัสผ่านของคุณไม่เป็นไปตามข้อกำหนด โปรดลองใช้รหัสผ่านอื่น + รหัสผ่านยาวเกินไป ความยาวสูงสุดคือ %1$d + ไม่พบพาสคีย์สำหรับบัญชีนี้ โปรดลงชื่อเข้าใช้ด้วยวิธีอื่น diff --git a/auth/src/main/res/values-tl/strings.xml b/auth/src/main/res/values-tl/strings.xml index 90e55a4704..ce6fd212a1 100755 --- a/auth/src/main/res/values-tl/strings.xml +++ b/auth/src/main/res/values-tl/strings.xml @@ -3,14 +3,13 @@ Mag-sign in Magpatuloy Sa pagpapatuloy, ipinababatid mo na tinatanggap mo ang aming %1$s at %2$s. - %1$s \u00A0 \u00A0 %2$s Error sa network, tingnan ang koneksyon mo sa internet. Google - Fecebook + Facebook Twitter GitHub Telepono - Email + Email Mag-sign in sa Google Mag-sign in sa Google Mag-sign in sa Facebook @@ -36,17 +35,14 @@ Bansa Pumili ng bansa Maghanap ng bansa hal. +1, "US" - Password + Password Bagong password Hindi mo ito maaaring iwanan na walang laman. Mali ang email address na iyon Ilagay ang iyong email address upang magpatuloy - Maglagay ng pangalan at apelyido. - Tinitingnan ang mga umiiral nang account… Mag-sign up Pangalan at apelyido I-save - Nagsa-sign up… Hindi masyadong malakas ang password. Gumamit ng %1$d character man lang at kumbinasyon ng mga titik at mga numero Hindi masyadong malakas ang password. Gumamit ng %1$d na character man lang at kumbinasyon ng mga titik at mga numero Hindi nagtagumpay ang pagpaparehistro ng email account Mga Tuntunin ng Serbisyo @@ -54,17 +50,12 @@ Mayroon ka nang account Maligayang pagbabalik! May account ka na\n - Nagamit mo na ang %1$s.        Mag-sign in gamit ang %2$s upang magpatuloy. - Nagamit mo na ang %1$s        upang mag-sign in. Ilagay ang iyong password para sa account na iyon. - Nagamit mo na ang %1$s. Maaari mong ikonekta ang iyong %2$s account sa %1$s sa pamamagitan ng pag-sign in gamit ang link ng email sa ibaba.\n\nPara matagumpay na maikonekta ng daloy na ito ang iyong %2$s account sa email na ito, kailangan mong buksan ang link sa parehong device o browser. - Nagsa-sign in… Nagkaproblema sa pag-sign in? I-recover ang password Suriin ang iyong email Kumuha ng mga tagubilin na ipadala sa email na ito na magpapaliwanag kung paano  i-reset ang iyong password. Ipadala Sundin ang mga tagubilin na ipinadala sa %1$s upang ma-recover ang iyong password. - Ipinapadala… Hindi tumutugma ang email address na iyon sa isang kasalukuyang account Nagkaroon ng hindi malamang error. Maling password. @@ -90,19 +81,14 @@ Maglagay ng wastong numero ng telepono Ilagay ang 6-digit na code na ipinadala namin sa Ipadala muli ang code sa loob ng %1$s - I-verify ang numero ng iyong telepono Bine-verify… Maling code. Subukang muli. Masyadong maraming beses nang nagamit ang numero ng teleponong ito - Nagkaproblema sa pag-verify ng numero ng iyong telepono Wala nang bisa ang code na ito - Mag-sign in gamit ang numero ng telepono - Awtomatikong na-verify ang numero ng telepono Ipadala Muli ang Code I-verify ang Numero ng Telepono - Use a different phone number + Gumamit ng ibang numero ng telepono Sa pag-tap sa “%1$s,“ maaaring magpadala ng SMS. Maaaring ipatupad ang mga rate ng pagmemensahe at data. - Sa pag-tap sa “%1$s”, ipinababatid mo na tinatanggap mo ang aming %2$s at %3$s. Maaaring magpadala ng SMS. Maaaring ipatupad ang mga rate ng pagmemensahe at data. Error sa Pagpapatotoo Subukan Muli Kailangan ang karagdagang pagpapatotoo. Mangyaring kumpletuhin ang multi-factor authentication. @@ -143,36 +129,31 @@ Na-verify ang pagkakakilanlan. Subukan muli ang iyong aksyon. Pamahalaan ang two-factor authentication Maling password - Pumili ng paraan ng pag-verify - Magdagdag ng karagdagang layer ng seguridad - SMS - Authenticator app - Ang numerong ito ay nauugnay sa ibang account - Kinakailangan ang pag-verify - I-scan ang QR code gamit ang iyong authenticator app - Naka-set up na ang authenticator app - Sigurado ka bang gusto mong alisin ang paraang ito? - Hindi ma-alis ang paraan - Naalis ang paraan - Ilagay ang verification code - Hindi ma-update ang profile - Na-update ang profile - Kumpirmahin ang iyong pagkakakilanlan upang magpatuloy - Kinakailangan ang muling pag-authenticate - Matagumpay ang muling pag-authenticate - Mag-authenticate muli Alisin Ipadala muli ang verification email - Secret key + Sikretong key Mag-sign out - Naka-sign in bilang + Naka-sign in bilang %1$s Laktawan Gumamit ng ibang paraan - Verification code + Code sa pag-verify Na-verify ang email I-verify Nagpadala kami ng verification email sa %1$s Kasalukuyang naka-disable ang multi-factor authentication + Mali ang email address o password na iyon + Wala nang bisa ang verification session na iyon. Humiling ng bagong code. + Hindi nakumpleto ang pag-verify ng telepono. Subukan muli. + Kabilang ang mga kredensyal na iyon sa ibang account. + Hindi naka-set up ang numero ng teleponong iyon para sa pag-verify sa account na ito. + Nag-expire na ang iyong sign-in session. Mag-sign in muli para magpatuloy. + Wala nang bisa ang link na iyon. Humiling ng bago. + I-verify ang iyong email address bago ka magpatuloy. + Naka-set up na ang paraan ng pag-verify na iyon sa account na ito. + Naabot mo na ang limitasyon ng mga paraan ng pag-verify sa account na ito. + Hindi natutugunan ng iyong password ang mga kinakailangan. Sumubok ng iba. + Masyadong mahaba ang password. Ang maximum na haba ay %1$d. + Wala kaming nakitang passkey para sa account na ito. Mag-sign in sa ibang paraan. diff --git a/auth/src/main/res/values-tr/strings.xml b/auth/src/main/res/values-tr/strings.xml index 15a7d80b91..055396a39d 100755 --- a/auth/src/main/res/values-tr/strings.xml +++ b/auth/src/main/res/values-tr/strings.xml @@ -3,7 +3,6 @@ Oturum aç Devam Devam ederek %1$s ve %2$s hükümlerimizi kabul ettiğinizi bildirirsiniz. - %1$s \u00A0 \u00A0 %2$s Ağ hatası, internet bağlantınızı kontrol edin. Google Facebook @@ -41,12 +40,9 @@ Bu alanı boş bırakamazsınız. E-posta adresi doğru değil Devam etmek için e-posta adresinizi girin - Lütfen ad ve soyadı girin. - Mevcut hesaplar kontrol ediliyor… Kaydol Ad ve Soyad Kaydet - Kaydolunuyor… Şifre yeterince güçlü değil. En az %1$d karakter kullanın ve hem harf hem de rakam ekleyin Şifre yeterince güçlü değil. En az %1$d karakter kullanın ve hem harf hem de rakam ekleyin E-posta hesabı kaydı başarısız oldu Hizmet Şartları @@ -54,17 +50,12 @@ Zaten bir hesabınız var Tekrar hoş geldiniz! Zaten bir hesabınız var\n - %1$s adresini zaten kullandınız. Devam etmek için %2$s ile oturum açın. - %1$s adresini oturum açmak için zaten kullandınız. Bu hesap için şifrenizi girin. - %1$s adresini zaten kullandınız. Aşağıdaki e-posta bağlantısıyla oturum açarak %2$s hesabınızı %1$s adresine bağlayabilirsiniz.\n\nAkışın %2$s hesabınızı bu e-posta adresine sorunsuz şekilde bağlayabilmesi için bağlantıyı aynı cihazda veya tarayıcıda açmanız gerekir. - Oturum açılıyor… Oturum açılamıyor mu? Şifreyi kurtarın E-postanızı kontrol edin Şifrenizi nasıl sıfırlayacağınızı açıklayan talimatlar bu e-postaya gönderilsin. Gönder Şifrenizi kurtarmak için %1$s adresine gönderilen talimatları uygulayın. - Gönderiliyor… Bu e-posta adresi mevcut bir hesapla eşleşmiyor Bilinmeyen bir hata oluştu. Yanlış şifre. @@ -90,25 +81,20 @@ Geçerli bir telefon numarası girin Şu telefon numarasına gönderdiğimiz 6 haneli kodu girin: %1$s içinde kodu yeniden gönder - Telefon numaranızı doğrulayın Doğrulanıyor… Yanlış kod. Tekrar deneyin. Bu telefon numarası çok fazla kez kullanıldı - Telefon numaranız doğrulanırken bir sorun oluştu Bu kod artık geçerli değil - Telefon numarasıyla oturum açın - Telefon numarası otomatik olarak doğrulandı Kodu Yeniden Gönder Telefon Numarasını Doğrula - Use a different phone number + Farklı bir telefon numarası kullan “%1$s” öğesine dokunarak SMS gönderilebilir. Mesaj ve veri ücretleri uygulanabilir. - “%1$s” öğesine dokunarak %2$s ve %3$s hükümlerimizi kabul ettiğinizi bildirirsiniz. SMS gönderilebilir. Mesaj ve veri ücretleri uygulanabilir. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Kimlik Doğrulama Hatası + Tekrar dene + Ek doğrulama gerekiyor. Lütfen çok faktörlü kimlik doğrulamayı tamamlayın. + Hesabın bağlanması gerekiyor. Lütfen farklı bir oturum açma yöntemi deneyin. + Kimlik doğrulama iptal edildi. Hazır olduğunuzda tekrar deneyin. Kimlik Doğrulama Yöntemini Seçin @@ -144,29 +130,11 @@ Kimlik doğrulandı. İşleminizi tekrar deneyin. İki faktörlü kimlik doğrulamayı yönet Yanlış şifre - Bir doğrulama yöntemi seçin - Ekstra bir güvenlik katmanı ekleyin - SMS - Kimlik doğrulama uygulaması - Bu telefon numarası başka bir hesapla ilişkilendirilmiş - Doğrulama gerekli - QR kodunu kimlik doğrulama uygulamanızla tarayın - Kimlik doğrulama uygulaması zaten ayarlanmış - Bu yöntemi kaldırmak istediğinizden emin misiniz? - Yöntem kaldırılamıyor - Yöntem kaldırıldı - Doğrulama kodunu girin - Profil güncellenemiyor - Profil güncellendi - Devam etmek için kimliğinizi onaylayın - Yeniden kimlik doğrulama gerekli - Yeniden kimlik doğrulama başarılı - Yeniden kimlik doğrula Kaldır Doğrulama e-postasını tekrar gönder Gizli anahtar Çıkış yap - Şu kullanıcı olarak oturum açıldı + %1$s olarak oturum açıldı Atla Farklı bir yöntem kullan Doğrulama kodu @@ -176,4 +144,17 @@ Çok faktörlü kimlik doğrulama şu anda devre dışı + E-posta adresi veya şifre yanlış + Bu doğrulama oturumu artık geçerli değil. Yeni bir kod isteyin. + Telefon doğrulaması tamamlanmadı. Tekrar deneyin. + Bu kimlik bilgileri başka bir hesaba ait. + Bu telefon numarası, bu hesapta doğrulama için ayarlanmamış. + Oturumunuzun süresi doldu. Devam etmek için tekrar oturum açın. + Bu bağlantı artık geçerli değil. Yeni bir tane isteyin. + Devam etmeden önce e-posta adresinizi doğrulayın. + Bu doğrulama yöntemi bu hesapta zaten ayarlanmış. + Bu hesap için doğrulama yöntemi sınırına ulaştınız. + Şifreniz gereksinimleri karşılamıyor. Başka bir şifre deneyin. + Şifre çok uzun. İzin verilen en fazla uzunluk %1$d. + Bu hesap için parola anahtarı bulunamadı. Başka bir yöntemle oturum açın. diff --git a/auth/src/main/res/values-uk/strings.xml b/auth/src/main/res/values-uk/strings.xml index 338339d6c1..1cf1c7f374 100755 --- a/auth/src/main/res/values-uk/strings.xml +++ b/auth/src/main/res/values-uk/strings.xml @@ -3,7 +3,6 @@ Увійти Продовжити Продовжуючи, ви приймаєте такі документи: %1$s і %2$s. - %1$s \u00A0 \u00A0 %2$s Помилка мережі. Перевірте інтернет-з’єднання. Google Facebook @@ -41,12 +40,9 @@ Потрібно заповнити це поле. Електронна адреса неправильна Щоб продовжити, введіть електронну адресу - Введіть ім’я та прізвище. - Перевірка наявних облікових записів… Зареєструватися Ім’я та прізвище Зберегти - Реєстрація… Пароль недостатньо надійний. Введіть принаймні %1$d символ та комбінацію літер і цифр Пароль недостатньо надійний. Введіть принаймні %1$d символи та комбінацію літер і цифр Пароль недостатньо надійний. Введіть принаймні %1$d символів та комбінацію літер і цифр Пароль недостатньо надійний. Введіть принаймні %1$d символу та комбінацію літер і цифр Не вдалося зареєструвати обліковий запис електронної пошти Умови використання @@ -54,17 +50,12 @@ У вас уже є обліковий запис З поверненням! У вас уже є обліковий запис\n - Ви вже використовували електронну адресу %1$s. Щоб продовжити, увійдіть в обліковий запис %2$s. - Ви вже використовували електронну адресу %1$s для входу. Введіть пароль для цього облікового запису. - Ви вже використовували адресу %1$s. Ви можете зв’язати свій обліковий запис %2$s з адресою %1$s, увійшовши за допомогою посилання нижче.\n\nЩоб зв’язати обліковий запис %2$s з цією електронною адресою, потрібно відкрити посилання на тому ж пристрої або в тому ж веб-переглядачі. - Виконується вхід… Не вдається ввійти? Відновити пароль Перевірте свою електронну пошту На цю електронну адресу надійдуть інструкції, як скинути пароль. Надіслати Виконайте інструкції з відновлення пароля, надіслані на адресу %1$s. - Надсилання… Ця електронна адреса не відповідає адресі наявного облікового запису Сталася невідома помилка. Неправильний пароль. @@ -90,25 +81,20 @@ Введіть дійсний номер телефону Введіть 6-значний код, який ми надіслали на номер Повторно надіслати код через %1$s - Підтвердити номер телефону Підтвердження… Неправильний код. Повторіть спробу. Цей номер телефону використовувався забагато разів - Не вдалося підтвердити ваш номер телефону Цей код уже не дійсний - Увійти, використовуючи номер телефону - Номер телефону підтверджено автоматично Повторно надіслати код Підтвердити номер телефону - Use a different phone number + Використати інший номер телефону Коли ви торкнетесь опції “%1$s”, вам може надійти SMS-повідомлення. За SMS і використання трафіку може стягуватися плата. - Торкаючись кнопки “%1$s”, ви приймаєте такі документи: %2$s і %3$s. Вам може надійти SMS-повідомлення. За SMS і використання трафіку може стягуватися плата. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Помилка автентифікації + Повторити спробу + Потрібне додаткове підтвердження. Виконайте багатофакторну автентифікацію. + Обліковий запис потрібно зв\'язати. Спробуйте інший спосіб входу. + Автентифікацію скасовано. Повторіть спробу, коли будете готові. Виберіть спосіб автентифікації @@ -144,29 +130,11 @@ Особу підтверджено. Повторіть дію. Керування двофакторною автентифікацією Неправильний пароль - Виберіть спосіб підтвердження - Додайте додатковий рівень безпеки - SMS - Додаток для автентифікації - Цей номер телефону вже пов\'язаний з іншим обліковим записом - Потрібне підтвердження - Скануйте QR-код додатком для автентифікації - Додаток для автентифікації вже налаштовано - Ви впевнені, що хочете видалити цей спосіб? - Не вдалося видалити спосіб - Спосіб видалено - Введіть код підтвердження - Не вдалося оновити профіль - Профіль оновлено - Підтвердіть свою особу, щоб продовжити - Потрібна повторна автентифікація - Повторна автентифікація виконана - Повторна автентифікація Видалити Надіслати лист підтвердження повторно Секретний ключ Вийти - Ви ввійшли як + Ви ввійшли як %1$s Пропустити Використати інший спосіб Код підтвердження @@ -176,4 +144,17 @@ Багатофакторна автентифікація наразі вимкнена + Неправильна електронна адреса або пароль + Цей сеанс підтвердження більше не дійсний. Запросіть новий код. + Не вдалося завершити підтвердження номера телефону. Повторіть спробу. + Ці облікові дані належать іншому обліковому запису. + Цей номер телефону не налаштовано для підтвердження в цьому обліковому записі. + Сеанс входу закінчився. Увійдіть знову, щоб продовжити. + Це посилання більше не дійсне. Запросіть нове. + Підтвердьте свою електронну адресу, перш ніж продовжити. + Цей спосіб підтвердження вже налаштовано в цьому обліковому записі. + Ви досягли ліміту способів підтвердження для цього облікового запису. + Пароль не відповідає вимогам. Спробуйте інший. + Пароль задовгий. Максимальна довжина – %1$d. + Не вдалося знайти ключ доступу для цього облікового запису. Увійдіть іншим способом. diff --git a/auth/src/main/res/values-ur/strings.xml b/auth/src/main/res/values-ur/strings.xml index aa780c0fb3..b7bccb4c27 100755 --- a/auth/src/main/res/values-ur/strings.xml +++ b/auth/src/main/res/values-ur/strings.xml @@ -3,7 +3,6 @@ سائن ان کریں جاری رکھیں جاری رکھ کر، آپ نشاندہی کر رہے ہیں کہ آپ ہماری %1$s اور %2$s کو قبول کرتے ہیں۔ - %1$s \u00A0 \u00A0 %2$s نیٹ ورک میں خرابی، اپنا انٹرنیٹ کنکشن چیک کریں۔ Google Facebook @@ -41,12 +40,9 @@ آپ اسے خالی نہیں چھوڑ سکتے۔ یہ ای میل پتہ غلط ہے جاری رکھنے کے لیے اپنا ای میل پتہ درج کریں - براہ کرم پہلا اور آخری نام درج کریں۔ - موجودہ اکاؤنٹس چیک ہو رہے ہیں… سائن اپ کریں پہلا اور آخری نام محفوظ کریں - سائن اپ ہو رہا ہے… پاس ورڈ کافی مضبوط نہیں ہے۔ کم از کم %1$d کریکٹر اور حروف و اعداد کا مرکب استعمال کریں پاس ورڈ کافی مضبوط نہیں ہے۔ کم از کم %1$d کریکٹرز اور حروف و اعداد کا مرکب استعمال کریں ای میل اکاؤنٹ کی رجسٹریشن نا کام رہی سروس کی شرائط @@ -54,17 +50,12 @@ آپ کے پاس پہلے سے ہی ایک اکاؤنٹ موجود ہے واپسی پر خوش آمدید! آپ کے پاس پہلے سے ہی ایک اکاؤنٹ ہے‎\n - آپ نے پہلے ہی %1$s استعمال کر لیا ہے۔ جاری رکھنے کے لیے %2$s کے ساتھ سائن ان کریں۔ - آپ نے پہلے ہی %1$s استعمال کر لیا ہے سائن کرنے کے لیے۔ اس کاؤنٹ کے لیے اپنا پاس ورڈ درج کریں۔ - آپ پہلے ہی %1$s استعمال کر چکے ہیں۔ ذیل میں موجود ای میل کے لنک کے ساتھ سائن ان کر کے آپ اپنے %2$s اکاؤنٹ کو %1$s سے منسلک کر سکتے ہیں۔‎\n\nاس فلو کو کامیابی کے ساتھ آپ کے %2$s اکاؤنٹ کو اس ای میل کے ساتھ شامل کرنے کیلئے آپ کو اسی آلہ یا براؤزر پر لنک کھولنا ہوگا۔ - سائن ان ہو رہا ہے… سائن ان کرنے میں دشواری؟ پاس ورڈ بازیافت کریں اپنا ای میل چیک کریں اس ای میل پر بھیجی گئی وہ ہدایات حاصل کریں جن میں اس بات کی وضاحت ہے کہ کیسے اپنا پاس ورڈ دوبارہ ترتیب دیں۔ بھیجیں اپنے پاس ورڈ کو بازیافت کرنے کے لیے %1$s پر بھیجی گئی ہدایات کی پیروی کریں۔ - بھیجا جا رہا ہے… یہ ای میل پتہ کسی موجودہ اکاؤنٹ کے مماثل نہیں ہے ایک نامعلوم خرابی پیش آ گئی۔ غلط پاس ورڈ۔ @@ -90,25 +81,20 @@ براہ کرم ایک درست فون نمبر درج کریں ہماری جانب سے حسبِ ذیل کو بھیجا گیا 6 عدد کا کوڈ درج کریں %1$s میں دوبارہ کوڈ بھیجیں - اپنے فون نمبر کی توثیق کریں توثیق ہو رہی ہے… غلط کوڈ۔ دوبارہ کوشش کریں۔ یہ فون نمبر پہلے ہی کافی مرتبہ استعمال ہو چکا ہے - آپ کے فون نمبر کی توثیق کرنے میں ایک مسئلہ تھا یہ کوڈ اب جائز نہیں رہا - فون نمبر کے ساتھ سائن ان کریں - فون نمبر کی خودکار طور پر توثیق ہو گئی کوڈ دوبارہ بھیجیں فون نمبر کی توثیق کریں - Use a different phone number + مختلف فون نمبر استعمال کریں %1$s پر تھپتھپانے سے، ایک SMS بھیجا جا سکتا ہے۔ پیغام اور ڈیٹا کی شرحوں کا اطلاق ہو سکتا ہے۔ - “%1$s” کو تھپتھپا کر، آپ نشاندہی کر رہے ہیں کہ آپ ہماری %2$s اور %3$s کو قبول کرتے ہیں۔ ایک SMS بھیجا جا سکتا ہے۔ پیغام اور ڈیٹا نرخ لاگو ہو سکتے ہیں۔ - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + تصدیق کی خرابی + دوبارہ کوشش کریں + اضافی تصدیق درکار ہے۔ براہ کرم کثیر عنصری تصدیق مکمل کریں۔ + اکاؤنٹ کو لنک کرنے کی ضرورت ہے۔ براہ کرم مختلف سائن ان طریقہ آزمائیں۔ + تصدیق منسوخ کر دی گئی۔ تیار ہونے پر دوبارہ کوشش کریں۔ تصدیقی طریقہ منتخب کریں @@ -144,29 +130,11 @@ شناخت تصدیق شدہ۔ اپنا عمل دوبارہ آزمائیں۔ دو عنصری توثیق کا نظم کریں غلط پاس ورڈ - تصدیق کا طریقہ منتخب کریں - سیکیورٹی کی ایک اضافی پرت شامل کریں - SMS - تصدیقی ایپ - یہ فون نمبر دوسرے اکاؤنٹ سے منسلک ہے - تصدیق درکار ہے - اپنی تصدیقی ایپ سے QR کوڈ اسکین کریں - تصدیقی ایپ پہلے ہی سیٹ اپ ہے - کیا آپ واقعی اس طریقے کو ہٹانا چاہتے ہیں؟ - طریقہ ہٹانے سے قاصر - طریقہ ہٹا دیا گیا - تصدیقی کوڈ درج کریں - پروفائل اپ ڈیٹ کرنے سے قاصر - پروفائل اپ ڈیٹ ہو گیا - جاری رکھنے کے لیے اپنی شناخت کی تصدیق کریں - دوبارہ توثیق درکار ہے - دوبارہ توثیق کامیاب - دوبارہ توثیق کریں ہٹائیں تصدیقی ای میل دوبارہ بھیجیں خفیہ کلید سائن آؤٹ - بطور سائن ان ہیں + بطور %1$s سائن ان ہیں چھوڑیں ایک مختلف طریقہ استعمال کریں تصدیقی کوڈ @@ -176,4 +144,17 @@ ملٹی فیکٹر تصدیق فی الحال غیر فعال ہے + وہ ای میل یا پاس ورڈ درست نہیں ہے + یہ توثیقی سیشن اب کارآمد نہیں رہا۔ نیا کوڈ طلب کریں۔ + فون کی توثیق مکمل نہیں ہوئی۔ دوبارہ کوشش کریں۔ + یہ اسناد کسی دوسرے اکاؤنٹ سے تعلق رکھتی ہیں۔ + یہ فون نمبر اس اکاؤنٹ پر توثیق کے لیے سیٹ اپ نہیں ہے۔ + آپ کا سائن ان سیشن ختم ہو گیا۔ جاری رکھنے کے لیے دوبارہ سائن ان کریں۔ + یہ لنک اب کارآمد نہیں رہا۔ نیا لنک طلب کریں۔ + جاری رکھنے سے پہلے اپنے ای میل پتے کی توثیق کریں۔ + یہ توثیقی طریقہ اس اکاؤنٹ پر پہلے سے سیٹ اپ ہے۔ + آپ اس اکاؤنٹ پر توثیقی طریقوں کی حد تک پہنچ گئے ہیں۔ + آپ کا پاس ورڈ تقاضے پورے نہیں کرتا۔ کوئی دوسرا آزمائیں۔ + پاس ورڈ بہت طویل ہے۔ زیادہ سے زیادہ طوالت %1$d ہے۔ + اس اکاؤنٹ کے لیے پاس کی نہیں ملی۔ کسی اور طریقے سے سائن ان کریں۔ diff --git a/auth/src/main/res/values-vi/strings.xml b/auth/src/main/res/values-vi/strings.xml index c2a0b99938..6ebe875689 100755 --- a/auth/src/main/res/values-vi/strings.xml +++ b/auth/src/main/res/values-vi/strings.xml @@ -3,14 +3,13 @@ Đăng nhập Tiếp tục Bằng cách tiếp tục, bạn cho biết rằng bạn chấp nhận %1$s và %2$s của chúng tôi. - %1$s \u00A0 \u00A0 %2$s Đã xảy ra lỗi mạng, hãy kiểm tra kết nối Internet của bạn. Google Facebook Twitter GitHub Điện thoại - Email + Địa chỉ email Đăng nhập bằng Google Đăng nhập bằng Google Đăng nhập bằng Facebook @@ -31,7 +30,7 @@ Đăng nhập bằng Yahoo Đăng nhập bằng Yahoo Tiếp - Email + Địa chỉ email Số điện thoại Quốc gia Chọn quốc gia @@ -41,12 +40,9 @@ Bạn không được để trống trường này. Địa chỉ email đó không đúng Nhập địa chỉ email của bạn để tiếp tục - Vui lòng nhập họ và tên. - Đang kiểm tra các tài khoản hiện có… Đăng ký Họ và tên Lưu - Đang đăng ký… Mật khẩu không đủ mạnh. Sử dụng ít nhất %1$d ký tự, kết hợp chữ cái và số Mật khẩu không đủ mạnh. Sử dụng ít nhất %1$d ký tự, kết hợp chữ cái và số Đăng ký tài khoản email không thành công Điều khoản dịch vụ @@ -54,17 +50,12 @@ Bạn đã có tài khoản Chào mừng bạn quay trở lại! Bạn đã có tài khoản\n - Bạn đã sử dụng %1$s. Đăng nhập bằng %2$s để tiếp tục. - Bạn đã sử dụng %1$s để đăng nhập. Hãy nhập mật khẩu của bạn cho tài khoản đó. - Bạn đã sử dụng %1$s. Bạn có thể kết nối tài khoản %2$s của mình với %1$s bằng cách đăng nhập vào liên kết email bên dưới.\n\nĐể quy trình kết nối tài khoản %2$s của bạn với email này diễn ra thành công, bạn phải mở đường liên kết trên cùng một thiết bị hoặc trình duyệt. - Đang đăng nhập… Bạn gặp sự cố khi đăng nhập? Khôi phục mật khẩu Kiểm tra email của bạn Xem hướng dẫn gửi tới email này có giải thích cách đặt lại mật khẩu của bạn. Gửi Làm theo hướng dẫn gửi tới %1$s để khôi phục mật khẩu của bạn. - Đang gửi… Địa chỉ email đó không khớp với tài khoản hiện có Đã xảy ra lỗi không xác định. Sai mật khẩu. @@ -90,25 +81,20 @@ Nhập số điện thoại hợp lệ Nhập mã 6 chữ số mà chúng tôi đã gửi cho bạn Gửi lại mã sau %1$s - Xác minh số điện thoại của bạn Đang xác minh… Mã không chính xác. Hãy thử lại. Số điện thoại này đã được sử dụng quá nhiều lần - Đã xảy ra sự cố khi xác minh số điện thoại của bạn Mã này không còn hợp lệ - Đăng nhập bằng số điện thoại - Đã tự động xác minh số điện thoại Gửi lại mã Xác minh số điện thoại - Use a different phone number + Dùng số điện thoại khác Bằng cách nhấn vào “%1$s”, bạn có thể nhận được một tin nhắn SMS. Cước tin nhắn và dữ liệu có thể áp dụng. - Bằng cách nhấn vào “%1$s”, bạn cho biết rằng bạn chấp nhận %2$s và %3$s của chúng tôi. Bạn có thể nhận được một tin nhắn SMS. Cước tin nhắn và dữ liệu có thể áp dụng. - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + Lỗi xác thực + Thử lại + Bạn cần xác minh thêm. Vui lòng hoàn tất quy trình xác thực nhiều yếu tố. + Bạn cần liên kết tài khoản. Vui lòng thử một phương thức đăng nhập khác. + Quá trình xác thực đã bị hủy. Vui lòng thử lại khi bạn sẵn sàng. Chọn phương thức xác thực @@ -144,29 +130,11 @@ Đã xác minh danh tính. Thử lại hành động của bạn. Quản lý xác thực hai yếu tố Mật khẩu không đúng - Chọn phương thức xác minh - Thêm lớp bảo mật bổ sung - SMS - Ứng dụng xác thực - Số điện thoại này được liên kết với tài khoản khác - Yêu cầu xác minh - Quét mã QR bằng ứng dụng xác thực của bạn - Ứng dụng xác thực đã được thiết lập - Bạn có chắc chắn muốn xóa phương thức này không? - Không thể xóa phương thức - Đã xóa phương thức - Nhập mã xác minh - Không thể cập nhật hồ sơ - Đã cập nhật hồ sơ - Xác nhận danh tính của bạn để tiếp tục - Yêu cầu xác thực lại - Xác thực lại thành công - Xác thực lại Xóa Gửi lại email xác minh Khóa bí mật Đăng xuất - Đã đăng nhập với tư cách + Đã đăng nhập với tư cách %1$s Bỏ qua Sử dụng phương thức khác Mã xác minh @@ -176,4 +144,17 @@ Xác thực đa yếu tố hiện đang bị vô hiệu hóa + Email hoặc mật khẩu không chính xác + Phiên xác minh này không còn hợp lệ. Hãy yêu cầu mã mới. + Quá trình xác minh số điện thoại chưa hoàn tất. Hãy thử lại. + Thông tin đăng nhập này thuộc về một tài khoản khác. + Số điện thoại này chưa được thiết lập để xác minh trên tài khoản này. + Phiên đăng nhập của bạn đã hết hạn. Hãy đăng nhập lại để tiếp tục. + Đường liên kết này không còn hợp lệ. Hãy yêu cầu một đường liên kết mới. + Hãy xác minh địa chỉ email của bạn trước khi tiếp tục. + Phương thức xác minh này đã được thiết lập trên tài khoản này. + Bạn đã đạt đến giới hạn số phương thức xác minh trên tài khoản này. + Mật khẩu của bạn không đáp ứng các yêu cầu. Hãy thử mật khẩu khác. + Mật khẩu quá dài. Độ dài tối đa là %1$d. + Không tìm thấy mã xác thực cho tài khoản này. Hãy đăng nhập bằng cách khác. diff --git a/auth/src/main/res/values-zh-rCN/strings.xml b/auth/src/main/res/values-zh-rCN/strings.xml index 12fb4c3dd8..7868e3fd33 100755 --- a/auth/src/main/res/values-zh-rCN/strings.xml +++ b/auth/src/main/res/values-zh-rCN/strings.xml @@ -3,7 +3,6 @@ 登录 继续 继续即表示您接受我们的%1$s和%2$s。 - %1$s \u00A0 \u00A0 %2$s 网络错误,请检查您的互联网连接。 Google Facebook @@ -41,12 +40,9 @@ 此处不能留空。 该电子邮件地址不正确 请输入您的电子邮件地址以继续 - 请输入姓氏和名字。 - 正在检查是否存在现有帐号… 注册 姓名 保存 - 正在注册… 密码的安全系数不够高。请使用至少 %1$d 个字符,并混合使用字母和数字 密码的安全系数不够高。请使用至少 %1$d 个字符,并混合使用字母和数字 未能使用电子邮件地址注册帐号 服务条款 @@ -54,17 +50,12 @@ 您已经有帐号了 欢迎回来! 您已经有帐号了\n - 您已经使用了 %1$s。请使用 %2$s 帐号登录以继续操作。 - 您已经使用 %1$s 登录了,请输入该帐号的密码。 - 您已经使用了 %1$s。您可以先使用下方电子邮件链接登录,然后将%2$s帐号与 %1$s 关联起来。\n\n为了完成此流程,顺利将您的%2$s帐号与此电子邮件地址关联起来,您必须使用相同的设备或浏览器打开链接。 - 正在登录… 登录时遇到问题? 找回密码 请查收电子邮件 向此电子邮件地址发送关于如何重置密码的说明。 发送 请按照发送到 %1$s 的说明找回密码。 - 正在发送… 该电子邮件地址没有相匹配的现有帐号 发生未知错误。 密码不正确。 @@ -90,25 +81,20 @@ 请输入有效的电话号码 输入我们发送至以下电话号码的 6 位数验证码 %1$s 后可重新发送验证码 - 验证您的电话号码 正在验证… 验证码有误,请重试。 此电话号码的使用次数过多 - 验证您的电话号码时出现问题 此验证码已失效 - 使用电话号码登录 - 电话号码已自动验证 重新发送验证码 验证电话号码 - Use a different phone number + 使用其他电话号码 您点按“%1$s”后,系统会向您发送一条短信。这可能会产生短信费用和上网流量费。 - 点按“%1$s”即表示您接受我们的%2$s和%3$s。系统会向您发送一条短信。这可能会产生短信费用和上网流量费。 - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + 身份验证错误 + 重试 + 需要额外的验证。请完成多重身份验证。 + 需要关联账户。请尝试其他登录方式。 + 身份验证已取消。准备好后请重试。 选择身份验证方法 @@ -144,29 +130,11 @@ 身份已验证。请重试您的操作。 管理双重验证 密码不正确 - 选择验证方式 - 添加额外的安全保障 - 短信 - 身份验证器应用 - 该手机号码已与其他账户关联 - 需要验证 - 使用身份验证器应用扫描二维码 - 身份验证器应用已设置 - 确定要移除此验证方式吗? - 无法移除验证方式 - 验证方式已移除 - 输入验证码 - 无法更新个人资料 - 个人资料已更新 - 确认您的身份以继续 - 需要重新验证 - 重新验证成功 - 重新验证 移除 重新发送验证邮件 密钥 退出 - 登录身份 + 登录身份:%1$s 跳过 使用其他方式 验证码 @@ -176,4 +144,17 @@ 多重身份验证当前已禁用 + 电子邮件地址或密码不正确 + 该验证会话已失效,请重新获取验证码。 + 电话号码验证未完成,请重试。 + 这些凭据属于其他帐号。 + 该电话号码未在此帐号中设置为验证方式。 + 您的登录会话已过期,请重新登录以继续。 + 该链接已失效,请重新获取。 + 请先验证您的电子邮件地址,然后再继续。 + 该验证方式已在此帐号中设置。 + 您已达到此帐号的验证方式数量上限。 + 您的密码不符合要求,请尝试其他密码。 + 密码过长。最大长度为 %1$d。 + 未找到此账号的通行密钥,请通过其他方式登录。 diff --git a/auth/src/main/res/values-zh-rHK/strings.xml b/auth/src/main/res/values-zh-rHK/strings.xml index fb7a627ae6..509d77223a 100755 --- a/auth/src/main/res/values-zh-rHK/strings.xml +++ b/auth/src/main/res/values-zh-rHK/strings.xml @@ -3,7 +3,6 @@ 登入 繼續 選擇繼續即表示您同意接受我們的《%1$s》和《%2$s》。 - %1$s \u00A0 \u00A0 %2$s 網路發生錯誤,請檢查您的網際網路連線。 Google Facebook @@ -41,12 +40,9 @@ 此欄位不可留空。 電子郵件地址不正確 如要繼續,請輸入您的電子郵件地址 - 請輸入姓名。 - 正在檢查是否已有帳戶… 註冊 姓氏和名字 儲存 - 註冊中… 密碼安全強度不足。請使用至少 %1$d 個字元並混用字母和數字 密碼安全強度不足。請使用至少 %1$d 個字元並混用字母和數字 電子郵件帳戶註冊失敗 服務條款 @@ -54,17 +50,12 @@ 您已經有帳戶 歡迎回來! 您已經有帳戶了\n - 目前您用於登入的帳戶是 %1$s。 如要繼續,請使用 %2$s 帳戶登入。 - 您已使用 %1$s 登入。請輸入這個帳戶的密碼。 - 您已經使用過 %1$s。只要使用下方的電子郵件連結登入,即可將您的 %2$s 帳戶連結至 %1$s。\n\n如要順利將您的 %2$s 帳戶連結至這個電子郵件,請透過登入時使用的裝置或瀏覽器來開啟連結。 - 登入中… 無法登入嗎? 重新取得密碼 請查看您的電子郵件信箱 這個電子郵件會收到重設密碼的 操作說明。 傳送 請依照傳送至 %1$s 的操作說明,重新取得密碼。 - 傳送中… 找不到與這個電子郵件地址相符的帳戶 發生不明錯誤。 密碼不正確。 @@ -90,25 +81,20 @@ 請輸入有效的電話號碼 請輸入傳送至以下電話號碼的 6 位數驗證碼 %1$s 秒後將重新發送驗證碼 - 驗證您的電話號碼 驗證中… 驗證碼錯誤,請再試一次。 使用這個電話號碼的次數過多 - 驗證電話號碼時發生問題 這個代碼已經失效 - 使用電話號碼登入 - 已自動驗證電話號碼 重新傳送驗證碼 驗證電話號碼 - Use a different phone number + 使用其他電話號碼 輕觸 [%1$s] 後,系統將會傳送一封簡訊。您可能需支付簡訊和數據傳輸費用。 - 輕觸 [%1$s] 即表示您同意接受我們的《%2$s》和《%3$s》。系統將會傳送簡訊給您,不過您可能需要支付簡訊和數據傳輸費用。 - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + 驗證錯誤 + 重試 + 需要額外驗證。請完成雙重驗證。 + 需要連結帳戶。請嘗試其他登入方式。 + 驗證已取消。準備好後請再試一次。 選擇驗證方法 @@ -144,29 +130,11 @@ 身分已驗證。請重試您的操作。 管理雙重驗證 密碼不正確 - 選擇驗證方式 - 加入額外的安全保障 - 短訊 - 驗證器應用程式 - 此電話號碼已與其他帳戶關聯 - 需要驗證 - 使用驗證器應用程式掃描二維碼 - 驗證器應用程式已設定 - 確定要移除此驗證方式嗎? - 無法移除驗證方式 - 驗證方式已移除 - 輸入驗證碼 - 無法更新個人資料 - 個人資料已更新 - 確認您的身分以繼續 - 需要重新驗證 - 重新驗證成功 - 重新驗證 移除 重新發送驗證電郵 密鑰 登出 - 登入身分 + 登入身分:%1$s 略過 使用其他方式 驗證碼 @@ -176,4 +144,17 @@ 多重身份验证当前已禁用 + 電郵地址或密碼不正確 + 此驗證程序已失效,請重新要求驗證碼。 + 電話驗證尚未完成,請再試一次。 + 這些憑證屬於其他帳戶。 + 此電話號碼並未在此帳戶中設定為驗證方法。 + 您的登入程序已逾時,請重新登入以繼續。 + 此連結已失效,請要求新的連結。 + 請先驗證您的電郵地址,然後再繼續。 + 此驗證方法已在此帳戶中設定。 + 您已達到此帳戶的驗證方法數量上限。 + 您的密碼不符合要求,請嘗試其他密碼。 + 密碼太長。長度上限為 %1$d。 + 找不到此帳戶的密碼金鑰,請使用其他方式登入。 diff --git a/auth/src/main/res/values-zh-rTW/strings.xml b/auth/src/main/res/values-zh-rTW/strings.xml index 63b993c5b7..eb3b205ca2 100755 --- a/auth/src/main/res/values-zh-rTW/strings.xml +++ b/auth/src/main/res/values-zh-rTW/strings.xml @@ -3,7 +3,6 @@ 登入 繼續 選擇繼續即表示您同意接受我們的《%1$s》和《%2$s》。 - %1$s \u00A0 \u00A0 %2$s 網路發生錯誤,請檢查您的網際網路連線。 Google Facebook @@ -41,12 +40,9 @@ 此欄位不可留空。 電子郵件地址不正確 如要繼續,請輸入您的電子郵件地址 - 請輸入姓名。 - 正在檢查是否已有帳戶… 註冊 姓氏和名字 儲存 - 註冊中… 密碼安全強度不足。請使用至少 %1$d 個字元並混用字母和數字 密碼安全強度不足。請使用至少 %1$d 個字元並混用字母和數字 電子郵件帳戶註冊失敗 服務條款 @@ -54,17 +50,12 @@ 您已經有帳戶 歡迎回來! 您已經有帳戶了\n - 目前您用於登入的帳戶是 %1$s。 如要繼續,請使用 %2$s 帳戶登入。 - 您已使用 %1$s 登入。請輸入這個帳戶的密碼。 - 您已經使用過 %1$s。只要使用下方的電子郵件連結登入,即可將您的 %2$s 帳戶連結至 %1$s。\n\n如要順利將您的 %2$s 帳戶連結至這個電子郵件,請透過登入時使用的裝置或瀏覽器來開啟連結。 - 登入中… 無法登入嗎? 重新取得密碼 請查看您的電子郵件信箱 這個電子郵件會收到重設密碼的 操作說明。 傳送 請依照傳送至 %1$s 的操作說明,重新取得密碼。 - 傳送中… 找不到與這個電子郵件地址相符的帳戶 發生不明錯誤。 密碼不正確。 @@ -90,25 +81,20 @@ 請輸入有效的電話號碼 請輸入傳送至以下電話號碼的 6 位數驗證碼 %1$s 秒後將重新發送驗證碼 - 驗證您的電話號碼 驗證中… 驗證碼錯誤,請再試一次。 使用這個電話號碼的次數過多 - 驗證電話號碼時發生問題 這個代碼已經失效 - 使用電話號碼登入 - 已自動驗證電話號碼 重新傳送驗證碼 驗證電話號碼 - Use a different phone number + 使用其他電話號碼 輕觸 [%1$s] 後,系統將會傳送一封簡訊。您可能需支付簡訊和數據傳輸費用。 - 輕觸 [%1$s] 即表示您同意接受我們的《%2$s》和《%3$s》。系統將會傳送簡訊給您,不過您可能需要支付簡訊和數據傳輸費用。 - Authentication Error - Try again - Additional verification required. Please complete multi-factor authentication. - Account needs to be linked. Please try a different sign-in method. - Authentication was cancelled. Please try again when ready. + 驗證錯誤 + 重試 + 需要額外驗證。請完成雙重驗證。 + 需要連結帳戶。請嘗試其他登入方式。 + 驗證已取消。準備好後請再試一次。 選擇驗證方法 @@ -144,29 +130,11 @@ 身分已驗證。請重試您的操作。 管理雙重驗證 密碼不正確 - 選擇驗證方式 - 新增額外的安全保障 - 簡訊 - 驗證器應用程式 - 此電話號碼已與其他帳號關聯 - 需要驗證 - 使用驗證器應用程式掃描 QR 碼 - 驗證器應用程式已設定 - 確定要移除此驗證方式嗎? - 無法移除驗證方式 - 驗證方式已移除 - 輸入驗證碼 - 無法更新個人資料 - 個人資料已更新 - 確認您的身分以繼續 - 需要重新驗證 - 重新驗證成功 - 重新驗證 移除 重新傳送驗證郵件 密鑰 登出 - 登入身分 + 登入身分:%1$s 略過 使用其他方式 驗證碼 @@ -176,4 +144,17 @@ 多重身份验证当前已禁用 + 電子郵件地址或密碼不正確 + 此驗證工作階段已失效,請重新要求驗證碼。 + 電話驗證尚未完成,請再試一次。 + 這些憑證屬於其他帳號。 + 此電話號碼並未在這個帳號中設定為驗證方法。 + 您的登入工作階段已過期,請重新登入以繼續。 + 此連結已失效,請要求新的連結。 + 請先驗證您的電子郵件地址,然後再繼續。 + 此驗證方法已在這個帳號中設定。 + 您已達到這個帳號的驗證方法數量上限。 + 您的密碼不符合要求,請嘗試其他密碼。 + 密碼太長。長度上限為 %1$d。 + 找不到此帳戶的密碼金鑰,請使用其他方式登入。 diff --git a/auth/src/main/res/values-zh/strings.xml b/auth/src/main/res/values-zh/strings.xml index ada6243e74..bce5465d8d 100755 --- a/auth/src/main/res/values-zh/strings.xml +++ b/auth/src/main/res/values-zh/strings.xml @@ -3,7 +3,6 @@ 登录 继续 继续即表示您接受我们的%1$s和%2$s。 - %1$s \u00A0 \u00A0 %2$s 网络错误,请检查您的互联网连接。 Google Facebook @@ -41,12 +40,9 @@ 此处不能留空。 该电子邮件地址不正确 请输入您的电子邮件地址以继续 - 请输入姓氏和名字。 - 正在检查是否存在现有帐号… 注册 姓名 保存 - 正在注册… 密码的安全系数不够高。请使用至少 %1$d 个字符,并混合使用字母和数字 密码的安全系数不够高。请使用至少 %1$d 个字符,并混合使用字母和数字 未能使用电子邮件地址注册帐号 服务条款 @@ -54,17 +50,12 @@ 您已经有帐号了 欢迎回来! 您已经有帐号了\n - 您已经使用了 %1$s。请使用 %2$s 帐号登录以继续操作。 - 您已经使用 %1$s 登录了,请输入该帐号的密码。 - 您已经使用了 %1$s。您可以先使用下方电子邮件链接登录,然后将%2$s帐号与 %1$s 关联起来。\n\n为了完成此流程,顺利将您的%2$s帐号与此电子邮件地址关联起来,您必须使用相同的设备或浏览器打开链接。 - 正在登录… 登录时遇到问题? 找回密码 请查收电子邮件 向此电子邮件地址发送关于如何重置密码的说明。 发送 请按照发送到 %1$s 的说明找回密码。 - 正在发送… 该电子邮件地址没有相匹配的现有帐号 发生未知错误。 密码不正确。 @@ -90,19 +81,14 @@ 请输入有效的电话号码 输入我们发送至以下电话号码的 6 位数验证码 %1$s 后可重新发送验证码 - 验证您的电话号码 正在验证… 验证码有误,请重试。 此电话号码的使用次数过多 - 验证您的电话号码时出现问题 此验证码已失效 - 使用电话号码登录 - 电话号码已自动验证 重新发送验证码 验证电话号码 - Use a different phone number + 使用其他电话号码 您点按“%1$s”后,系统会向您发送一条短信。这可能会产生短信费用和上网流量费。 - 点按"%1$s"即表示您接受我们的%2$s和%3$s。系统会向您发送一条短信。这可能会产生短信费用和上网流量费。 身份验证错误 重试 需要额外的验证。请完成多重身份验证。 @@ -143,29 +129,11 @@ 身份已验证。请重试您的操作。 管理双重验证 密码不正确 - 选择验证方式 - 添加额外的安全保障 - 短信 - 身份验证器应用 - 该手机号码已与其他账户关联 - 需要验证 - 使用身份验证器应用扫描二维码 - 身份验证器应用已设置 - 确定要移除此验证方式吗? - 无法移除验证方式 - 验证方式已移除 - 输入验证码 - 无法更新个人资料 - 个人资料已更新 - 确认您的身份以继续 - 需要重新验证 - 重新验证成功 - 重新验证 移除 重新发送验证邮件 密钥 退出 - 登录身份 + 登录身份:%1$s 跳过 使用其他方式 验证码 @@ -175,4 +143,17 @@ 多重身份验证当前已禁用 + 电子邮件地址或密码不正确 + 该验证会话已失效,请重新获取验证码。 + 电话号码验证未完成,请重试。 + 这些凭据属于其他帐号。 + 该电话号码未在此帐号中设置为验证方式。 + 您的登录会话已过期,请重新登录以继续。 + 该链接已失效,请重新获取。 + 请先验证您的电子邮件地址,然后再继续。 + 该验证方式已在此帐号中设置。 + 您已达到此帐号的验证方式数量上限。 + 您的密码不符合要求,请尝试其他密码。 + 密码过长。最大长度为 %1$d。 + 未找到此账号的通行密钥,请通过其他方式登录。 diff --git a/auth/src/main/res/values/strings.xml b/auth/src/main/res/values/strings.xml index ca6738c108..5814e89535 100644 --- a/auth/src/main/res/values/strings.xml +++ b/auth/src/main/res/values/strings.xml @@ -2,31 +2,29 @@ ui_flow - @string/app_name - Loading... + Loading… Initializing - Signing in as guest... - Signing in with Google... - Signing in with Facebook... - Signing in with %1$s... - Verifying phone number... - Submitting verification code... - Signing in with phone... - Creating account... - Signing in... - Signing in... - Sending sign-in link... - Signing in with email link... - Sending password reset email... - Signing out... - Finishing that action... - Deleting account... + Signing in as guest… + Signing in with Google… + Signing in with Facebook… + Signing in with %1$s… + Verifying phone number… + Submitting verification code… + Signing in with phone… + Creating account… + Signing in… + Signing in… + Sending sign-in link… + Signing in with email link… + Sending password reset email… + Signing out… + Finishing that action… + Deleting account… Sign in Continue By continuing, you are indicating that you accept our %1$s and %2$s. - %1$s \u00A0 \u00A0 %2$s Network error, check your internet connection. @@ -90,18 +88,6 @@ Enrolled on %1$s Enter TOTP code Scan the QR code or enter the secret key in your authenticator app - Choose a verification method - Add an extra layer of security - SMS - Authenticator app - This phone number is associated with another account - Verification required - Scan the QR code with your authenticator app - Authenticator app is already set up - Are you sure you want to remove this method? - Unable to remove method - Method removed - Enter the verification code Verify your identity @@ -109,14 +95,6 @@ Account: %1$s Incorrect password. Please try again. Authentication failed. Please try again. - Confirm your identity to continue - Re-authentication required - Re-authentication successful - Re-authenticate - - - Unable to update profile - Profile updated Next @@ -131,14 +109,11 @@ You can\'t leave this empty. That email address isn\'t correct Enter your email address to continue - Please enter a first and last name. - Checking for existing accounts... Sign up First & last name Save - Signing up... Password not strong enough. Use at least %1$d character and a mix of letters and numbers Password not strong enough. Use at least %1$d characters and a mix of letters and numbers @@ -149,25 +124,10 @@ email_field - @string/fui_sign_in_default - @string/fui_sign_in_default You already have an account Welcome back! You already have an account\n - - You\'ve already used %1$s. - Sign in with %2$s to continue. - - - You\'ve already used %1$s - to sign in. Enter your password for that account. - - - You\'ve already used %1$s. - You can connect your %2$s account with %1$s by signing in with email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser. - - Signing in... Trouble signing in? @@ -177,7 +137,6 @@ reset your password. Send Follow the instructions sent to %1$s to recover your password. - Sending... That email address doesn\'t match an existing account @@ -194,7 +153,14 @@ Passwords do not match - Password must be at least %1$d characters long + + Password must be at least %1$d characters long + + Password is too long. The maximum length is %1$d. Password must contain at least one uppercase letter Password must contain at least one lowercase letter Password must contain at least one number @@ -229,19 +195,14 @@ Enter a valid phone number Enter the 6-digit code we sent to Resend code in %1$s - Verify your phone number - Verifying... + Verifying… Wrong code. Try again. This phone number has been used too many times - There was a problem verifying your phone number This code is no longer valid - Sign in with phone number - Phone number automatically verified Resend Code Verify Phone Number Use a different phone number By tapping "%1$s", an SMS may be sent. Message & data rates may apply. - By tapping "%1$s", you are indicating that you accept our %2$s and %3$s. An SMS may be sent. Message & data rates may apply. Authentication Error @@ -252,20 +213,35 @@ User account has been disabled - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + That email or password isn\'t correct + That verification session is no longer valid. Request a new code. + Phone verification didn\'t complete. Try again. + Those credentials belong to a different account. + That phone number isn\'t set up for verification on this account. + Your sign-in session expired. Sign in again to continue. + That link is no longer valid. Request a new one. + Verify your email address before you continue. + That verification method is already set up on this account. + You\'ve reached the limit for verification methods on this account. + Your password doesn\'t meet the requirements. Try a different one. + We couldn\'t find a passkey for this account. Sign in another way. Choose Authentication Method diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt new file mode 100644 index 0000000000..d3c2835671 --- /dev/null +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionRecoveryResolutionTest.kt @@ -0,0 +1,516 @@ +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.firebase.ui.auth + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.ui.components.getRecoveryActionText +import com.firebase.ui.auth.ui.components.getRecoveryMessage +import com.firebase.ui.auth.ui.components.isRecoverable +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import com.google.firebase.FirebaseException +import com.google.firebase.FirebaseTooManyRequestsException +import com.google.firebase.auth.FirebaseAuthException +import com.google.firebase.auth.FirebaseAuthInvalidUserException +import com.google.firebase.auth.FirebaseAuthMissingActivityForRecaptchaException +import com.google.firebase.auth.FirebaseAuthMultiFactorException +import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException +import com.google.firebase.auth.FirebaseAuthUserCollisionException +import com.google.firebase.auth.FirebaseAuthWeakPasswordException +import java.util.Locale +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.kotlin.doCallRealMethod +import org.mockito.kotlin.whenever +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * End-to-end resolution tests: a real Firebase exception goes through [AuthException.from] with a + * real [DefaultAuthUIStringProvider], and the result is rendered through + * [getRecoveryMessage] exactly as the error dialog would render it. + * + * Why the real provider and not a mock: the `fui_error_*` type-level hooks are deliberately blank + * so hosts can override them, so a mocked provider that stubs one proves nothing about what ships. + * Every assertion here fails if a branch of `from()` falls through a blank hook to the raw, + * untranslated Firebase SDK diagnostic. + * + * @suppress Internal test class + */ +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) +class AuthExceptionRecoveryResolutionTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + private val strings: AuthUIStringProvider = DefaultAuthUIStringProvider(context) + + /** Renders [firebaseException] the way the error dialog does, end to end. */ + private fun resolve( + firebaseException: Exception, + provider: AuthUIStringProvider = strings + ): String = getRecoveryMessage(AuthException.from(firebaseException, provider), provider) + + // Verbatim firebase-auth 24.2.0 diagnostics. All are English regardless of device locale. + private val networkDiagnostic = "A network error (such as timeout, interrupted connection or " + + "unreachable host) has occurred." + private val userNotFoundDiagnostic = "There is no user record corresponding to this " + + "identifier. The user may have been deleted." + private val weakPasswordDiagnostic = "The given password is invalid. [ Password should be at " + + "least 6 characters ]" + private val emailInUseDiagnostic = "The email address is already in use by another account." + private val mfaDiagnostic = "Please complete a second factor challenge." + private val recentLoginDiagnostic = "This operation is sensitive and requires recent " + + "authentication. Log in again before retrying this request." + private val cancelledDiagnostic = "User cancelled the sign-in flow." + private val operationNotAllowedDiagnostic = "This operation is not allowed. This may be " + + "because the given sign-in provider is disabled for this Firebase project. Enable it " + + "in the Firebase console, under the sign-in method tab of the Auth section. " + + "[ OPERATION_NOT_ALLOWED ]" + + private fun userCollision(code: String, email: String? = null): FirebaseAuthUserCollisionException { + val exception = mock(FirebaseAuthUserCollisionException::class.java) + whenever(exception.errorCode).thenReturn(code) + whenever(exception.message).thenReturn(emailInUseDiagnostic) + whenever(exception.email).thenReturn(email) + return exception + } + + private fun multiFactor(): FirebaseAuthMultiFactorException { + val exception = mock(FirebaseAuthMultiFactorException::class.java) + whenever(exception.message).thenReturn(mfaDiagnostic) + return exception + } + + // ============================================================================================= + // The six types whose type-level hook is deliberately blank + // ============================================================================================= + + @Test + fun `network failure resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = object : FirebaseException(networkDiagnostic) {} + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.networkErrorRecoveryMessage) + assertThat(resolved).isNotEqualTo(networkDiagnostic) + } + + @Test + fun `user not found resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = + FirebaseAuthInvalidUserException("ERROR_USER_NOT_FOUND", userNotFoundDiagnostic) + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.userNotFoundRecoveryMessage) + assertThat(resolved).isNotEqualTo(userNotFoundDiagnostic) + } + + @Test + fun `an unmapped user code resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = + FirebaseAuthInvalidUserException("ERROR_SOMETHING_NEW", userNotFoundDiagnostic) + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.userNotFoundRecoveryMessage) + assertThat(resolved).isNotEqualTo(userNotFoundDiagnostic) + } + + @Test + fun `weak password resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = FirebaseAuthWeakPasswordException( + "ERROR_WEAK_PASSWORD", + weakPasswordDiagnostic, + "Password should be at least 6 characters" + ) + + val authException = AuthException.from(firebaseException, strings) + + assertThat(authException.message).isEqualTo(strings.weakPasswordRecoveryMessage) + assertThat(authException.message).isNotEqualTo(weakPasswordDiagnostic) + assertThat(resolve(firebaseException)).startsWith(strings.weakPasswordRecoveryMessage) + } + + @Test + fun `email already in use resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = userCollision("ERROR_EMAIL_ALREADY_IN_USE") + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.emailAlreadyInUseRecoveryMessage) + assertThat(resolved).isNotEqualTo(emailInUseDiagnostic) + } + + @Test + fun `mfa required resolves to the library's own copy, not the SDK diagnostic`() { + val firebaseException = multiFactor() + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.mfaRequiredRecoveryMessage) + assertThat(resolved).isNotEqualTo(mfaDiagnostic) + } + + @Test + fun `auth cancelled resolves to the library's own copy, not the SDK diagnostic`() { + for (code in listOf("ERROR_USER_CANCELLED", "ERROR_WEB_CONTEXT_CANCELED")) { + val firebaseException = object : FirebaseAuthException(code, cancelledDiagnostic) {} + + val resolved = resolve(firebaseException) + + assertWithMessage(code).that(resolved).isEqualTo(strings.authCancelledRecoveryMessage) + assertWithMessage(code).that(resolved).isNotEqualTo(cancelledDiagnostic) + } + } + + @Test + fun `too many requests resolves to the library's own copy, not the SDK diagnostic`() { + val diagnostic = "We have blocked all requests from this device due to unusual activity." + val firebaseException = FirebaseTooManyRequestsException(diagnostic) + + val resolved = resolve(firebaseException) + + assertThat(resolved).isEqualTo(strings.tooManyRequestsRecoveryMessage) + assertThat(resolved).isNotEqualTo(diagnostic) + } + + @Test + fun `account collision resolves to the library's own copy, not the SDK diagnostic`() { + val codes = listOf( + "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL", + "ERROR_CREDENTIAL_ALREADY_IN_USE", + "ERROR_SOME_FUTURE_COLLISION_CODE", + ) + + for (code in codes) { + val resolved = resolve(userCollision(code)) + + assertWithMessage(code).that(resolved) + .isEqualTo(strings.accountLinkingRequiredRecoveryMessage) + assertWithMessage(code).that(resolved).isNotEqualTo(emailInUseDiagnostic) + } + } + + // ============================================================================================= + // Misconfiguration — the diagnostic must not be on the message at all + // ============================================================================================= + + @Test + fun `a disabled sign-in provider never puts the console diagnostic on the message`() { + val firebaseException = + object : FirebaseAuthException("ERROR_OPERATION_NOT_ALLOWED", operationNotAllowedDiagnostic) {} + + val authException = AuthException.from(firebaseException, strings) + + assertThat(authException).isInstanceOf(AuthException.MisconfigurationException::class.java) + // EmailAuthScreen and PhoneAuthScreen render this inline, bypassing getRecoveryMessage. + assertThat(authException.message).isEqualTo(strings.unknownErrorRecoveryMessage) + assertThat(authException.message).doesNotContain("Firebase") + assertThat(authException.message).doesNotContain("OPERATION_NOT_ALLOWED") + // The diagnostic is still there for logs and for hosts. + assertThat(authException.cause).isEqualTo(firebaseException) + assertThat(authException.cause?.message).isEqualTo(operationNotAllowedDiagnostic) + assertThat(resolve(firebaseException)).isEqualTo(strings.unknownErrorRecoveryMessage) + } + + @Test + fun `the email-template and quota codes are misconfiguration, not unknown errors`() { + val codes = listOf( + "ERROR_INVALID_MESSAGE_PAYLOAD", + "ERROR_INVALID_SENDER", + "ERROR_INVALID_RECIPIENT_EMAIL", + // Declared on FirebaseAuthMissingActivityForRecaptchaException's own constructor. + "ERROR_MISSING_ACTIVITY", + "ERROR_WEB_STORAGE_UNSUPPORTED", + "ERROR_QUOTA_EXCEEDED", + ) + + for (code in codes) { + val diagnostic = "Raw SDK English naming $code." + val firebaseException = object : FirebaseAuthException(code, diagnostic) {} + val authException = AuthException.from(firebaseException, strings) + + assertWithMessage(code).that(authException) + .isInstanceOf(AuthException.MisconfigurationException::class.java) + assertWithMessage(code).that(authException.message).isNotEqualTo(diagnostic) + assertWithMessage(code).that(authException.cause?.message).isEqualTo(diagnostic) + } + } + + @Test + fun `the SDK's own missing-activity exception type reaches the misconfiguration arm`() { + // The synthetic assertions elsewhere pin the `when` arm; this pins that the SDK's own + // type still reaches it, which an SDK release reparenting it would break silently. + val firebaseException = FirebaseAuthMissingActivityForRecaptchaException() + + val authException = AuthException.from(firebaseException, strings) + + assertThat(firebaseException.errorCode).isEqualTo("ERROR_MISSING_ACTIVITY") + assertThat(authException) + .isInstanceOf(AuthException.MisconfigurationException::class.java) + // Retrying cannot conjure the Activity the host never supplied. + assertThat(isRecoverable(authException)).isFalse() + assertThat(authException.message).isEqualTo(strings.unknownErrorRecoveryMessage) + assertThat(authException.message).doesNotContain("Recaptcha") + // The SDK's own English stays on the cause, where logs still reach it. + assertThat(authException.cause).isEqualTo(firebaseException) + assertThat(authException.cause?.message).contains("valid Activity is required") + } + + // ============================================================================================= + // Blanket invariant + // ============================================================================================= + + @Test + fun `no Firebase error code resolves to the raw SDK diagnostic`() { + // One representative code per named arm of the FirebaseAuthInvalidCredentialsException + // branch of from(), then, below the blank line, the codes that fall through to its `else`. + val codes = listOf( + "ERROR_INVALID_CREDENTIAL", + "ERROR_WRONG_PASSWORD", + "ERROR_INVALID_EMAIL", + "ERROR_MISSING_EMAIL", + "ERROR_MISSING_PASSWORD", + "ERROR_INVALID_PHONE_NUMBER", + "ERROR_MISSING_PHONE_NUMBER", + "ERROR_INVALID_VERIFICATION_CODE", + "ERROR_SESSION_EXPIRED", + "ERROR_INVALID_VERIFICATION_ID", + "ERROR_RETRY_PHONE_AUTH", + "ERROR_USER_MISMATCH", + "ERROR_PHONE_NUMBER_NOT_FOUND", + "ERROR_MULTI_FACTOR_INFO_NOT_FOUND", + "ERROR_MISSING_MULTI_FACTOR_INFO", + "ERROR_INVALID_MULTI_FACTOR_SESSION", + "ERROR_INVALID_CUSTOM_TOKEN", + "ERROR_MISSING_OR_INVALID_NONCE", + "ERROR_INVALID_AUTHENTICATOR_RESPONSE", + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", + + // The first ships in firebase-auth 24.2.0; the second stands in for a future code. + "ERROR_REJECTED_CREDENTIAL", + "ERROR_SOME_FUTURE_CREDENTIAL_CODE", + ) + val diagnostic = "The Firebase SDK's own untranslated English." + + for (code in codes) { + val firebaseException = + com.google.firebase.auth.FirebaseAuthInvalidCredentialsException(code, diagnostic) + assertWithMessage(code).that(resolve(firebaseException)).isNotEqualTo(diagnostic) + assertWithMessage(code).that(AuthException.from(firebaseException, strings).message) + .isNotEqualTo(diagnostic) + } + } + + @Test + fun `no plain auth error code resolves to the raw SDK diagnostic`() { + val codes = listOf( + "ERROR_UNVERIFIED_EMAIL", + "ERROR_SECOND_FACTOR_ALREADY_ENROLLED", + "ERROR_MAXIMUM_SECOND_FACTOR_COUNT_EXCEEDED", + "ERROR_OPERATION_NOT_ALLOWED", + "ERROR_UNAUTHORIZED_DOMAIN", + "ERROR_INVALID_CERT_HASH", + "ERROR_RECAPTCHA_NOT_ENABLED", + "ERROR_QUOTA_EXCEEDED", + // The else branch: neither user-facing nor a known setup problem. + "INTERNAL_ERROR", + "ERROR_WEB_INTERNAL_ERROR", + "ERROR_SOME_FUTURE_AUTH_CODE", + ) + val diagnostic = "The Firebase SDK's own untranslated English." + + for (code in codes) { + val firebaseException = object : FirebaseAuthException(code, diagnostic) {} + assertWithMessage(code).that(resolve(firebaseException)).isNotEqualTo(diagnostic) + assertWithMessage(code).that(AuthException.from(firebaseException, strings).message) + .isNotEqualTo(diagnostic) + } + } + + // ============================================================================================= + // The copy is actually translated, not just library-owned + // ============================================================================================= + + + + @Test + fun `a French device changing its email sees French, not the English reauth diagnostic`() { + val french = DefaultAuthUIStringProvider(context, Locale.FRENCH) + val firebaseException = FirebaseAuthRecentLoginRequiredException( + "ERROR_REQUIRES_RECENT_LOGIN", + recentLoginDiagnostic + ) + + val resolved = resolve(firebaseException, french) + + // `errorRecentLoginRequired` ships blank, so the arm falls to the MFA string. + assertThat(resolved).isEqualTo(french.mfaErrorRecentLoginRequired) + assertThat(resolved).isNotEqualTo(recentLoginDiagnostic) + assertThat(resolved).isNotEqualTo(strings.mfaErrorRecentLoginRequired) + } + + @Test + fun `reauthentication required resolves to library copy, not the SDK diagnostic`() { + val firebaseException = FirebaseAuthRecentLoginRequiredException( + "ERROR_REQUIRES_RECENT_LOGIN", + recentLoginDiagnostic + ) + + val result = AuthException.from(firebaseException, strings) + + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(result.message).isEqualTo(strings.mfaErrorRecentLoginRequired) + assertThat(result.cause?.message).isEqualTo(recentLoginDiagnostic) + assertThat(resolve(firebaseException)).isEqualTo(strings.mfaErrorRecentLoginRequired) + } + + // ============================================================================================= + // Developer-setup faults in the invalid-credential family + // ============================================================================================= + + @Test + fun `a bad Sign in with Apple nonce is reported as a misconfiguration, not a bad password`() { + val diagnostic = "The supplied auth credential is malformed, has expired or is " + + "currently unsupported. [ MISSING_OR_INVALID_NONCE ]" + + for (code in listOf("ERROR_MISSING_OR_INVALID_NONCE", "ERROR_INVALID_AUTHENTICATOR_RESPONSE")) { + val firebaseException = + com.google.firebase.auth.FirebaseAuthInvalidCredentialsException(code, diagnostic) + val result = AuthException.from(firebaseException, strings) + + // The host built the federated request wrong, so this must not be recoverable. + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.MisconfigurationException::class.java) + assertWithMessage(code).that(result.message) + .isEqualTo(strings.unknownErrorRecoveryMessage) + assertWithMessage(code).that(result.cause?.message).isEqualTo(diagnostic) + } + } + + @Test + fun `unnamed invalid-credential codes stay recoverable but never show the SDK diagnostic`() { + val diagnostic = "The Firebase SDK's own untranslated English." + + for (code in listOf("ERROR_REJECTED_CREDENTIAL", "ERROR_SOME_FUTURE_CREDENTIAL_CODE")) { + val firebaseException = + com.google.firebase.auth.FirebaseAuthInvalidCredentialsException(code, diagnostic) + val result = AuthException.from(firebaseException, strings) + + // "Mismatching credentials" also covers the wrong account, which signing in fixes. + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.InvalidCredentialsException::class.java) + // But the copy has to be generic — we do not know what the code means. + assertWithMessage(code).that(result.message) + .isEqualTo(strings.unknownErrorRecoveryMessage) + assertWithMessage(code).that(result.message).isNotEqualTo(diagnostic) + assertWithMessage(code).that(result.cause?.message).isEqualTo(diagnostic) + } + } + + @Test + fun `a missing passkey enrolment points at another sign-in method, not a futile retry`() { + val diagnostic = "Cannot find the passkey linked to the current account." + val firebaseException = com.google.firebase.auth.FirebaseAuthInvalidCredentialsException( + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", diagnostic + ) + + val result = AuthException.from(firebaseException, strings) + + // Not InvalidCredentialsException: that is recoverable, so the dialog would offer a retry. + assertThat(result).isInstanceOf(AuthException.SignInMethodUnavailableException::class.java) + assertThat(result).isNotInstanceOf(AuthException.InvalidCredentialsException::class.java) + // Specific copy, not the generic unknown-error string. + assertThat(result.message).isEqualTo(strings.errorPasskeyNotFound) + assertThat(result.message).isNotEqualTo(strings.unknownErrorRecoveryMessage) + assertThat(result.message).isNotEqualTo(diagnostic) + assertThat(result.cause?.message).isEqualTo(diagnostic) + } + + @Test + fun `the dialog offers no retry action for a missing passkey enrolment`() { + val firebaseException = com.google.firebase.auth.FirebaseAuthInvalidCredentialsException( + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", + "Cannot find the passkey linked to the current account." + ) + + val result = AuthException.from(firebaseException, strings) + + // The dialog renders the action button only when isRecoverable is true. + assertThat(isRecoverable(result)).isFalse() + // And the text itself is not a retry invitation, for any caller reading it directly. + assertThat(getRecoveryActionText(result, strings)).isNotEqualTo(strings.retryAction) + assertThat(getRecoveryActionText(result, strings)).isEqualTo(strings.dismissAction) + // The body still says the useful thing. + assertThat(getRecoveryMessage(result, strings)).isEqualTo(strings.errorPasskeyNotFound) + } + + // ============================================================================================= + // A host's own hook still wins + // ============================================================================================= + + @Test + fun `a host that fills the type-level hook still overrides the generic recovery copy`() { + val hostStrings = mock(AuthUIStringProvider::class.java) + whenever(hostStrings.errorNetworkGeneric).thenReturn("Host network copy") + whenever(hostStrings.networkErrorRecoveryMessage).thenReturn("Generic network copy") + + val result = AuthException.from(object : FirebaseException(networkDiagnostic) {}, hostStrings) + + assertThat(result.message).isEqualTo("Host network copy") + } + + @Test + fun `a host's credential copy does not hijack the missing-passkey message`() { + // `errorInvalidCredentials` is the hook for a type this arm deliberately does not return, + // so a host overriding both must see its passkey copy, not its password copy. + val hostStrings = mock(AuthUIStringProvider::class.java) + whenever(hostStrings.errorInvalidCredentials) + .thenReturn("Check your password and try again.") + whenever(hostStrings.errorPasskeyNotFound).thenReturn("Use another way to sign in.") + + val result = AuthException.from( + com.google.firebase.auth.FirebaseAuthInvalidCredentialsException( + "ERROR_PASSKEY_ENROLLMENT_NOT_FOUND", + "Cannot find the passkey linked to the current account." + ), + hostStrings + ) + + assertThat(result).isInstanceOf(AuthException.SignInMethodUnavailableException::class.java) + assertThat(result.message).isEqualTo("Use another way to sign in.") + assertThat(result.message).isNotEqualTo("Check your password and try again.") + } + + @Test + fun `a host that leaves the passkey hook unset gets the generic string, not credential copy`() { + // The interface default is what a host implementing AuthUIStringProvider directly sees; + // credential copy there would sit in a dialog with no retry button. + val hostStrings = mock(AuthUIStringProvider::class.java) + doCallRealMethod().whenever(hostStrings).errorPasskeyNotFound + whenever(hostStrings.errorInvalidCredentials).thenReturn("Check your password and try again.") + whenever(hostStrings.errorUnknownAuth).thenReturn("Something went wrong. Please try later.") + + assertThat(hostStrings.errorPasskeyNotFound) + .isEqualTo("Something went wrong. Please try later.") + assertThat(hostStrings.errorPasskeyNotFound) + .isNotEqualTo("Check your password and try again.") + } +} diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt index ea8ec7ecd9..47d4f07a13 100644 --- a/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/AuthExceptionTest.kt @@ -16,8 +16,12 @@ package com.firebase.ui.auth import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import com.google.firebase.FirebaseException +import com.google.firebase.FirebaseTooManyRequestsException +import com.google.firebase.auth.FirebaseAuthActionCodeException import com.google.firebase.auth.FirebaseAuthException +import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException import com.google.firebase.auth.FirebaseAuthInvalidUserException import com.google.firebase.auth.FirebaseAuthWeakPasswordException import org.junit.Test @@ -52,19 +56,35 @@ class AuthExceptionTest { } @Test - fun `from() maps FirebaseAuthException with ERROR_TOO_MANY_REQUESTS to TooManyRequestsException`() { - // Arrange - val firebaseException = object : FirebaseAuthException("ERROR_TOO_MANY_REQUESTS", "Too many requests") {} + fun `from() maps FirebaseTooManyRequestsException to TooManyRequestsException`() { + // Arrange — rate limiting arrives as this type, not as a FirebaseAuthException. It carries + // no error code, so the only thing that can select the arm is the exception class itself. + val firebaseException = FirebaseTooManyRequestsException( + "We have blocked all requests from this device due to unusual activity. Try again later." + ) // Act val authException = AuthException.from(firebaseException) - // Assert + // Assert — without a dedicated arm this falls through to `is FirebaseException` and a + // throttled user is told they have no internet connection. assertThat(authException).isInstanceOf(AuthException.TooManyRequestsException::class.java) - assertThat(authException.message).isEqualTo("Too many requests") assertThat(authException.cause).isEqualTo(firebaseException) } + @Test + fun `from() takes the too-many-requests message from the string provider`() { + val firebaseException = FirebaseTooManyRequestsException("Blocked due to unusual activity.") + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorTooManyRequests).thenReturn("Zu viele Versuche") + + val result = AuthException.from(firebaseException, stringProvider) + + assertThat(result).isInstanceOf(AuthException.TooManyRequestsException::class.java) + assertThat(result.message).isEqualTo("Zu viele Versuche") + } + + @Test fun `from() maps FirebaseAuthException with unknown error code to UnknownException`() { // Arrange @@ -289,4 +309,288 @@ class AuthExceptionTest { assertThat(exception.failingRequirements).isEqualTo(requirements) } + + // ============================================================================================= + // Per-error-code message selection + // + // Every code below is one the resolved firebase-auth 24.2.0 maps onto the exception type the + // arm matches, so each branch is reachable. The provider member is stubbed to a sentinel that + // exists nowhere else: collapsing two codes onto one branch, or dropping a branch back to the + // arm's generic `else`, changes the message and fails the test. + // ============================================================================================= + + /** The raw text the SDK would put on the exception, which must lose to the provider string. */ + private val sdkText = "The Firebase SDK's own untranslated English." + + private fun invalidCredentials(errorCode: String) = + FirebaseAuthInvalidCredentialsException(errorCode, sdkText) + + @Test + fun `from() routes each invalid-credentials error code to its own string`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorIncorrectEmailOrPassword).thenReturn("s:incorrectEmailOrPassword") + whenever(stringProvider.invalidPassword).thenReturn("s:invalidPassword") + whenever(stringProvider.invalidEmailAddress).thenReturn("s:invalidEmailAddress") + whenever(stringProvider.missingEmailAddress).thenReturn("s:missingEmailAddress") + whenever(stringProvider.requiredField).thenReturn("s:requiredField") + whenever(stringProvider.invalidPhoneNumber).thenReturn("s:invalidPhoneNumber") + whenever(stringProvider.missingPhoneNumber).thenReturn("s:missingPhoneNumber") + whenever(stringProvider.invalidVerificationCode).thenReturn("s:invalidVerificationCode") + whenever(stringProvider.errorSessionExpired).thenReturn("s:sessionExpired") + whenever(stringProvider.errorInvalidVerificationId).thenReturn("s:invalidVerificationId") + whenever(stringProvider.errorRetryPhoneAuth).thenReturn("s:retryPhoneAuth") + whenever(stringProvider.errorUserMismatch).thenReturn("s:userMismatch") + whenever(stringProvider.errorPhoneNumberNotEnrolled).thenReturn("s:phoneNumberNotEnrolled") + whenever(stringProvider.errorMultiFactorSessionExpired).thenReturn("s:multiFactorSessionExpired") + + val expected = mapOf( + "ERROR_INVALID_CREDENTIAL" to "s:incorrectEmailOrPassword", + "ERROR_WRONG_PASSWORD" to "s:invalidPassword", + "ERROR_INVALID_EMAIL" to "s:invalidEmailAddress", + "ERROR_MISSING_EMAIL" to "s:missingEmailAddress", + "ERROR_MISSING_PASSWORD" to "s:requiredField", + "ERROR_MISSING_VERIFICATION_CODE" to "s:requiredField", + "ERROR_INVALID_PHONE_NUMBER" to "s:invalidPhoneNumber", + "ERROR_MISSING_PHONE_NUMBER" to "s:missingPhoneNumber", + "ERROR_INVALID_VERIFICATION_CODE" to "s:invalidVerificationCode", + "ERROR_SESSION_EXPIRED" to "s:sessionExpired", + "ERROR_INVALID_VERIFICATION_ID" to "s:invalidVerificationId", + "ERROR_MISSING_VERIFICATION_ID" to "s:invalidVerificationId", + "ERROR_RETRY_PHONE_AUTH" to "s:retryPhoneAuth", + "ERROR_USER_MISMATCH" to "s:userMismatch", + "ERROR_PHONE_NUMBER_NOT_FOUND" to "s:phoneNumberNotEnrolled", + "ERROR_MULTI_FACTOR_INFO_NOT_FOUND" to "s:phoneNumberNotEnrolled", + "ERROR_INVALID_MULTI_FACTOR_SESSION" to "s:multiFactorSessionExpired", + "ERROR_MISSING_MULTI_FACTOR_SESSION" to "s:multiFactorSessionExpired", + ) + + val actual = expected.keys.associateWith { code -> + val result = AuthException.from(invalidCredentials(code), stringProvider) + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + result.message + } + + assertThat(actual).containsExactlyEntriesIn(expected) + } + + @Test + fun `from() uses ERROR_INVALID_CREDENTIAL copy that does not blame the password`() { + // Under email enumeration protection this single code covers wrong password AND no such + // account, so reusing the wrong-password string would state something false. + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorIncorrectEmailOrPassword).thenReturn("Email or password wrong") + whenever(stringProvider.invalidPassword).thenReturn("Incorrect password.") + + val result = AuthException.from(invalidCredentials("ERROR_INVALID_CREDENTIAL"), stringProvider) + + assertThat(result.message).isEqualTo("Email or password wrong") + } + + @Test + fun `from() prefers the blank-able type-level hook over the per-code string`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorInvalidCredentials).thenReturn("Host-wide override") + whenever(stringProvider.invalidPassword).thenReturn("Incorrect password.") + + val result = AuthException.from(invalidCredentials("ERROR_WRONG_PASSWORD"), stringProvider) + + assertThat(result.message).isEqualTo("Host-wide override") + } + + @Test + fun `from() falls back to the Firebase message when the per-code string is blank`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorInvalidCredentials).thenReturn("") + whenever(stringProvider.invalidPassword).thenReturn("") + + val result = AuthException.from(invalidCredentials("ERROR_WRONG_PASSWORD"), stringProvider) + + assertThat(result.message).isEqualTo(sdkText) + } + + @Test + fun `from() routes custom token codes to MisconfigurationException`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.unknownErrorRecoveryMessage).thenReturn("Une erreur est survenue") + + for (code in listOf("ERROR_INVALID_CUSTOM_TOKEN", "ERROR_CUSTOM_TOKEN_MISMATCH")) { + val firebaseException = invalidCredentials(code) + val result = AuthException.from(firebaseException, stringProvider) + assertThat(result).isInstanceOf(AuthException.MisconfigurationException::class.java) + // The diagnostic names the developer's own token backend; it is renderable nowhere. + assertWithMessage(code).that(result.message).isEqualTo("Une erreur est survenue") + assertWithMessage(code).that(result.cause).isEqualTo(firebaseException) + assertWithMessage(code).that(result.cause?.message).isEqualTo(sdkText) + } + } + + @Test + fun `from() routes expired user tokens to the session-expired copy`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorMultiFactorSessionExpired).thenReturn("Session gone") + + for (code in listOf("ERROR_INVALID_USER_TOKEN", "ERROR_USER_TOKEN_EXPIRED")) { + val firebaseException = FirebaseAuthInvalidUserException(code, sdkText) + val result = AuthException.from(firebaseException, stringProvider) + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(result.message).isEqualTo("Session gone") + } + } + + @Test + fun `from() routes action code failures to the action-code copy`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorActionCodeInvalid).thenReturn("Link no longer valid") + + for (code in listOf("ERROR_EXPIRED_ACTION_CODE", "ERROR_INVALID_ACTION_CODE")) { + val firebaseException = FirebaseAuthActionCodeException(code, sdkText) + val result = AuthException.from(firebaseException, stringProvider) + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(result.message).isEqualTo("Link no longer valid") + } + } + + @Test + fun `from() routes the user-facing plain auth codes to their own strings`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorUnverifiedEmail).thenReturn("s:unverifiedEmail") + whenever(stringProvider.errorSecondFactorAlreadyEnrolled).thenReturn("s:alreadyEnrolled") + whenever(stringProvider.errorMaximumSecondFactorCountExceeded).thenReturn("s:maxFactors") + + val expected = mapOf( + "ERROR_UNVERIFIED_EMAIL" to "s:unverifiedEmail", + "ERROR_SECOND_FACTOR_ALREADY_ENROLLED" to "s:alreadyEnrolled", + "ERROR_MAXIMUM_SECOND_FACTOR_COUNT_EXCEEDED" to "s:maxFactors", + ) + + val actual = expected.keys.associateWith { code -> + val result = AuthException.from(object : FirebaseAuthException(code, sdkText) {}, stringProvider) + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + result.message + } + + assertThat(actual).containsExactlyEntriesIn(expected) + } + + @Test + fun `from() routes developer setup codes to MisconfigurationException, diagnostic on the cause`() { + val configurationCodes = listOf( + "ERROR_OPERATION_NOT_ALLOWED", + "ERROR_APP_NOT_AUTHORIZED", + "ERROR_UNAUTHORIZED_DOMAIN", + "ERROR_MISSING_CONTINUE_URI", + "ERROR_INVALID_CERT_HASH", + "ERROR_DYNAMIC_LINK_NOT_ACTIVATED", + "ERROR_INVALID_DYNAMIC_LINK_DOMAIN", + "ERROR_INVALID_HOSTING_LINK_DOMAIN", + "ERROR_INVALID_PROVIDER_ID", + "ERROR_ADMIN_RESTRICTED_OPERATION", + "ERROR_UNSUPPORTED_FIRST_FACTOR", + "ERROR_UNSUPPORTED_PASSTHROUGH_OPERATION", + "ERROR_INVALID_REQ_TYPE", + "ERROR_WEB_CONTEXT_ALREADY_PRESENTED", + "ERROR_INVALID_TENANT_ID", + "ERROR_TENANT_ID_MISMATCH", + "ERROR_UNSUPPORTED_TENANT_OPERATION", + "ERROR_RECAPTCHA_NOT_ENABLED", + "ERROR_CAPTCHA_CHECK_FAILED", + "ERROR_MISSING_RECAPTCHA_TOKEN", + "ERROR_INVALID_RECAPTCHA_TOKEN", + "ERROR_INVALID_RECAPTCHA_ACTION", + "ERROR_MISSING_RECAPTCHA_VERSION", + "ERROR_INVALID_RECAPTCHA_VERSION", + "ERROR_MISSING_CLIENT_TYPE", + "ERROR_MISSING_CLIENT_IDENTIFIER", + "ERROR_ALTERNATE_CLIENT_IDENTIFIER_REQUIRED", + // Email-template settings in the Firebase console. + "ERROR_INVALID_MESSAGE_PAYLOAD", + "ERROR_INVALID_SENDER", + "ERROR_INVALID_RECIPIENT_EMAIL", + // Host integration and project quota. The synthetic exception below pins the `when` + // arm; AuthExceptionRecoveryResolutionTest drives the SDK's own missing-activity type. + "ERROR_MISSING_ACTIVITY", + "ERROR_WEB_STORAGE_UNSUPPORTED", + "ERROR_QUOTA_EXCEEDED", + ) + // EmailAuthScreen and PhoneAuthScreen render `exception.message` inline without going + // through getRecoveryMessage, so the diagnostic must not be on the message at all. It + // stays on the cause, where logs and `exception.cause?.message` still reach it. + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorUnknownAuth).thenReturn("Generic unknown error") + whenever(stringProvider.unknownErrorRecoveryMessage).thenReturn("Une erreur est survenue") + + for (code in configurationCodes) { + val firebaseException = object : FirebaseAuthException(code, sdkText) {} + val result = AuthException.from(firebaseException, stringProvider) + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.MisconfigurationException::class.java) + assertWithMessage(code).that(result.message).isEqualTo("Une erreur est survenue") + assertWithMessage(code).that(result.cause).isEqualTo(firebaseException) + assertWithMessage(code).that(result.cause?.message).isEqualTo(sdkText) + } + } + + @Test + fun `from() keeps internal errors out of MisconfigurationException`() { + // INTERNAL_ERROR is what the SDK falls back to for a status it does not recognise, and + // ERROR_WEB_INTERNAL_ERROR is a backend fault. Neither is a setup problem. + for (code in listOf("INTERNAL_ERROR", "ERROR_WEB_INTERNAL_ERROR")) { + val result = AuthException.from(object : FirebaseAuthException(code, sdkText) {}) + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.UnknownException::class.java) + } + } + + @Test + fun `from() maps ERROR_USER_CANCELLED to AuthCancelledException`() { + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorAuthCancelled).thenReturn("You cancelled") + + for (code in listOf("ERROR_USER_CANCELLED", "ERROR_WEB_CONTEXT_CANCELED")) { + val result = AuthException.from(object : FirebaseAuthException(code, sdkText) {}, stringProvider) + assertWithMessage(code).that(result) + .isInstanceOf(AuthException.AuthCancelledException::class.java) + assertWithMessage(code).that(result.message).isEqualTo("You cancelled") + } + } + + @Test + fun `new AuthUIStringProvider members compile to real JVM default methods`() { + // Decision: every new member must be source- AND binary-compatible, so a host that + // implemented the interface before this change still compiles and still links. An + // abstract JVM method here would break every existing implementor at runtime. + val newMembers = listOf( + "getErrorIncorrectEmailOrPassword", + "getErrorInvalidVerificationId", + "getErrorRetryPhoneAuth", + "getErrorUserMismatch", + "getErrorPhoneNumberNotEnrolled", + "getErrorSessionExpired", + "getErrorMultiFactorSessionExpired", + "getErrorActionCodeInvalid", + "getErrorUnverifiedEmail", + "getErrorSecondFactorAlreadyEnrolled", + "getErrorMaximumSecondFactorCountExceeded", + ) + + for (name in newMembers) { + val method = AuthUIStringProvider::class.java.getMethod(name) + assertWithMessage(name).that(method.isDefault).isTrue() + } + } + @Test + fun `an expired user token honours the credentials hook, not the account-generic one`() { + // It produces an InvalidCredentialsException, so errorUserAccountGeneric — the hook the + // UserNotFoundException arm below it uses — must not win. + val stringProvider = mock(AuthUIStringProvider::class.java) + whenever(stringProvider.errorInvalidCredentials).thenReturn("Custom: credentials") + whenever(stringProvider.errorUserAccountGeneric).thenReturn("Custom: account generic") + val firebaseException = FirebaseAuthInvalidUserException("ERROR_USER_TOKEN_EXPIRED", "x") + + val result = AuthException.from(firebaseException, stringProvider) + + assertThat(result).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(result.message).isEqualTo("Custom: credentials") + } + } \ No newline at end of file diff --git a/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthUITest.kt index 9bbbb16087..105a0d2a8b 100644 --- a/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthUITest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthUITest.kt @@ -41,6 +41,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.anyString +import org.mockito.Mockito.doAnswer import org.mockito.Mockito.doNothing import org.mockito.Mockito.doThrow import org.mockito.Mockito.mock @@ -436,7 +437,6 @@ class FirebaseAuthUITest { val mockUser = mock(FirebaseUser::class.java) val mockUserInfo = mock(UserInfo::class.java) `when`(mockUserInfo.providerId).thenReturn("google.com") - `when`(mockUser.providerId).thenReturn("google.com") `when`(mockUser.providerData).thenReturn(listOf(mockUserInfo)) // Setup mock auth @@ -484,7 +484,6 @@ class FirebaseAuthUITest { val mockUser = mock(FirebaseUser::class.java) val mockUserInfo = mock(UserInfo::class.java) `when`(mockUserInfo.providerId).thenReturn("facebook.com") - `when`(mockUser.providerId).thenReturn("facebook.com") `when`(mockUser.providerData).thenReturn(listOf(mockUserInfo)) // Setup mock auth @@ -524,7 +523,6 @@ class FirebaseAuthUITest { val mockUser = mock(FirebaseUser::class.java) val mockUserInfo = mock(UserInfo::class.java) `when`(mockUserInfo.providerId).thenReturn("password") - `when`(mockUser.providerId).thenReturn("password") `when`(mockUser.providerData).thenReturn(listOf(mockUserInfo)) // Setup mock auth @@ -580,6 +578,87 @@ class FirebaseAuthUITest { verify(mockAuth).signOut() } + @Test + fun `signOut() calls Facebook sign out even though FirebaseAuth clears the user first`() = + runTest { + // Setup mock user with Facebook provider + val mockUser = mock(FirebaseUser::class.java) + val mockUserInfo = mock(UserInfo::class.java) + `when`(mockUserInfo.providerId).thenReturn("facebook.com") + `when`(mockUser.providerData).thenReturn(listOf(mockUserInfo)) + + // Setup mock auth that clears currentUser on signOut(), like the real FirebaseAuth + val mockAuth = mock(FirebaseAuth::class.java) + var signedOut = false + `when`(mockAuth.currentUser).thenAnswer { if (signedOut) null else mockUser } + doAnswer { signedOut = true; null }.`when`(mockAuth).signOut() + + var facebookSignOutCalled = false + val mockLoginManagerProvider = object : AuthProvider.Facebook.LoginManagerProvider { + override fun getCredential(token: String): com.google.firebase.auth.AuthCredential { + throw UnsupportedOperationException("Not used in this test") + } + + override fun logOut() { + facebookSignOutCalled = true + } + } + + val instance = FirebaseAuthUI.create(defaultApp, mockAuth) + instance.testLoginManagerProvider = mockLoginManagerProvider + val context = ApplicationProvider.getApplicationContext() + + instance.signOut(context) + + assertThat(facebookSignOutCalled).isTrue() + assertThat(mockAuth.currentUser).isNull() + } + + @Test + fun `signOut() reads linked providers from providerData not FirebaseUser providerId`() = + runTest { + // The real FirebaseUser.providerId is always "firebase"; the per-provider ids live in + // providerData. + val mockUser = mock(FirebaseUser::class.java) + val mockUserInfo = mock(UserInfo::class.java) + `when`(mockUserInfo.providerId).thenReturn("google.com") + `when`(mockUser.providerId).thenReturn("firebase") + `when`(mockUser.providerData).thenReturn(listOf(mockUserInfo)) + + val mockAuth = mock(FirebaseAuth::class.java) + `when`(mockAuth.currentUser).thenReturn(mockUser) + doNothing().`when`(mockAuth).signOut() + + var googleSignOutCalled = false + val mockCredentialManagerProvider = + object : AuthProvider.Google.CredentialManagerProvider { + override suspend fun getGoogleCredential( + context: Context, + credentialManager: androidx.credentials.CredentialManager, + serverClientId: String, + filterByAuthorizedAccounts: Boolean, + autoSelectEnabled: Boolean, + ): AuthProvider.Google.GoogleSignInResult { + throw UnsupportedOperationException("Not used in this test") + } + + override suspend fun clearCredentialState( + context: Context, + credentialManager: androidx.credentials.CredentialManager, + ) { + googleSignOutCalled = true + } + } + + val instance = FirebaseAuthUI.create(defaultApp, mockAuth) + instance.testCredentialManagerProvider = mockCredentialManagerProvider + val context = ApplicationProvider.getApplicationContext() + + instance.signOut(context) + + assertThat(googleSignOutCalled).isTrue() + } + // ============================================================================================= // Delete Account Tests // ============================================================================================= diff --git a/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt b/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt new file mode 100644 index 0000000000..7247fc6faf --- /dev/null +++ b/auth/src/test/java/com/firebase/ui/auth/PasswordPolicyMessageLocalizationTest.kt @@ -0,0 +1,311 @@ +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.firebase.ui.auth + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.ui.components.getRecoveryMessage +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import com.google.firebase.FirebaseException +import com.google.firebase.auth.FirebaseAuthWeakPasswordException +import java.util.Locale +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * Covers the Google Identity Platform password-policy path. + * + * The fixtures are the real backend strings, captured from a project with `Require` enforcement + * and a minimum length of 10. + * + * @suppress Internal test class + */ +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) +class PasswordPolicyMessageLocalizationTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + private val strings: AuthUIStringProvider = DefaultAuthUIStringProvider(context) + private val french: AuthUIStringProvider = DefaultAuthUIStringProvider(context, Locale.FRENCH) + + // ============================================================================================= + // The real backend strings + // ============================================================================================= + + private companion object { + /** Verbatim single-constraint probe responses. */ + const val MIN_LENGTH = "Password must contain at least 10 characters" + const val UPPER_CASE = "Password must contain an upper case character" + const val LOWER_CASE = "Password must contain a lower case character" + const val NUMERIC = "Password must contain a numeric character" + + /** + * Unverified: the probe project has special characters disabled. GIdP's wording is + * "non-alphanumeric", which is where the substring collision with [NUMERIC] comes from. + */ + const val NON_ALPHANUMERIC = "Password must contain a non-alphanumeric character" + + /** All failing requirements come back together, comma-separated, inside one bracket. */ + const val ALL_THREE = + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS : Missing password requirements: " + + "[$MIN_LENGTH, $UPPER_CASE, $NUMERIC]" + + /** + * The marker with nothing parseable after it. `parsePasswordPolicyRequirements` returns an + * empty list, and the whole message is then the generic string. + */ + const val UNPARSEABLE = "PASSWORD_DOES_NOT_MEET_REQUIREMENTS" + + val ENGLISH_REQUIREMENT_SENTENCES = + listOf(MIN_LENGTH, UPPER_CASE, LOWER_CASE, NUMERIC) + } + + /** The two exception shapes that carry a policy rejection into `from()`. */ + private fun shapesFor(sourceText: String): List> = listOf( + // `from()` reads `reason` first for this type. + "FirebaseAuthWeakPasswordException" to + FirebaseAuthWeakPasswordException("ERROR_WEAK_PASSWORD", sourceText, sourceText), + "FirebaseException" to FirebaseException(sourceText), + ) + + // ============================================================================================= + // Untranslated English reaching the dialog + // ============================================================================================= + + @Test + fun `a parsed policy rejection never renders the backend's English sentences`() { + for ((shape, exception) in shapesFor(ALL_THREE)) { + val rendered = getRecoveryMessage(AuthException.from(exception, strings), strings) + + for (sentence in ENGLISH_REQUIREMENT_SENTENCES) { + assertWithMessage("%s -> dialog body still contains %s", shape, sentence) + .that(rendered).doesNotContain(sentence) + } + assertWithMessage("%s -> dialog body is blank", shape) + .that(rendered.isBlank()).isFalse() + } + } + + @Test + fun `an unparseable policy rejection never renders the old hardcoded literal`() { + for ((shape, exception) in shapesFor(UNPARSEABLE)) { + val rendered = getRecoveryMessage(AuthException.from(exception, strings), strings) + + // `errorWeakPasswordGeneric` is the host's hook and ships blank. + assertWithMessage("%s -> dialog body fell back to the hardcoded literal", shape) + .that(rendered).doesNotContain("Password does not meet policy requirements") + assertWithMessage("%s -> dialog body", shape) + .that(rendered).isEqualTo(strings.errorPasswordPolicyGeneric) + } + } + + @Test + fun `both policy paths resolve to French on a French device`() { + for (sourceText in listOf(ALL_THREE, UNPARSEABLE)) { + for ((shape, exception) in shapesFor(sourceText)) { + val rendered = getRecoveryMessage(AuthException.from(exception, french), french) + + for (sentence in ENGLISH_REQUIREMENT_SENTENCES) { + assertWithMessage("%s -> French dialog body contains %s", shape, sentence) + .that(rendered).doesNotContain(sentence) + } + assertWithMessage("%s -> French dialog body is not French", shape) + .that(rendered).contains("mot de passe") + } + } + } + + // ============================================================================================= + // What the mapping actually produces + // ============================================================================================= + + @Test + fun `each requirement maps to the library's own translated copy`() { + val rendered = getRecoveryMessage( + AuthException.from(FirebaseException(ALL_THREE), strings), strings + ) + + // The project's own minimum, read back out of the sentence — not the 6 that + // `weakPasswordRecoveryMessage` hardcodes. + assertThat(rendered).contains(strings.passwordTooShort(10)) + assertThat(rendered).contains(strings.passwordMissingUppercase) + assertThat(rendered).contains(strings.passwordMissingDigit) + // Only the three that failed. + assertThat(rendered).doesNotContain(strings.passwordMissingLowercase) + assertThat(rendered.lines()).hasSize(3) + } + + @Test + fun `the lower case requirement maps too`() { + // Not in ALL_THREE, so it needs its own fixture to be covered at all. + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException("PASSWORD_DOES_NOT_MEET_REQUIREMENTS: [$LOWER_CASE]"), strings + ), + strings + ) + + assertThat(rendered).isEqualTo(strings.passwordMissingLowercase) + } + + @Test + fun `the non-alphanumeric requirement maps to the special-character copy, not the digit one`() { + // "numeric" is a substring of "non-alphanumeric", so the digit arm can swallow this. + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException("PASSWORD_DOES_NOT_MEET_REQUIREMENTS: [$NON_ALPHANUMERIC]"), + strings + ), + strings + ) + + assertThat(rendered).isEqualTo(strings.passwordMissingSpecialCharacter) + assertThat(rendered).isNotEqualTo(strings.passwordMissingDigit) + } + + @Test + fun `the digit and special-character requirements stay distinct when both fail`() { + // The pair must resolve to two different strings whichever arm is tested first. + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: [$NUMERIC, $NON_ALPHANUMERIC]" + ), + strings + ), + strings + ) + + assertThat(rendered.lines()).containsExactly( + strings.passwordMissingDigit, + strings.passwordMissingSpecialCharacter, + ).inOrder() + } + + @Test + fun `the spelled-out special character wording maps too`() { + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: " + + "[Password must contain a special character]" + ), + strings + ), + strings + ) + + assertThat(rendered).isEqualTo(strings.passwordMissingSpecialCharacter) + } + + @Test + fun `an exclusive maximum-length requirement states the maximum, not the bound`() { + // "fewer than 4096" permits 4095, and passwordTooLong renders the maximum, not the bound. + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: " + + "[Password must contain fewer than 4096 characters]" + ), + strings + ), + strings + ) + + assertThat(rendered).isEqualTo(strings.passwordTooLong(4095)) + assertThat(rendered).isNotEqualTo(strings.passwordTooLong(4096)) + } + + @Test + fun `an inclusive maximum-length requirement takes the number as written`() { + // "at most N" and "no more than N" are inclusive, so no adjustment applies. + for (wording in listOf("at most 64", "no more than 64")) { + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: " + + "[Password must contain $wording characters]" + ), + strings + ), + strings + ) + + assertWithMessage(wording).that(rendered).isEqualTo(strings.passwordTooLong(64)) + } + } + + @Test + fun `an unrecognised requirement is kept verbatim rather than dropped`() { + val reworded = "Password must not be one of your last 5 passwords" + val rendered = getRecoveryMessage( + AuthException.from( + FirebaseException( + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS: [$UPPER_CASE, $reworded]" + ), + strings + ), + strings + ) + + // The known one is still translated... + assertThat(rendered).contains(strings.passwordMissingUppercase) + // ...and the unknown one is kept verbatim instead of vanishing or blanking the message. + assertThat(rendered).contains(reworded) + } + + @Test + fun `failingRequirements keeps the raw untranslated sentences`() { + val exception = AuthException.from(FirebaseException(ALL_THREE), french) + + assertThat(exception).isInstanceOf(AuthException.PasswordPolicyViolationException::class.java) + val policy = exception as AuthException.PasswordPolicyViolationException + + // This list stays raw even when `message` is French. + assertThat(policy.failingRequirements) + .containsExactly(MIN_LENGTH, UPPER_CASE, NUMERIC).inOrder() + assertThat(policy.message).doesNotContain(MIN_LENGTH) + } + + @Test + fun `a policy rejection stays a PasswordPolicyViolationException, not a WeakPasswordException`() { + for ((shape, exception) in shapesFor(ALL_THREE)) { + assertWithMessage("%s", shape).that(AuthException.from(exception, strings)) + .isInstanceOf(AuthException.PasswordPolicyViolationException::class.java) + } + + // A plain weak-password rejection must not be pulled into the policy type. + val plain = FirebaseAuthWeakPasswordException( + "ERROR_WEAK_PASSWORD", "Password should be at least 6 characters", + "Password should be at least 6 characters" + ) + assertThat(AuthException.from(plain, strings)) + .isInstanceOf(AuthException.WeakPasswordException::class.java) + } + + @Test + fun `a null string provider still leaves the backend sentences readable`() { + // Nothing to resolve against, so the raw sentences are the only output. + val resolved = AuthException.from(FirebaseException(ALL_THREE), null as AuthUIStringProvider?) + + assertThat(resolved.message).contains(MIN_LENGTH) + assertThat(resolved.message).contains(UPPER_CASE) + } +} diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProviderFirebaseAuthUITest.kt index 937f698d0f..72ece3340d 100644 --- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProviderFirebaseAuthUITest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProviderFirebaseAuthUITest.kt @@ -23,6 +23,8 @@ import com.firebase.ui.auth.AuthState import com.firebase.ui.auth.FirebaseAuthUI import com.firebase.ui.auth.configuration.AuthUIConfiguration import com.firebase.ui.auth.configuration.authUIConfiguration +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider import com.google.android.gms.tasks.TaskCompletionSource import com.google.common.truth.Truth.assertThat import com.google.firebase.FirebaseApp @@ -422,4 +424,53 @@ class AnonymousAuthProviderFirebaseAuthUITest { ArgumentMatchers.anyString() ) } + + // ============================================================================================= + // Error message routing — the configured AuthUIStringProvider, not the device + // ============================================================================================= + + @Test + fun `signInAnonymously - failure message comes from the configured string provider`() = + runTest { + // "A network error has occurred", in Japanese. + val localizedMessage = "ネットワークエラーが発生しました" + val networkException = FirebaseNetworkException("A network error has occurred.") + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(networkException) + `when`(mockFirebaseAuth.signInAnonymously()).thenReturn(taskCompletionSource.task) + + val localizedConfig = authUIConfiguration { + context = applicationContext + providers { + provider(AuthProvider.Anonymous) + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorNetworkGeneric: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(localizedConfig).signInAnonymously() + } catch (t: Throwable) { + thrown = t + } + + // Without the configured provider the conversion keeps Firebase's own English text. + assertThat(thrown).isInstanceOf(AuthException.NetworkException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } } diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt index 4cb63bc4f9..31058c2487 100644 --- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt @@ -24,6 +24,9 @@ import com.firebase.ui.auth.FirebaseAuthUI import com.firebase.ui.auth.configuration.AuthUIConfiguration import com.firebase.ui.auth.configuration.PasswordRule import com.firebase.ui.auth.configuration.authUIConfiguration +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.ui.components.getRecoveryMessage import com.firebase.ui.auth.util.EmailLinkPersistenceManager import com.firebase.ui.auth.util.MockPersistenceManager import com.google.android.gms.tasks.TaskCompletionSource @@ -31,13 +34,16 @@ import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import com.google.firebase.FirebaseApp import com.google.firebase.FirebaseOptions +import com.google.firebase.FirebaseTooManyRequestsException import com.google.firebase.auth.ActionCodeSettings import com.google.firebase.auth.AuthCredential import com.google.firebase.auth.AuthResult import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.auth.FirebaseAuthException import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException import com.google.firebase.auth.FirebaseAuthInvalidUserException import com.google.firebase.auth.FirebaseAuthUserCollisionException +import com.google.firebase.auth.FirebaseAuthWeakPasswordException import com.google.firebase.auth.FirebaseUser import com.google.firebase.auth.GoogleAuthProvider import com.google.firebase.auth.SignInMethodQueryResult @@ -2030,4 +2036,498 @@ class EmailAuthProviderFirebaseAuthUITest { val state = instance.authStateFlow().first { it !is AuthState.Loading } assertThat(state).isEqualTo(AuthState.Success(result = mockAuthResult, user = mockUser, isNewUser = false)) } + + // ============================================================================================= + // Error message routing — the configured AuthUIStringProvider, not the device + // + // Every test here overrides exactly one member of the provider and asserts that string comes + // back on both the thrown exception and the emitted AuthState.Error. Reverting the call site + // to `AuthException.from(e)` or `AuthException.from(e, context)` leaves Firebase's own English + // text in place and fails the test. + // ============================================================================================= + + /** A provider whose only difference from the default is [errorWeakPasswordGeneric]. */ + private fun weakPasswordProvider(message: String): AuthUIStringProvider = + object : AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorWeakPasswordGeneric: String = message + } + + @Test + fun `createOrLinkUserWithEmailAndPassword - weak password message comes from the configured string provider`() = + runTest { + // "The password is too weak", in Japanese. + val localizedMessage = "パスワードが弱すぎます" + val weakPasswordException = FirebaseAuthWeakPasswordException( + "ERROR_WEAK_PASSWORD", + "The given password is invalid.", + "Password should be at least 6 characters" + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(weakPasswordException) + `when`(mockFirebaseAuth.currentUser).thenReturn(null) + `when`( + mockFirebaseAuth.createUserWithEmailAndPassword( + "test@example.com", + "Pass@123" + ) + ).thenReturn(taskCompletionSource.task) + + val emailProvider = AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + val config = authUIConfiguration { + context = applicationContext + providers { provider(emailProvider) } + stringProvider = weakPasswordProvider(localizedMessage) + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).createOrLinkUserWithEmailAndPassword( + context = applicationContext, + provider = emailProvider, + name = null, + email = "test@example.com", + password = "Pass@123" + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.WeakPasswordException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `createOrLinkUserWithEmailAndPassword - policy violation with no listed requirements uses the configured string provider`() = + runTest { + // "The password does not meet the requirements", in Japanese. + val localizedMessage = "パスワードが要件を満たしていません" + // No bracketed requirement list, so the message has to come from the provider. + val policyException = FirebaseAuthWeakPasswordException( + "ERROR_WEAK_PASSWORD", + "The given password is invalid.", + "PASSWORD_DOES_NOT_MEET_REQUIREMENTS" + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(policyException) + `when`(mockFirebaseAuth.currentUser).thenReturn(null) + `when`( + mockFirebaseAuth.createUserWithEmailAndPassword( + "test@example.com", + "Pass@123" + ) + ).thenReturn(taskCompletionSource.task) + + val emailProvider = AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + val config = authUIConfiguration { + context = applicationContext + providers { provider(emailProvider) } + stringProvider = weakPasswordProvider(localizedMessage) + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).createOrLinkUserWithEmailAndPassword( + context = applicationContext, + provider = emailProvider, + name = null, + email = "test@example.com", + password = "Pass@123" + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown) + .isInstanceOf(AuthException.PasswordPolicyViolationException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `signInWithEmailAndPassword - user-not-found message comes from the configured string provider`() = + runTest { + // "No account was found for that email address", in Japanese. + val localizedMessage = "そのメールアドレスのアカウントは見つかりませんでした" + val userNotFoundException = FirebaseAuthInvalidUserException( + "ERROR_USER_NOT_FOUND", + "User not found" + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(userNotFoundException) + `when`(mockFirebaseAuth.signInWithEmailAndPassword("test@example.com", "Pass@123")) + .thenReturn(taskCompletionSource.task) + + val config = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorUserNotFound: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithEmailAndPassword( + context = applicationContext, + email = "test@example.com", + password = "Pass@123" + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.UserNotFoundException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `signInAndLinkWithCredential - failure message comes from the configured string provider`() = + runTest { + // "Those credentials are not valid", in Japanese. + val localizedMessage = "その認証情報は有効ではありません" + val credential = GoogleAuthProvider.getCredential("google-id-token", null) + val invalidCredentialsException = FirebaseAuthInvalidCredentialsException( + "ERROR_INVALID_CREDENTIAL", + "Invalid credential" + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(invalidCredentialsException) + `when`(mockFirebaseAuth.currentUser).thenReturn(null) + `when`(mockFirebaseAuth.signInWithCredential(credential)) + .thenReturn(taskCompletionSource.task) + + val config = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorInvalidCredentials: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).signInAndLinkWithCredential(credential) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `sendSignInLinkToEmail - failure message comes from the configured string provider`() = + runTest { + // "Too many attempts. Please try again later", in Japanese. + val localizedMessage = "試行回数が多すぎます。しばらくしてからもう一度お試しください" + `when`(mockFirebaseAuth.currentUser).thenReturn(null) + // Rate limiting is a FirebaseTooManyRequestsException, not a FirebaseAuthException: + // it is not an auth exception at all and carries no error code. + val tooManyRequests = FirebaseTooManyRequestsException( + "We have blocked all requests from this device due to unusual activity." + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(tooManyRequests) + `when`(mockFirebaseAuth.sendSignInLinkToEmail(anyString(), any())) + .thenReturn(taskCompletionSource.task) + + val provider = AuthProvider.Email( + isEmailLinkSignInEnabled = true, + emailLinkActionCodeSettings = ActionCodeSettings.newBuilder() + .setUrl("https://example.com") + .setHandleCodeInApp(true) + .setAndroidPackageName("com.test", true, null) + .build(), + passwordValidationRules = emptyList() + ) + val config = authUIConfiguration { + context = applicationContext + providers { provider(provider) } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorTooManyRequests: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).sendSignInLinkToEmail( + context = applicationContext, + provider = provider, + email = "test@example.com", + credentialForLinking = null + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.TooManyRequestsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `sendPasswordResetEmail - failure message comes from the configured string provider`() = + runTest { + // "This account has been disabled", in Japanese. + val localizedMessage = "このアカウントは無効になっています" + val disabledException = FirebaseAuthInvalidUserException( + "ERROR_USER_DISABLED", + "The user account has been disabled by an administrator." + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(disabledException) + `when`( + mockFirebaseAuth.sendPasswordResetEmail( + ArgumentMatchers.eq("test@example.com"), + ArgumentMatchers.isNull() + ) + ).thenReturn(taskCompletionSource.task) + + val config = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorUserDisabled: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).sendPasswordResetEmail("test@example.com") + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `signInWithEmailAndPassword - wrong password uses the per-code string, not the invalid-credentials one`() = + runTest { + // "The password is incorrect", in Japanese. ERROR_WRONG_PASSWORD used to share one flat + // arm with every other invalid-credential code, so this string was unreachable. + val localizedMessage = "パスワードが正しくありません" + val wrongPassword = FirebaseAuthInvalidCredentialsException( + "ERROR_WRONG_PASSWORD", + "The password is invalid or the user does not have a password. [ INVALID_PASSWORD ]" + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(wrongPassword) + `when`(mockFirebaseAuth.signInWithEmailAndPassword("test@example.com", "Pass@123")) + .thenReturn(taskCompletionSource.task) + + val config = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val invalidPassword: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithEmailAndPassword( + context = applicationContext, + email = "test@example.com", + password = "Pass@123" + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `signInWithEmailAndPassword - merged invalid-credential code does not blame the password`() = + runTest { + // With email enumeration protection on, a wrong password and a nonexistent account both + // arrive as ERROR_INVALID_CREDENTIAL, so "Incorrect password" would be a false claim. + val incorrectEmailOrPassword = "That email or password isn't correct" + val invalidCredential = FirebaseAuthInvalidCredentialsException( + "ERROR_INVALID_CREDENTIAL", + "The supplied auth credential is incorrect, malformed or has expired." + ) + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(invalidCredential) + `when`(mockFirebaseAuth.signInWithEmailAndPassword("test@example.com", "Pass@123")) + .thenReturn(taskCompletionSource.task) + + val config = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithEmailAndPassword( + context = applicationContext, + email = "test@example.com", + password = "Pass@123" + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(incorrectEmailOrPassword) + assertThat(thrown).hasMessageThat() + .isNotEqualTo(applicationContext.getString(R.string.fui_error_invalid_password)) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(incorrectEmailOrPassword) + } + + @Test + fun `sendSignInLinkToEmail - a disabled provider surfaces as MisconfigurationException`() = + runTest { + // The user can do nothing about a provider left disabled in the Firebase console, so + // the raw diagnostic stays on the cause for logs and never reaches the message. + val rawDiagnostic = "This operation is not allowed. This may be because the given " + + "sign-in provider is disabled for this Firebase project. [ OPERATION_NOT_ALLOWED ]" + `when`(mockFirebaseAuth.currentUser).thenReturn(null) + val notAllowed = + object : FirebaseAuthException("ERROR_OPERATION_NOT_ALLOWED", rawDiagnostic) {} + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException(notAllowed) + `when`(mockFirebaseAuth.sendSignInLinkToEmail(anyString(), any())) + .thenReturn(taskCompletionSource.task) + + val provider = AuthProvider.Email( + isEmailLinkSignInEnabled = true, + emailLinkActionCodeSettings = ActionCodeSettings.newBuilder() + .setUrl("https://example.com") + .setHandleCodeInApp(true) + .setAndroidPackageName("com.test", true, null) + .build(), + passwordValidationRules = emptyList() + ) + val config = authUIConfiguration { + context = applicationContext + providers { provider(provider) } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).sendSignInLinkToEmail( + context = applicationContext, + provider = provider, + email = "test@example.com", + credentialForLinking = null + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.MisconfigurationException::class.java) + // EmailAuthScreen renders `exception.message` inline, so the message itself has to be + // clean; the diagnostic is still reachable as `exception.cause?.message`. + assertThat(thrown).hasMessageThat().doesNotContain("Firebase") + assertThat(thrown).hasMessageThat().doesNotContain("OPERATION_NOT_ALLOWED") + assertThat(thrown?.cause).isEqualTo(notAllowed) + assertThat(thrown?.cause).hasMessageThat().isEqualTo(rawDiagnostic) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + val emitted = (state as AuthState.Error).exception + assertThat(emitted).isInstanceOf(AuthException.MisconfigurationException::class.java) + assertThat( + getRecoveryMessage( + emitted as AuthException, + DefaultAuthUIStringProvider(applicationContext) + ) + ).doesNotContain("Firebase") + } } diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProviderFirebaseAuthUI.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProviderFirebaseAuthUI.kt index db2931f03d..1b41464b9a 100644 --- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProviderFirebaseAuthUI.kt +++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProviderFirebaseAuthUI.kt @@ -27,10 +27,13 @@ import com.firebase.ui.auth.AuthState import com.firebase.ui.auth.FirebaseAuthUI import com.firebase.ui.auth.configuration.authUIConfiguration import com.firebase.ui.auth.configuration.auth_provider.AuthProvider.Facebook.FacebookProfileData +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider import com.firebase.ui.auth.util.EmailLinkPersistenceManager import com.google.android.gms.tasks.TaskCompletionSource import com.google.common.truth.Truth.assertThat import com.google.firebase.FirebaseApp +import com.google.firebase.FirebaseNetworkException import com.google.firebase.FirebaseOptions import com.google.firebase.auth.AuthCredential import com.google.firebase.auth.AuthResult @@ -356,4 +359,100 @@ class FacebookAuthProviderFirebaseAuthUITest { assertThat(e).isInstanceOf(AuthException.UnknownException::class.java) } } + + // ============================================================================================= + // Error message routing — the configured AuthUIStringProvider, not the device + // ============================================================================================= + + @Test + @Config(manifest = Config.NONE, qualifiers = "night") + fun `signInWithFacebook - FacebookException message comes from the configured string provider`() = + runTest { + // "An unknown error occurred during sign-in", in Japanese. + val localizedMessage = "サインイン中に不明なエラーが発生しました" + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + val provider = spy(AuthProvider.Facebook()) + val config = authUIConfiguration { + context = applicationContext + providers { provider(provider) } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorUnknownAuth: String = localizedMessage + } + } + + val mockAccessToken = mock { + on { token } doReturn "error-token" + } + doAnswer { + throw FacebookException("Graph error") + }.whenever(provider).fetchFacebookProfile(any()) + + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithFacebook( + context = applicationContext, + provider = provider, + accessToken = mockAccessToken, + credentialProvider = mockFBAuthCredentialProvider + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.UnknownException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first { it is AuthState.Error } + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + @Config(manifest = Config.NONE, qualifiers = "night") + fun `signInWithFacebook - credential failure message comes from the configured string provider`() = + runTest { + // "A network error has occurred", in Japanese. + val localizedMessage = "ネットワークエラーが発生しました" + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + val provider = spy(AuthProvider.Facebook()) + val config = authUIConfiguration { + context = applicationContext + providers { provider(provider) } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorNetworkGeneric: String = localizedMessage + } + } + + val mockAccessToken = mock { + on { token } doReturn "network-token" + } + doReturn(null).whenever(provider).fetchFacebookProfile(any()) + // A FirebaseException that is not a FirebaseAuthException, so it maps to + // NetworkException. Raised from the token exchange, which sits in signInWithFacebook's + // own body rather than in the delegated signInAndLinkWithCredential. + doAnswer { + throw FirebaseNetworkException("A network error has occurred.") + }.whenever(mockFBAuthCredentialProvider).getCredential("network-token") + + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithFacebook( + context = applicationContext, + provider = provider, + accessToken = mockAccessToken, + credentialProvider = mockFBAuthCredentialProvider + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.NetworkException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first { it is AuthState.Error } + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } } diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProviderFirebaseAuthUITest.kt index 8aaffe0040..a8a3238786 100644 --- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProviderFirebaseAuthUITest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProviderFirebaseAuthUITest.kt @@ -27,6 +27,9 @@ import com.firebase.ui.auth.AuthException import com.firebase.ui.auth.AuthState import com.firebase.ui.auth.FirebaseAuthUI import com.firebase.ui.auth.configuration.authUIConfiguration +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.recordingScope import com.google.android.gms.common.api.Scope import com.google.android.gms.tasks.TaskCompletionSource import com.google.common.truth.Truth.assertThat @@ -1115,4 +1118,112 @@ class GoogleAuthProviderFirebaseAuthUITest { assertThat(reportedFailures).isEmpty() } + + // ============================================================================================= + // Error message routing — the configured AuthUIStringProvider, not the device + // ============================================================================================= + + @Test + fun `signInWithGoogle - credential manager failure message comes from the configured string provider`() = + runTest { + // "An unknown error occurred during sign-in", in Japanese. + val localizedMessage = "サインイン中に不明なエラーが発生しました" + `when`( + mockCredentialManagerProvider.getGoogleCredential( + context = eq(applicationContext), + credentialManager = any(), + serverClientId = eq("test-client-id"), + filterByAuthorizedAccounts = eq(true), + autoSelectEnabled = eq(false) + ) + ).thenThrow(RuntimeException("No credentials available")) + + val googleProvider = AuthProvider.Google( + serverClientId = "test-client-id", + scopes = emptyList() + ) + val config = authUIConfiguration { + context = applicationContext + providers { provider(googleProvider) } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorUnknownAuth: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithGoogle( + context = applicationContext, + provider = googleProvider, + authorizationProvider = mockAuthorizationProvider, + credentialManagerProvider = mockCredentialManagerProvider + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.UnknownException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + + @Test + fun `signInWithGoogle - scope authorization failure message comes from the configured string provider`() = + runTest { + // "An unknown error occurred during sign-in", in Japanese. + val localizedMessage = "サインイン中に不明なエラーが発生しました" + `when`(mockAuthorizationProvider.authorize(eq(applicationContext), any())) + .thenThrow(RuntimeException("Authorization failed")) + // Sign-in continues past the authorization failure, so the Error state is transient: + // a recording scope keeps it instead of letting the later states overwrite it. + `when`( + mockCredentialManagerProvider.getGoogleCredential( + context = eq(applicationContext), + credentialManager = any(), + serverClientId = eq("test-client-id"), + filterByAuthorizedAccounts = eq(true), + autoSelectEnabled = eq(false) + ) + ).thenAnswer { throw AuthException.AuthCancelledException("stop here") } + + val googleProvider = AuthProvider.Google( + serverClientId = "test-client-id", + scopes = listOf("https://www.googleapis.com/auth/drive") + ) + val config = authUIConfiguration { + context = applicationContext + providers { provider(googleProvider) } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorUnknownAuth: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + val recorded = mutableListOf() + try { + instance.recordingScope(config, recorded).signInWithGoogle( + context = applicationContext, + provider = googleProvider, + authorizationProvider = mockAuthorizationProvider, + credentialManagerProvider = mockCredentialManagerProvider + ) + } catch (_: Throwable) { + // The cancellation that stops the flow after the authorization failure. + } + + verify(mockAuthorizationProvider).authorize(eq(applicationContext), any()) + val authorizationError = recorded + .filterIsInstance() + .firstOrNull { it.exception is AuthException.UnknownException } + assertThat(authorizationError).isNotNull() + assertThat(authorizationError!!.exception).hasMessageThat() + .isEqualTo(localizedMessage) + } } diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProviderFirebaseAuthUITest.kt index 644324d47b..72fbbe5e89 100644 --- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProviderFirebaseAuthUITest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProviderFirebaseAuthUITest.kt @@ -23,6 +23,8 @@ import com.firebase.ui.auth.AuthException import com.firebase.ui.auth.AuthState import com.firebase.ui.auth.FirebaseAuthUI import com.firebase.ui.auth.configuration.authUIConfiguration +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.TaskCompletionSource import com.google.common.truth.Truth.assertThat @@ -32,6 +34,7 @@ import com.google.firebase.FirebaseOptions import com.google.firebase.auth.AuthCredential import com.google.firebase.auth.AuthResult import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException import com.google.firebase.auth.FirebaseAuthUserCollisionException import com.google.firebase.auth.FirebaseAuthWebException import com.google.firebase.auth.FirebaseUser @@ -444,4 +447,60 @@ class OAuthProviderFirebaseAuthUITest { assertThat(reportedFailures).isEmpty() } + + // ============================================================================================= + // Error message routing — the configured AuthUIStringProvider, not the device + // ============================================================================================= + + @Test + fun `signInWithProvider - failure message comes from the configured string provider`() = + runTest { + // "Those credentials are not valid", in Japanese. + val localizedMessage = "その認証情報は有効ではありません" + val taskCompletionSource = TaskCompletionSource() + taskCompletionSource.setException( + FirebaseAuthInvalidCredentialsException( + "ERROR_INVALID_CREDENTIAL", + "The supplied auth credential is malformed or has expired." + ) + ) + `when`(mockFirebaseAuth.pendingAuthResult).thenReturn(null) + `when`(mockFirebaseAuth.currentUser).thenReturn(null) + `when`( + mockFirebaseAuth.startActivityForSignInWithProvider( + any(), + any() + ) + ).thenReturn(taskCompletionSource.task) + + val githubProvider = AuthProvider.Github(customParameters = emptyMap()) + val config = authUIConfiguration { + context = applicationContext + providers { provider(githubProvider) } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorInvalidCredentials: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(config).signInWithProvider( + applicationContext, + activity = mockActivity, + provider = githubProvider + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } } diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProviderFirebaseAuthUITest.kt index 11f87f249a..efcd3e6301 100644 --- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProviderFirebaseAuthUITest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProviderFirebaseAuthUITest.kt @@ -23,12 +23,15 @@ import com.firebase.ui.auth.AuthState import com.firebase.ui.auth.FirebaseAuthUI import com.firebase.ui.auth.configuration.AuthUIConfiguration import com.firebase.ui.auth.configuration.authUIConfiguration +import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider import com.google.android.gms.tasks.TaskCompletionSource import com.google.common.truth.Truth.assertThat import com.google.firebase.FirebaseApp import com.google.firebase.FirebaseOptions import com.google.firebase.auth.AuthResult import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException import com.google.firebase.auth.FirebaseUser import com.google.firebase.auth.MultiFactorSession import com.google.firebase.auth.PhoneAuthCredential @@ -406,6 +409,75 @@ class PhoneAuthProviderFirebaseAuthUITest { .isNotInstanceOf(AuthState.Error::class.java) } + @Test + fun `verifyPhoneNumber - failure message comes from the configured string provider`() = + runTest { + // "The format of the phone number is incorrect", in Japanese. + val localizedMessage = "電話番号の形式が正しくありません" + val localizedConfig = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Phone( + defaultNumber = null, + defaultCountryCode = null, + allowedCountries = null, + ) + ) + } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorInvalidCredentials: String = localizedMessage + } + } + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + val phoneProvider = AuthProvider.Phone( + defaultNumber = null, + defaultCountryCode = null, + allowedCountries = null, + timeout = 60L, + ) + val rejectingVerifier = object : AuthProvider.Phone.Verifier { + override fun verifyPhoneNumber( + auth: FirebaseAuth, + activity: Activity?, + phoneNumber: String, + timeout: Long, + forceResendingToken: PhoneAuthProvider.ForceResendingToken?, + multiFactorSession: MultiFactorSession?, + isInstantVerificationEnabled: Boolean, + ): Flow = flow { + throw FirebaseAuthInvalidCredentialsException( + "ERROR_INVALID_PHONE_NUMBER", + "The format of the phone number provided is incorrect." + ) + } + } + + var thrown: Throwable? = null + try { + instance.flowScope(localizedConfig).verifyPhoneNumber( + provider = phoneProvider, + activity = null, + phoneNumber = "not-a-number", + verifier = rejectingVerifier + ) + } catch (t: Throwable) { + thrown = t + } + + // Building the exception without the configured provider leaves Firebase's own English + // message on it, and the error dialog renders that verbatim. + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat() + .isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } + @Test fun `verifyPhoneNumber - cancellation does not clobber a newer unrelated state`() = runTest { val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) @@ -607,4 +679,55 @@ class PhoneAuthProviderFirebaseAuthUITest { verify(anonymousUser).linkWithCredential(mockCredential) } + @Test + fun `submitVerificationCode - failure message comes from the configured string provider`() = + runTest { + // "The verification code is incorrect", in Japanese. + val localizedMessage = "確認コードが正しくありません" + // Raised while building the credential, which is submitVerificationCode's own work: + // everything after it is delegated to signInAndLinkWithCredential. + `when`(mockPhoneAuthCredentialProvider.getCredential("test-verification-id", "000000")) + .thenAnswer { + throw FirebaseAuthInvalidCredentialsException( + "ERROR_INVALID_VERIFICATION_CODE", + "The sms verification code used to create the phone auth credential is invalid." + ) + } + + val phoneProvider = AuthProvider.Phone( + defaultNumber = null, + defaultCountryCode = null, + allowedCountries = null, + timeout = 60L, + ) + val localizedConfig = authUIConfiguration { + context = applicationContext + providers { provider(phoneProvider) } + stringProvider = object : + AuthUIStringProvider by DefaultAuthUIStringProvider(applicationContext) { + override val errorInvalidCredentials: String = localizedMessage + } + } + + val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth) + var thrown: Throwable? = null + try { + instance.flowScope(localizedConfig).submitVerificationCode( + applicationContext, + verificationId = "test-verification-id", + code = "000000", + credentialProvider = mockPhoneAuthCredentialProvider + ) + } catch (t: Throwable) { + thrown = t + } + + assertThat(thrown).isInstanceOf(AuthException.InvalidCredentialsException::class.java) + assertThat(thrown).hasMessageThat().isEqualTo(localizedMessage) + + val state = instance.authStateFlow().first() + assertThat(state).isInstanceOf(AuthState.Error::class.java) + assertThat((state as AuthState.Error).exception).hasMessageThat() + .isEqualTo(localizedMessage) + } } \ No newline at end of file diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt index c095131a13..f4c373d07e 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialogLogicTest.kt @@ -4,6 +4,7 @@ import com.firebase.ui.auth.AuthException import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider import com.google.common.truth.Truth import com.google.firebase.auth.EmailAuthProvider +import com.google.firebase.auth.FirebaseAuthException import com.google.firebase.auth.GoogleAuthProvider import org.junit.Test import org.junit.runner.RunWith @@ -41,9 +42,22 @@ class ErrorRecoveryDialogLogicTest { // ============================================================================================= @Test - fun `getRecoveryMessage returns network error message for NetworkException`() { + fun `getRecoveryMessage prefers the library-owned message for NetworkException`() { + // Arrange - AuthException.from now puts the configured provider's string on the exception, + // so discarding it here would throw away the host's own translated copy. + val error = AuthException.NetworkException("Pas de connexion Internet") + + // Act + val message = getRecoveryMessage(error, mockStringProvider) + + // Assert + Truth.assertThat(message).isEqualTo("Pas de connexion Internet") + } + + @Test + fun `getRecoveryMessage returns network error message for NetworkException with blank message`() { // Arrange - val error = AuthException.NetworkException("Network error") + val error = AuthException.NetworkException("") // Act val message = getRecoveryMessage(error, mockStringProvider) @@ -77,15 +91,16 @@ class ErrorRecoveryDialogLogicTest { } @Test - fun `getRecoveryMessage returns generic message for InvalidCredentialsException with generic error text`() { - // Arrange - When error message is the generic fallback + fun `getRecoveryMessage shows the hardcoded fallback text for InvalidCredentialsException`() { + // Arrange - The old sentinel dropped this exact string on the floor. It could never match + // real traffic anyway: the SDK formats every message as " [ ]". val error = AuthException.InvalidCredentialsException("Invalid credentials provided") // Act val message = getRecoveryMessage(error, mockStringProvider) - // Assert - Should show the localized generic message - Truth.assertThat(message).isEqualTo("Incorrect password.") + // Assert + Truth.assertThat(message).isEqualTo("Invalid credentials provided") } @Test @@ -101,22 +116,34 @@ class ErrorRecoveryDialogLogicTest { } @Test - fun `getRecoveryMessage returns user not found message for UserNotFoundException`() { + fun `getRecoveryMessage prefers the library-owned message for UserNotFoundException`() { // Arrange - val error = AuthException.UserNotFoundException("User not found") + val error = AuthException.UserNotFoundException("Aucun compte pour cette adresse") // Act val message = getRecoveryMessage(error, mockStringProvider) // Assert - Truth.assertThat(message).isEqualTo("That email address doesn't match an existing account") + Truth.assertThat(message).isEqualTo("Aucun compte pour cette adresse") } @Test - fun `getRecoveryMessage returns weak password message with reason for WeakPasswordException`() { + fun `getRecoveryMessage returns user not found message for UserNotFoundException with blank message`() { // Arrange + val error = AuthException.UserNotFoundException("") + + // Act + val message = getRecoveryMessage(error, mockStringProvider) + + // Assert + Truth.assertThat(message).isEqualTo("That email address doesn't match an existing account") + } + + @Test + fun `getRecoveryMessage drops the untranslated reason for WeakPasswordException`() { + // Arrange - the reason is the raw SDK string, English in every locale. val error = AuthException.WeakPasswordException( - "Password is too weak", + "", null, "Password should be at least 8 characters" ) @@ -124,14 +151,17 @@ class ErrorRecoveryDialogLogicTest { // Act val message = getRecoveryMessage(error, mockStringProvider) - // Assert - Truth.assertThat(message).isEqualTo("Password not strong enough. Use at least 6 characters and a mix of letters and numbers\n\nReason: Password should be at least 8 characters") + // Assert - blank message, so the provider string supplies the whole body. The reason is + // not appended: it is untranslated, and the provider string already states the minimum. + Truth.assertThat(message).isEqualTo("Password not strong enough. Use at least 6 characters and a mix of letters and numbers") + Truth.assertThat(message).doesNotContain("Reason:") + Truth.assertThat(message).doesNotContain("Password should be at least 8 characters") } @Test fun `getRecoveryMessage returns weak password message without reason for WeakPasswordException`() { // Arrange - val error = AuthException.WeakPasswordException("Password is too weak", null, null) + val error = AuthException.WeakPasswordException("", null, null) // Act val message = getRecoveryMessage(error, mockStringProvider) @@ -144,7 +174,7 @@ class ErrorRecoveryDialogLogicTest { fun `getRecoveryMessage returns email already in use message with email for EmailAlreadyInUseException`() { // Arrange val error = AuthException.EmailAlreadyInUseException( - "Email already in use", + "", null, "test@example.com" ) @@ -152,14 +182,14 @@ class ErrorRecoveryDialogLogicTest { // Act val message = getRecoveryMessage(error, mockStringProvider) - // Assert + // Assert - blank message, so the provider string supplies the base and the email is kept Truth.assertThat(message).isEqualTo("Email account registration unsuccessful (test@example.com)") } @Test fun `getRecoveryMessage returns email already in use message without email for EmailAlreadyInUseException`() { // Arrange - val error = AuthException.EmailAlreadyInUseException("Email already in use", null, null) + val error = AuthException.EmailAlreadyInUseException("", null, null) // Act val message = getRecoveryMessage(error, mockStringProvider) @@ -168,6 +198,103 @@ class ErrorRecoveryDialogLogicTest { Truth.assertThat(message).isEqualTo("Email account registration unsuccessful") } + // ============================================================================================= + // Misconfiguration — the one message that is never rendered + // ============================================================================================= + + @Test + fun `getRecoveryMessage never shows the raw message for MisconfigurationException`() { + // Arrange - exactly what firebase-auth 24.2.0 puts on a disabled sign-in provider. It is + // untranslated, it names the Firebase console, and the user can do nothing with it. + val rawDiagnostic = "This operation is not allowed. This may be because the given sign-in " + + "provider is disabled for this Firebase project. Enable it in the Firebase " + + "console, under the sign-in method tab of the Auth section. [ OPERATION_NOT_ALLOWED ]" + val error = AuthException.MisconfigurationException(rawDiagnostic) + + // Act + val message = getRecoveryMessage(error, mockStringProvider) + + // Assert + Truth.assertThat(message).isEqualTo("An unknown error occurred.") + Truth.assertThat(message).doesNotContain("Firebase") + Truth.assertThat(message).doesNotContain("OPERATION_NOT_ALLOWED") + } + + @Test + fun `MisconfigurationException from() keeps the raw diagnostic on the cause, not the message`() { + // EmailAuthScreen and PhoneAuthScreen render `exception.message` inline without going + // through getRecoveryMessage, so the diagnostic has to be off the message entirely. + val rawDiagnostic = "The supplied auth credential is malformed. [ INVALID_CERT_HASH ]" + val firebaseException = + object : FirebaseAuthException("ERROR_INVALID_CERT_HASH", rawDiagnostic) {} + + val error = AuthException.from(firebaseException, mockStringProvider) + + Truth.assertThat(error).isInstanceOf(AuthException.MisconfigurationException::class.java) + Truth.assertThat(error.message).isEqualTo("An unknown error occurred.") + Truth.assertThat(error.cause).isEqualTo(firebaseException) + Truth.assertThat(error.cause?.message).isEqualTo(rawDiagnostic) + } + + @Test + fun `isRecoverable returns false for MisconfigurationException`() { + val error = AuthException.MisconfigurationException("Unauthorized domain") + + Truth.assertThat(isRecoverable(error)).isFalse() + } + + // ============================================================================================= + // Subtypes whose arms used to discard error.message outright + // ============================================================================================= + + @Test + fun `getRecoveryMessage prefers the library-owned message for TooManyRequestsException`() { + val error = AuthException.TooManyRequestsException("Trop de tentatives") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Trop de tentatives") + } + + @Test + fun `getRecoveryMessage returns the recovery string for TooManyRequestsException with blank message`() { + val error = AuthException.TooManyRequestsException("") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("This phone number has been used too many times") + } + + @Test + fun `getRecoveryMessage prefers the library-owned message for MfaRequiredException`() { + val error = AuthException.MfaRequiredException("Vérification supplémentaire requise") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Vérification supplémentaire requise") + } + + @Test + fun `getRecoveryMessage returns the recovery string for MfaRequiredException with blank message`() { + val error = AuthException.MfaRequiredException("") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Additional verification required. Please complete multi-factor authentication.") + } + + @Test + fun `getRecoveryMessage prefers the library-owned message for AuthCancelledException`() { + val error = AuthException.AuthCancelledException("Connexion annulée") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Connexion annulée") + } + + @Test + fun `getRecoveryMessage returns the recovery string for AuthCancelledException with blank message`() { + val error = AuthException.AuthCancelledException("") + + Truth.assertThat(getRecoveryMessage(error, mockStringProvider)) + .isEqualTo("Authentication was cancelled. Please try again when ready.") + } + // ============================================================================================= // Recovery Action Text Tests // ============================================================================================= diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/components/TopLevelDialogControllerTest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/components/TopLevelDialogControllerTest.kt index 60fa0ba406..902cade0bb 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/components/TopLevelDialogControllerTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/ui/components/TopLevelDialogControllerTest.kt @@ -1,5 +1,11 @@ package com.firebase.ui.auth.ui.components +import android.content.Context +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick @@ -7,6 +13,7 @@ import androidx.test.core.app.ApplicationProvider import com.firebase.ui.auth.AuthException import com.firebase.ui.auth.AuthState import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider +import com.firebase.ui.auth.configuration.string_provider.LocalAuthUIStringProvider import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -39,8 +46,10 @@ class TopLevelDialogControllerTest { lateinit var controller: TopLevelDialogController composeTestRule.setContent { - controller = rememberTopLevelDialogController(stringProvider) { state } - controller.CurrentDialog() + CompositionLocalProvider(LocalAuthUIStringProvider provides stringProvider) { + controller = rememberTopLevelDialogController { state } + controller.CurrentDialog() + } } val error = AuthState.Error(Exception("boom")) @@ -69,8 +78,10 @@ class TopLevelDialogControllerTest { lateinit var controller: TopLevelDialogController composeTestRule.setContent { - controller = rememberTopLevelDialogController(stringProvider) { state } - controller.CurrentDialog() + CompositionLocalProvider(LocalAuthUIStringProvider provides stringProvider) { + controller = rememberTopLevelDialogController { state } + controller.CurrentDialog() + } } val error = AuthState.Error(Exception("boom")) @@ -97,6 +108,53 @@ class TopLevelDialogControllerTest { composeTestRule.onNodeWithText(stringProvider.errorDialogTitle).assertDoesNotExist() } + @Test + fun `de-dup fallback reads the latest authState lambda, not the first composition's`() { + stringProvider = DefaultAuthUIStringProvider(ApplicationProvider.getApplicationContext()) + val liveState = mutableStateOf(AuthState.Idle) + lateinit var controller: TopLevelDialogController + + composeTestRule.setContent { + // Mirrors FirebaseAuthScreen, which reads the collected state into a local `val` and + // passes `{ authState }`: every recomposition hands the factory a *new* lambda that + // has captured that frame's value, so an unkeyed `remember` would pin the first one. + val authState = liveState.value + CompositionLocalProvider(LocalAuthUIStringProvider provides stringProvider) { + controller = rememberTopLevelDialogController { authState } + controller.CurrentDialog() + } + } + + val error = AuthState.Error(Exception("boom")) + val exception = AuthException.from(error.exception, stringProvider) + + // Recompose with the Error before showing anything, so the first composition's captured + // value (Idle) and the live one differ. + composeTestRule.runOnIdle { liveState.value = error } + composeTestRule.waitForIdle() + + // No errorState argument, so `currentAuthState()` is the only path that can record the + // Error for de-duplication. + composeTestRule.runOnIdle { + controller.showErrorDialog(exception = exception) + } + composeTestRule.waitForIdle() + composeTestRule.onNodeWithText(stringProvider.errorDialogTitle).assertExists() + + composeTestRule.runOnIdle { controller.dismissDialog() } + composeTestRule.waitForIdle() + composeTestRule.onNodeWithText(stringProvider.errorDialogTitle).assertDoesNotExist() + + // Same Error still live, same exception instance: the fallback must have recorded it, so + // this repeat is a no-op. With a pinned first-composition lambda the fallback resolves to + // Idle, records nothing, and the dialog comes back. + composeTestRule.runOnIdle { + controller.showErrorDialog(exception = exception) + } + composeTestRule.waitForIdle() + composeTestRule.onNodeWithText(stringProvider.errorDialogTitle).assertDoesNotExist() + } + @Test fun `second observer of the same error does not overwrite the first observer's dialog`() { stringProvider = DefaultAuthUIStringProvider(ApplicationProvider.getApplicationContext()) @@ -104,8 +162,10 @@ class TopLevelDialogControllerTest { lateinit var controller: TopLevelDialogController composeTestRule.setContent { - controller = rememberTopLevelDialogController(stringProvider) { state } - controller.CurrentDialog() + CompositionLocalProvider(LocalAuthUIStringProvider provides stringProvider) { + controller = rememberTopLevelDialogController { state } + controller.CurrentDialog() + } } val error = AuthState.Error(Exception("boom")) @@ -150,4 +210,66 @@ class TopLevelDialogControllerTest { "secondOnRetryCalled=$secondOnRetryCalled)" } } + + @Test + fun `dialog survives a host that rebuilds its string provider every recomposition`() { + val context = ApplicationProvider.getApplicationContext() + stringProvider = DefaultAuthUIStringProvider(context) + var state: AuthState = AuthState.Idle + lateinit var controller: TopLevelDialogController + var tick by mutableIntStateOf(0) + + composeTestRule.setContent { + // What `authUIConfiguration { }` built inside a composable does: a fresh + // DefaultAuthUIStringProvider, identity-equal to nothing, on every recomposition. + @Suppress("UNUSED_EXPRESSION") + tick + val unstableProvider = DefaultAuthUIStringProvider(context) + CompositionLocalProvider(LocalAuthUIStringProvider provides unstableProvider) { + controller = rememberTopLevelDialogController { state } + controller.CurrentDialog() + } + } + + val error = AuthState.Error(Exception("boom")) + composeTestRule.runOnIdle { + state = error + controller.showErrorDialog( + exception = AuthException.from(error.exception, stringProvider) + ) + } + composeTestRule.waitForIdle() + composeTestRule.onNodeWithText(stringProvider.errorDialogTitle).assertExists() + + composeTestRule.runOnIdle { tick++ } + composeTestRule.waitForIdle() + + composeTestRule.onNodeWithText(stringProvider.errorDialogTitle).assertExists() + } + + @Suppress("DEPRECATION") + @Test + fun `deprecated constructor still renders with its explicit provider and no CompositionLocal`() { + stringProvider = DefaultAuthUIStringProvider(ApplicationProvider.getApplicationContext()) + var state: AuthState = AuthState.Idle + lateinit var controller: TopLevelDialogController + + // Deliberately no LocalAuthUIStringProvider in scope: the local throws when absent, so + // this pins that the deprecated path keeps honouring the provider it was handed. + composeTestRule.setContent { + controller = rememberTopLevelDialogController(stringProvider) { state } + controller.CurrentDialog() + } + + val error = AuthState.Error(Exception("boom")) + composeTestRule.runOnIdle { + state = error + controller.showErrorDialog( + exception = AuthException.from(error.exception, stringProvider) + ) + } + composeTestRule.waitForIdle() + + composeTestRule.onNodeWithText(stringProvider.errorDialogTitle).assertExists() + } } diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/screens/email/EmailAuthRouteNavigationTest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/screens/email/EmailAuthRouteNavigationTest.kt index 7d3abe4c62..c0552f9990 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/screens/email/EmailAuthRouteNavigationTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/ui/screens/email/EmailAuthRouteNavigationTest.kt @@ -657,7 +657,7 @@ class EmailAuthRouteNavigationTest { } val authState by remember(authUI) { authUI.authStateFlow() }.collectAsState(AuthState.Idle) - val dialogController = rememberTopLevelDialogController(stringProvider) { authState } + val dialogController = rememberTopLevelDialogController { authState } CompositionLocalProvider( LocalAuthUIStringProvider provides configuration.stringProvider, diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreenVerificationLifecycleTest.kt b/auth/src/test/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreenVerificationLifecycleTest.kt index 9624b2d47e..6a67f016b5 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreenVerificationLifecycleTest.kt +++ b/auth/src/test/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreenVerificationLifecycleTest.kt @@ -172,7 +172,7 @@ class PhoneAuthScreenVerificationLifecycleTest { */ private fun setScreenContent(withDialogs: Boolean = false) { composeTestRule.setContent { - val controller = rememberTopLevelDialogController(configuration.stringProvider) { + val controller = rememberTopLevelDialogController { AuthState.Idle } CompositionLocalProvider( @@ -202,8 +202,10 @@ class PhoneAuthScreenVerificationLifecycleTest { flowState = flowState, ) { state -> capturedState = state } } + // Inside the provider, like FirebaseAuthScreen: CurrentDialog resolves its + // strings from LocalAuthUIStringProvider at render time. + if (withDialogs) controller.CurrentDialog() } - if (withDialogs) controller.CurrentDialog() } composeTestRule.waitForIdle() } @@ -571,8 +573,9 @@ class PhoneAuthScreenVerificationLifecycleTest { settle() // The failure also tears down the verification, which must not append a second, - // spurious cancellation error behind the real one. - assertThat(reportedErrors.map { it.message }).containsExactly("sign-in blew up") + // spurious cancellation error behind the real one. AuthException.from replaces the + // message with renderable copy, so the original text is identified on the cause. + assertThat(reportedErrors.map { it.cause?.message }).containsExactly("sign-in blew up") } } diff --git a/auth/src/test/java/com/firebase/ui/auth/util/ContinueUrlBuilderTest.kt b/auth/src/test/java/com/firebase/ui/auth/util/ContinueUrlBuilderTest.kt new file mode 100644 index 0000000000..6b1b578138 --- /dev/null +++ b/auth/src/test/java/com/firebase/ui/auth/util/ContinueUrlBuilderTest.kt @@ -0,0 +1,270 @@ +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.firebase.ui.auth.util + +import androidx.core.net.toUri +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * Unit tests for [ContinueUrlBuilder]. Runs under Robolectric so the assertions can read the + * built URLs back through the real [android.net.Uri] parser rather than by string matching. + * + * @suppress Internal test class + */ +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) +class ContinueUrlBuilderTest { + + @Test + fun `url without a query gets a question mark separator`() { + val url = ContinueUrlBuilder("https://example.com/finish") + .appendSessionId("sid123") + .appendAnonymousUserId("auid456") + .appendProviderId("google.com") + .appendForceSameDeviceBit(true) + .build() + + assertThat(url).isEqualTo( + "https://example.com/finish?ui_sid=sid123&ui_auid=auid456&ui_pid=google.com&ui_sd=1" + ) + + val uri = url.toUri() + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + assertThat(uri.getQueryParameter("ui_auid")).isEqualTo("auid456") + assertThat(uri.getQueryParameter("ui_pid")).isEqualTo("google.com") + assertThat(uri.getQueryParameter("ui_sd")).isEqualTo("1") + } + + @Test + fun `url with an existing query gets an ampersand separator and keeps the consumer's params`() { + val url = ContinueUrlBuilder("https://example.com/finish?demo=fullcustomization") + .appendSessionId("sid123") + .appendAnonymousUserId("auid456") + .appendProviderId("google.com") + .appendForceSameDeviceBit(true) + .build() + + assertThat(url.count { it == '?' }).isEqualTo(1) + + val uri = url.toUri() + assertThat(uri.getQueryParameter("demo")).isEqualTo("fullcustomization") + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + assertThat(uri.getQueryParameter("ui_auid")).isEqualTo("auid456") + assertThat(uri.getQueryParameter("ui_pid")).isEqualTo("google.com") + assertThat(uri.getQueryParameter("ui_sd")).isEqualTo("1") + } + + @Test + fun `url with several existing params keeps every one of them`() { + val url = ContinueUrlBuilder("https://example.com/finish?demo=full&lang=en") + .appendSessionId("sid123") + .build() + + val uri = url.toUri() + assertThat(uri.getQueryParameter("demo")).isEqualTo("full") + assertThat(uri.getQueryParameter("lang")).isEqualTo("en") + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + } + + @Test + fun `url ending in an open query marker keeps every param retrievable`() { + assertThat( + ContinueUrlBuilder("https://example.com/finish?").appendSessionId("sid123").build() + ).isEqualTo("https://example.com/finish?ui_sid=sid123") + + // A trailing `&` is an empty param, which Uri keeps rather than folding away. Cosmetic + // only: both the consumer's params and ours still parse out. + val url = ContinueUrlBuilder("https://example.com/finish?demo=full&") + .appendSessionId("sid123") + .build() + + assertThat(url).isEqualTo("https://example.com/finish?demo=full&&ui_sid=sid123") + + val uri = url.toUri() + assertThat(uri.getQueryParameter("demo")).isEqualTo("full") + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + } + + @Test + fun `param values are percent encoded`() { + val hostile = "a&b=c d#e" + val url = ContinueUrlBuilder("https://example.com/finish?demo=full") + .appendAnonymousUserId(hostile) + .build() + + // Interpolated raw, this value would split the query and open a fragment. + assertThat(url).doesNotContain(hostile) + + // Pin the encoded form on the wire, not just that it survives a round trip. + assertThat(url) + .isEqualTo("https://example.com/finish?demo=full&ui_auid=a%26b%3Dc%20d%23e") + + val uri = url.toUri() + assertThat(uri.getQueryParameter("ui_auid")).isEqualTo(hostile) + assertThat(uri.getQueryParameter("demo")).isEqualTo("full") + assertThat(uri.fragment).isNull() + } + + @Test + fun `params are appended before the fragment`() { + val url = ContinueUrlBuilder("https://example.com/finish?demo=full#section") + .appendSessionId("sid123") + .appendForceSameDeviceBit(false) + .build() + + assertThat(url) + .isEqualTo("https://example.com/finish?demo=full&ui_sid=sid123&ui_sd=0#section") + + val uri = url.toUri() + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + assertThat(uri.getQueryParameter("ui_sd")).isEqualTo("0") + assertThat(uri.fragment).isEqualTo("section") + } + + @Test + fun `fragment on a url without a query still gets a question mark separator`() { + val url = ContinueUrlBuilder("https://example.com/finish#section") + .appendSessionId("sid123") + .build() + + assertThat(url).isEqualTo("https://example.com/finish?ui_sid=sid123#section") + assertThat(url.toUri().getQueryParameter("ui_sid")).isEqualTo("sid123") + } + + @Test + fun `blank values are skipped`() { + val url = ContinueUrlBuilder("https://example.com/finish") + .appendSessionId("") + .appendAnonymousUserId(" ") + .appendProviderId("google.com") + .build() + + assertThat(url).isEqualTo("https://example.com/finish?ui_pid=google.com") + + val uri = url.toUri() + assertThat(uri.getQueryParameter("ui_sid")).isNull() + assertThat(uri.getQueryParameter("ui_auid")).isNull() + } + + @Test + fun `blank first value does not steal the separator from the next param`() { + val url = ContinueUrlBuilder("https://example.com/finish?demo=full") + .appendSessionId("") + .appendAnonymousUserId("auid456") + .build() + + assertThat(url).isEqualTo("https://example.com/finish?demo=full&ui_auid=auid456") + } + + @Test + fun `no params leaves the url untouched`() { + assertThat(ContinueUrlBuilder("https://example.com/finish").build()) + .isEqualTo("https://example.com/finish") + + assertThat(ContinueUrlBuilder("https://example.com/finish?demo=full").build()) + .isEqualTo("https://example.com/finish?demo=full") + + assertThat(ContinueUrlBuilder("https://example.com/finish?demo=full#section").build()) + .isEqualTo("https://example.com/finish?demo=full#section") + } + + @Test + fun `multi segment path is untouched`() { + val url = ContinueUrlBuilder("https://example.com/finish/fullcustomization") + .appendSessionId("sid123") + .build() + + assertThat(url).isEqualTo("https://example.com/finish/fullcustomization?ui_sid=sid123") + + val uri = url.toUri() + assertThat(uri.lastPathSegment).isEqualTo("fullcustomization") + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + } + + @Test + fun `force same device bit is written as one or zero`() { + assertThat( + ContinueUrlBuilder("https://example.com/finish").appendForceSameDeviceBit(true).build() + ).isEqualTo("https://example.com/finish?ui_sd=1") + + assertThat( + ContinueUrlBuilder("https://example.com/finish").appendForceSameDeviceBit(false).build() + ).isEqualTo("https://example.com/finish?ui_sd=0") + } + + @Test + fun `query ending in an unencoded question mark still keeps every param`() { + // An unencoded '?' is legal inside a query (RFC 3986), so a trailing one does + // not mean the query is still open. + val input = "https://example.com/finish?next=/search?" + val url = ContinueUrlBuilder(input) + .appendSessionId("sid123") + .appendForceSameDeviceBit(true) + .build() + + val uri = url.toUri() + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + assertThat(uri.getQueryParameter("ui_sd")).isEqualTo("1") + assertThat(uri.getQueryParameter("next")).isEqualTo("/search?") + } + + @Test + fun `url ending in a bare double question mark keeps every param`() { + val url = ContinueUrlBuilder("https://example.com/finish??") + .appendSessionId("sid123") + .build() + + assertThat(url.toUri().getQueryParameter("ui_sid")).isEqualTo("sid123") + } + + @Test + fun `fragment before a query keeps the params in the query`() { + // Everything after the first '#' is the fragment, query-looking or not. + val url = ContinueUrlBuilder("https://example.com/finish#section?x=1") + .appendSessionId("sid123") + .build() + + val uri = url.toUri() + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + assertThat(uri.fragment).isEqualTo("section?x=1") + } + + @Test + fun `percent encoded url in a query value survives untouched`() { + val input = "https://example.com/finish?redirect=https%3A%2F%2Ffoo.com%2Fa%3Fb%3Dc" + val url = ContinueUrlBuilder(input).appendSessionId("sid123").build() + + val uri = url.toUri() + assertThat(uri.getQueryParameter("redirect")).isEqualTo("https://foo.com/a?b=c") + assertThat(uri.getQueryParameter("ui_sid")).isEqualTo("sid123") + } + + @Test + fun `the consumer's url is preserved verbatim ahead of the appended params`() { + val input = "https://example.com/finish?demo=full&lang=en" + val url = ContinueUrlBuilder(input).appendSessionId("sid123").build() + + assertThat(url).startsWith(input) + } + + @Test(expected = IllegalArgumentException::class) + fun `blank url is rejected`() { + ContinueUrlBuilder(" ") + } +} diff --git a/auth/src/test/java/com/firebase/ui/auth/util/EmailLinkParserTest.kt b/auth/src/test/java/com/firebase/ui/auth/util/EmailLinkParserTest.kt new file mode 100644 index 0000000000..ee831f5d05 --- /dev/null +++ b/auth/src/test/java/com/firebase/ui/auth/util/EmailLinkParserTest.kt @@ -0,0 +1,161 @@ +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.firebase.ui.auth.util + +import androidx.core.net.toUri +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** + * Unit tests for [EmailLinkParser], including round-trips of the continue URLs that + * [ContinueUrlBuilder] produces. + * + * @suppress Internal test class + */ +@RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) +class EmailLinkParserTest { + + @Test + fun `parses the parameters of a continue url built from a url without a query`() { + val parser = EmailLinkParser( + ContinueUrlBuilder("https://example.com/finish") + .appendSessionId("sid123") + .appendAnonymousUserId("auid456") + .appendProviderId("google.com") + .appendForceSameDeviceBit(true) + .build() + ) + + assertThat(parser.sessionId).isEqualTo("sid123") + assertThat(parser.anonymousUserId).isEqualTo("auid456") + assertThat(parser.providerId).isEqualTo("google.com") + assertThat(parser.forceSameDeviceBit).isTrue() + } + + @Test + fun `parses the parameters of a continue url built from a url that already had a query`() { + val parser = EmailLinkParser( + ContinueUrlBuilder("https://example.com/finish?demo=fullcustomization") + .appendSessionId("sid123") + .appendAnonymousUserId("auid456") + .appendProviderId("google.com") + .appendForceSameDeviceBit(true) + .build() + ) + + assertThat(parser.sessionId).isEqualTo("sid123") + assertThat(parser.anonymousUserId).isEqualTo("auid456") + assertThat(parser.providerId).isEqualTo("google.com") + assertThat(parser.forceSameDeviceBit).isTrue() + } + + @Test + fun `parses the parameters of a continue url that had a query and a fragment`() { + val parser = EmailLinkParser( + ContinueUrlBuilder("https://example.com/finish?demo=fullcustomization#section") + .appendSessionId("sid123") + .appendForceSameDeviceBit(false) + .build() + ) + + assertThat(parser.sessionId).isEqualTo("sid123") + assertThat(parser.forceSameDeviceBit).isFalse() + } + + @Test + fun `parses the parameters out of the continue url nested in an email link`() { + val continueUrl = ContinueUrlBuilder("https://example.com/finish?demo=fullcustomization") + .appendSessionId("sid123") + .appendAnonymousUserId("auid456") + .appendForceSameDeviceBit(true) + .build() + + val emailLink = "https://example.firebaseapp.com/__/auth/action".toUri() + .buildUpon() + .appendQueryParameter("mode", "signIn") + .appendQueryParameter("oobCode", "oob789") + .appendQueryParameter("continueUrl", continueUrl) + .build() + .toString() + + val parser = EmailLinkParser(emailLink) + + assertThat(parser.oobCode).isEqualTo("oob789") + assertThat(parser.sessionId).isEqualTo("sid123") + assertThat(parser.anonymousUserId).isEqualTo("auid456") + assertThat(parser.forceSameDeviceBit).isTrue() + } + + @Test + fun `missing optional parameters read back as null`() { + val parser = EmailLinkParser( + ContinueUrlBuilder("https://example.com/finish").appendSessionId("sid123").build() + ) + + assertThat(parser.sessionId).isEqualTo("sid123") + assertThat(parser.anonymousUserId).isNull() + assertThat(parser.providerId).isNull() + // The bit defaults to false when the link does not carry one. + assertThat(parser.forceSameDeviceBit).isFalse() + } + + @Test + fun `parses the parameters out of a continue url nested behind a dynamic link`() { + val continueUrl = ContinueUrlBuilder("https://example.com/finish?demo=fullcustomization") + .appendSessionId("sid123") + .appendAnonymousUserId("auid456") + .build() + + val actionLink = "https://example.firebaseapp.com/__/auth/action".toUri() + .buildUpon() + .appendQueryParameter("mode", "signIn") + .appendQueryParameter("oobCode", "oob789") + .appendQueryParameter("continueUrl", continueUrl) + .build() + .toString() + + // Exercises parseUri's `link=` branch, not just `continueUrl=`. + val dynamicLink = "https://example.page.link/x".toUri() + .buildUpon() + .appendQueryParameter("link", actionLink) + .build() + .toString() + + val parser = EmailLinkParser(dynamicLink) + + assertThat(parser.oobCode).isEqualTo("oob789") + assertThat(parser.sessionId).isEqualTo("sid123") + assertThat(parser.anonymousUserId).isEqualTo("auid456") + } + + @Test(expected = IllegalArgumentException::class) + fun `blank link is rejected`() { + EmailLinkParser(" ") + } + + @Test(expected = IllegalArgumentException::class) + fun `link without parameters is rejected`() { + EmailLinkParser("https://example.com/finish") + } + + @Test(expected = IllegalArgumentException::class) + fun `missing oob code is rejected when read`() { + EmailLinkParser("https://example.com/finish?ui_sid=sid123").oobCode + } +} diff --git a/build.gradle.kts b/build.gradle.kts index e5e280ced7..c5ff470598 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,9 @@ @file:Suppress("UnstableApiUsage") +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.dsl.LibraryExtension +import com.android.build.api.dsl.Lint + plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.android.library) apply false @@ -31,18 +35,44 @@ allprojects { } } -// Android Lint is configured per module, so there is no repo-wide entry point by default. -// This task is that entry point, and the module list is the gate's definition: -// - :app and :e2eTest declare no lint { } block yet, so they are deliberately absent (CPRN-433). +// The shared Android Lint policy, alongside the checkstyle one above. Modules add only their +// own disables; the strictness flags live here so a module cannot quietly opt out of the gate +// the way :library did with abortOnError = false. +fun Lint.applyCommonPolicy() { + disable += setOf( + "IconExpectedSize", + "InvalidPackage", // Firestore uses GRPC which makes lint mad + "NewerVersionAvailable", "GradleDependency", // For reproducible builds + "SelectableText", "SyntheticAccessor" // We almost never care about this + ) + + checkAllWarnings = true + warningsAsErrors = true + abortOnError = true +} + +subprojects { + plugins.withId("com.android.application") { + extensions.configure { lint { applyCommonPolicy() } } + } + plugins.withId("com.android.library") { + extensions.configure { lint { applyCommonPolicy() } } + } +} + +// Android Lint has no repo-wide entry point by default. This task is that entry point, and +// the module list is the gate's definition: // - :proguard-tests disables its debug variant on CI, so it is gated on release instead. tasks.register("lintAll") { group = "verification" - description = "Runs Android Lint for every module that configures a lint { } block." + description = "Runs Android Lint for every module gated on it." dependsOn( + ":app:lintDebug", ":auth:lintDebug", ":common:lintDebug", ":database:lintDebug", + ":e2eTest:lintDebug", ":firestore:lintDebug", ":library:lintDebug", ":storage:lintDebug", diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 7b8e3c497b..5fcb8e613f 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -23,20 +23,6 @@ android { targetCompatibility = JavaVersion.VERSION_1_8 } - lint { - // Common lint options across all modules - disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor" // We almost never care about this - ) - - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - - } buildTypes { named("release").configure { diff --git a/database/build.gradle.kts b/database/build.gradle.kts index 7e308b9e57..763627927f 100644 --- a/database/build.gradle.kts +++ b/database/build.gradle.kts @@ -25,20 +25,6 @@ android { targetCompatibility = JavaVersion.VERSION_1_8 } - lint { - // Common lint options across all modules - disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor" // We almost never care about this - ) - - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - - } buildTypes { named("release").configure { diff --git a/e2eTest/build.gradle.kts b/e2eTest/build.gradle.kts index e54f6003d1..b7b294807c 100644 --- a/e2eTest/build.gradle.kts +++ b/e2eTest/build.gradle.kts @@ -14,6 +14,7 @@ android { minSdk = Config.SdkVersions.min } + compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 diff --git a/e2eTest/src/test/java/com/firebase/ui/auth/ui/screens/EmailAuthScreenTest.kt b/e2eTest/src/test/java/com/firebase/ui/auth/ui/screens/EmailAuthScreenTest.kt index 838bac0f71..f74ce4ad87 100644 --- a/e2eTest/src/test/java/com/firebase/ui/auth/ui/screens/EmailAuthScreenTest.kt +++ b/e2eTest/src/test/java/com/firebase/ui/auth/ui/screens/EmailAuthScreenTest.kt @@ -6,6 +6,7 @@ import androidx.activity.ComponentActivity import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -65,6 +66,8 @@ import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config import androidx.credentials.PasswordCredential as AndroidPasswordCredential +private const val SIGN_OUT_BUTTON_LABEL = "SIGN OUT" + @Config(sdk = [34]) @RunWith(RobolectricTestRunner::class) class EmailAuthScreenTest { @@ -336,6 +339,100 @@ class EmailAuthScreenTest { assertThat(authUI.auth.currentUser!!.email).isEqualTo(email) } + @Test + fun `sign out from the authenticated screen clears the Firebase session`() { + val email = "signout-test-${System.currentTimeMillis()}@example.com" + val password = "test123" + + val user = ensureFreshUser(authUI, email, password) + requireNotNull(user) { "Failed to create user" } + + try { + verifyEmailInEmulator(authUI, emulatorApi, user) + } catch (e: Exception) { + Assume.assumeTrue( + "Skipping test: Firebase Auth Emulator OOB codes endpoint not available. " + + "Ensure emulator is running on localhost:9099. Error: ${e.message}", + false + ) + } + + authUI.auth.signOut() + shadowOf(Looper.getMainLooper()).idle() + + val configuration = authUIConfiguration { + context = applicationContext + providers { + provider( + AuthProvider.Email( + emailLinkActionCodeSettings = null, + passwordValidationRules = emptyList() + ) + ) + } + isCredentialManagerEnabled = false + } + + var currentAuthState: AuthState = AuthState.Idle + + composeAndroidTestRule.setContent { + CompositionLocalProvider( + LocalAuthUIStringProvider provides DefaultAuthUIStringProvider(applicationContext) + ) { + FirebaseAuthScreen( + configuration = configuration, + authUI = authUI, + onSignInSuccess = { }, + onSignInFailure = { }, + onSignInCancelled = { }, + // Drive FirebaseAuthUI.signOut() through the production callback rather than + // FirebaseAuth.signOut(): this module has no Facebook SDK on its classpath, so + // it also pins that signing out an email-only user touches no Facebook types. + authenticatedContent = { _, uiContext -> + Button(onClick = uiContext.onSignOut) { + Text(SIGN_OUT_BUTTON_LABEL) + } + } + ) + } + val authState by authUI.authStateFlow().collectAsState(AuthState.Idle) + currentAuthState = authState + } + + assertDirectEmailStart() + + composeAndroidTestRule.onNodeWithText(stringProvider.emailHint) + .performScrollTo() + .performTextInput(email) + composeAndroidTestRule.onNodeWithText(stringProvider.passwordHint) + .performScrollTo() + .performTextInput(password) + composeAndroidTestRule.onNodeWithText(stringProvider.signInDefault.uppercase()) + .performScrollTo() + .performClick() + + shadowOf(Looper.getMainLooper()).idle() + composeAndroidTestRule.waitUntil(timeoutMillis = AUTH_STATE_WAIT_TIMEOUT_MS) { + shadowOf(Looper.getMainLooper()).idle() + currentAuthState is AuthState.Success + } + shadowOf(Looper.getMainLooper()).idle() + + composeAndroidTestRule.onNodeWithText(SIGN_OUT_BUTTON_LABEL) + .assertIsDisplayed() + .performClick() + + shadowOf(Looper.getMainLooper()).idle() + composeAndroidTestRule.waitUntil(timeoutMillis = AUTH_STATE_WAIT_TIMEOUT_MS) { + shadowOf(Looper.getMainLooper()).idle() + currentAuthState is AuthState.Idle + } + shadowOf(Looper.getMainLooper()).idle() + + assertThat(currentAuthState).isInstanceOf(AuthState.Idle::class.java) + assertThat(authUI.auth.currentUser).isNull() + } + @Test fun `new email sign-up emits RequiresEmailVerification auth state`() { val name = "Test User" diff --git a/firestore/build.gradle.kts b/firestore/build.gradle.kts index 83ad1617df..e2a6feb39d 100644 --- a/firestore/build.gradle.kts +++ b/firestore/build.gradle.kts @@ -24,20 +24,6 @@ android { } } - lint { - // Common lint options across all modules - disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor" // We almost never care about this - ) - - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - - } buildTypes { named("release").configure { diff --git a/internal/lint/build.gradle.kts b/internal/lint/build.gradle.kts index 352a272573..f385f37dbf 100644 --- a/internal/lint/build.gradle.kts +++ b/internal/lint/build.gradle.kts @@ -6,6 +6,7 @@ dependencies { compileOnly(libs.lint.api) compileOnly(libs.kotlin.stdlib) + testImplementation(libs.junit) testImplementation(libs.lint.api) testImplementation(libs.lint.tests) } diff --git a/internal/lint/src/main/java/com/firebaseui/lint/internal/LintIssueRegistry.kt b/internal/lint/src/main/java/com/firebaseui/lint/internal/LintIssueRegistry.kt index 7f01ec34fc..6a9788ef94 100644 --- a/internal/lint/src/main/java/com/firebaseui/lint/internal/LintIssueRegistry.kt +++ b/internal/lint/src/main/java/com/firebaseui/lint/internal/LintIssueRegistry.kt @@ -11,7 +11,8 @@ class LintIssueRegistry : IssueRegistry() { get() = com.android.tools.lint.detector.api.CURRENT_API override val issues = listOf( - NonGlobalIdDetector.NON_GLOBAL_ID + NonGlobalIdDetector.NON_GLOBAL_ID, + UntranslatedResourceDetector.UNTRANSLATED_RESOURCE ) override val vendor = Vendor( diff --git a/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt b/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt new file mode 100644 index 0000000000..72d764024e --- /dev/null +++ b/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt @@ -0,0 +1,170 @@ +package com.firebaseui.lint.internal + +import com.android.SdkConstants.ATTR_NAME +import com.android.SdkConstants.ATTR_TRANSLATABLE +import com.android.SdkConstants.TAG_STRING +import com.android.SdkConstants.VALUE_FALSE +import com.android.ide.common.resources.configuration.FolderConfiguration +import com.android.resources.ResourceFolderType +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Context +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.Location +import com.android.tools.lint.detector.api.ResourceXmlDetector +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.XmlContext +import org.w3c.dom.Element + +/** + * Flags a string in a locale folder whose value is byte-identical to the base English one. + * + * `MissingTranslation` only fires when a string is *absent* from a locale, so a resource that + * was copied over untranslated is invisible to it: it is present, it just holds English. A + * whole class of shipped-in-English strings therefore never reaches a lint report; #2509 + * removed 269 such values, found by scanning rather than by any gate. + * + * Only `` elements are checked. An English copy of a `` or `` + * item is still invisible to this check as well as to `MissingTranslation`. + * + * English regional folders (`values-en-rGB` and friends) are skipped, as are strings marked + * `translatable="false"` and the [ALLOWED] names below. Anything else that is legitimately the + * same word in another language is suppressed at the site with `tools:ignore`, so the decision + * sits next to the string. + */ +class UntranslatedResourceDetector : ResourceXmlDetector() { + + /** Base `values/strings.xml` text, keyed by resource name. */ + private val baseStrings = mutableMapOf() + + /** Localized strings to judge once every resource file has been read. */ + private val localized = mutableListOf() + + private data class LocalizedString( + val name: String, + val folder: String, + val text: String, + val handle: Location.Handle + ) + + override fun appliesTo(folderType: ResourceFolderType): Boolean = + folderType == ResourceFolderType.VALUES + + override fun getApplicableElements(): List = listOf(TAG_STRING) + + override fun visitElement(context: XmlContext, element: Element) { + val name = element.getAttribute(ATTR_NAME) + if (name.isEmpty()) return + if (element.getAttribute(ATTR_TRANSLATABLE) == VALUE_FALSE) return + + // Compare rendered text rather than markup: a value that differs from the base only in + // its xliff placeholders is still untranslated copy. + val text = element.textContent.trim() + if (text.isEmpty()) return + if (!hasTranslatableWords(text)) return + + val folderName = context.file.parentFile?.name ?: return + + if (folderName == BASE_VALUES_FOLDER) { + // putIfAbsent rather than assignment: if a second source set ever contributes its + // own values/ folder, first-wins keeps the map from depending on traversal order. + baseStrings.putIfAbsent(name, text) + return + } + + // Only the base folder above seeds the comparison. Any other unqualified folder + // (values-v26, values-sw360dp, a future values-night) is a configuration variant of the + // English copy, not a translation, so it is neither a source nor a candidate. + val locale = FolderConfiguration.getConfigForFolder(folderName)?.localeQualifier ?: return + + if (locale.language == LANGUAGE_ENGLISH) return + if (name in ALLOWED) return + + localized += LocalizedString( + name = name, + folder = folderName, + text = text, + handle = context.createLocationHandle(element) + ) + } + + override fun afterCheckRootProject(context: Context) { + // Nothing to compare against when lint runs over a single file, which is the IDE's + // incremental mode. Reporting there would flag every locale string in the file. + if (baseStrings.isEmpty()) return + + for (string in localized) { + if (baseStrings[string.name] != string.text) continue + context.report( + UNTRANSLATED_RESOURCE, + string.handle.resolve(), + "\"${string.name}\" is the base English string in ${string.folder}, so it " + + "ships untranslated. Translate it, or mark it `tools:ignore=" + + "\"$ISSUE_ID\"` if the translation is genuinely identical." + ) + } + + baseStrings.clear() + localized.clear() + } + + /** + * Whether [text] contains anything a translator could change. + * + * Strings made only of placeholders, punctuation and whitespace have no words to translate, + * so they are necessarily identical in every locale folder that defines them. No base string + * is placeholder-only today; the last one, `fui_tos_and_pp_footer`, went with the unreferenced + * resources, so this guard is currently exercised only by its own test. + * + * Escape sequences are stripped first because they are spelled with letters. A non-breaking + * space is written in these files as a backslash followed by u00A0, and the u and the A in + * that sequence would otherwise read as translatable content. + */ + private fun hasTranslatableWords(text: String): Boolean = + text.replace(UNICODE_ESCAPE, "") + .replace(FORMAT_SPECIFIER, "") + .any(Char::isLetter) + + companion object { + private const val ISSUE_ID = "UntranslatedResource" + private const val LANGUAGE_ENGLISH = "en" + private const val BASE_VALUES_FOLDER = "values" + + /** `%s`, `%d`, `%1$s` and friends. */ + private val FORMAT_SPECIFIER = Regex("""%(\d+\$)?[-#+ 0,(]*\d*(\.\d+)?[a-zA-Z%]""") + + /** Matches the escape sequence as written in the file, not the character it denotes. */ + private val UNICODE_ESCAPE = Regex("""\\u[0-9a-fA-F]{4}""") + + /** + * Names exempted wholesale, because suppressing them per folder would mean roughly 375 + * `tools:ignore` attributes. + * + * The four `fui_idp_name_*` entries are brand names, which are not translated. Note this + * does make a regression invisible: if a locale ever replaced one with a mistranslation, + * nothing here would report it. That is not hypothetical. `values-fil` and `values-tl` + * spelled `fui_idp_name_facebook` as `Fecebook` from the original translation import + * (#771) until this change corrected it, and an exemption is exactly why no gate would + * have caught it. + */ + private val ALLOWED = setOf( + "fui_idp_name_facebook", + "fui_idp_name_github", + "fui_idp_name_google", + "fui_idp_name_twitter" + ) + + val UNTRANSLATED_RESOURCE = Issue.create( + ISSUE_ID, + "Localized string still holds the base English text", + "A string that is present in a locale folder but identical to the base English " + + "value ships as English to users of that locale. `MissingTranslation` cannot " + + "catch this, because it only reports strings that are absent.", + Category.MESSAGES, + 6, + Severity.ERROR, + Implementation(UntranslatedResourceDetector::class.java, Scope.ALL_RESOURCES_SCOPE) + ) + } +} diff --git a/internal/lint/src/test/java/com/firebaseui/lint/internal/NonGlobalIdDetectorTest.kt b/internal/lint/src/test/java/com/firebaseui/lint/internal/NonGlobalIdDetectorTest.kt index 5bdf360c3b..35269ca2ca 100644 --- a/internal/lint/src/test/java/com/firebaseui/lint/internal/NonGlobalIdDetectorTest.kt +++ b/internal/lint/src/test/java/com/firebaseui/lint/internal/NonGlobalIdDetectorTest.kt @@ -4,21 +4,10 @@ import com.android.tools.lint.checks.infrastructure.TestFiles.xml import com.android.tools.lint.checks.infrastructure.TestLintTask import com.firebaseui.lint.internal.NonGlobalIdDetector.Companion.NON_GLOBAL_ID import org.junit.Test -import java.io.File class NonGlobalIdDetectorTest { - // Nasty hack to make lint tests pass on Windows. For some reason, lint doesn't - // automatically find the Android SDK in its standard path on Windows. This hack looks - // through the system properties to find the path defined in `local.properties` and then - // sets lint's SDK home to that path if it's found. - private val sdkPath = System.getProperty("java.library.path").split(';').find { - it.contains("SDK", true) - } - - fun configuredLint(): TestLintTask = TestLintTask.lint().apply { - sdkHome(File(sdkPath ?: return@apply)) - } + fun configuredLint(): TestLintTask = TestLintTask.lint().allowMissingSdk() @Test fun `Passes on valid view id`() { diff --git a/internal/lint/src/test/java/com/firebaseui/lint/internal/UntranslatedResourceDetectorTest.kt b/internal/lint/src/test/java/com/firebaseui/lint/internal/UntranslatedResourceDetectorTest.kt new file mode 100644 index 0000000000..0c2c52c419 --- /dev/null +++ b/internal/lint/src/test/java/com/firebaseui/lint/internal/UntranslatedResourceDetectorTest.kt @@ -0,0 +1,184 @@ +package com.firebaseui.lint.internal + +import com.android.tools.lint.checks.infrastructure.TestFiles.xml +import com.android.tools.lint.checks.infrastructure.TestLintTask +import com.firebaseui.lint.internal.UntranslatedResourceDetector.Companion.UNTRANSLATED_RESOURCE +import org.junit.Test + +class UntranslatedResourceDetectorTest { + + private fun configuredLint(): TestLintTask = TestLintTask.lint().allowMissingSdk() + + private fun base(vararg entries: String) = xml( + "res/values/strings.xml", + """ + | + |${entries.joinToString("\n") { " $it" }} + |""".trimMargin() + ) + + private fun locale(folder: String, vararg entries: String) = xml( + "res/values-$folder/strings.xml", + """ + | + |${entries.joinToString("\n") { " $it" }} + |""".trimMargin() + ) + + @Test + fun `Passes on a translated string`() { + configuredLint() + .files( + base("""Sign in"""), + locale("fr", """Se connecter""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Fails on a string left as base English`() { + configuredLint() + .files( + base("""Authentication Error"""), + locale( + "zh-rTW", + """Authentication Error""" + ) + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectErrorCount(1) + } + + @Test + fun `Passes on English regional folders`() { + configuredLint() + .files( + base("""Sign in"""), + locale("en-rGB", """Sign in""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Passes on allowlisted brand names`() { + configuredLint() + .files( + base("""Google"""), + locale("fr", """Google""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Passes on non-translatable strings`() { + configuredLint() + .files( + base( + """Sign in""", + """Internal""" + ), + locale( + "fr", + """Se connecter""", + """Internal""" + ) + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Passes on a BCP47 locale folder with a real translation`() { + configuredLint() + .files( + base("""Sign in"""), + locale("b+es+419", """Iniciar sesión""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Fails on a BCP47 locale folder left as base English`() { + configuredLint() + .files( + base("""Sign in"""), + locale("b+es+419", """Sign in""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectErrorCount(1) + } + + @Test + fun `Passes on a placeholder-only string`() { + configuredLint() + .files( + base( + """Sign in""", + """%1${'$'}s \u00A0 %2${'$'}s""" + ), + locale( + "fr", + """Se connecter""", + """%1${'$'}s \u00A0 %2${'$'}s""" + ) + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Passes when the string carries a tools ignore`() { + configuredLint() + .files( + base("""Password"""), + xml( + "res/values-it/strings.xml", + """ + | + | Password + |""".trimMargin() + ) + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + /** + * Only `values/` seeds the comparison. A string defined solely in a configuration variant is + * not a base string, so a locale copying it is not reported. + * + * The fixture deliberately keeps `fui_sign_in` out of `values/`: if the detector treated any + * unqualified folder as the base, `values-v26` would seed it and `values-fr` would report. + */ + @Test + fun `Does not treat a non-locale configuration folder as the base`() { + configuredLint() + .files( + base("""Unrelated"""), + xml( + "res/values-v26/strings.xml", + """ + | + | Sign in + |""".trimMargin() + ), + locale("fr", """Sign in""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } +} diff --git a/internal/lintchecks/build.gradle.kts b/internal/lintchecks/build.gradle.kts index fc24e7762f..aec59f0529 100644 --- a/internal/lintchecks/build.gradle.kts +++ b/internal/lintchecks/build.gradle.kts @@ -23,21 +23,6 @@ android { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } - - lint { - // Common lint options across all modules - disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor" // We almost never care about this - ) - - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - - } } dependencies { diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 8cad8e6b05..d709bba4a6 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -23,20 +23,6 @@ android { targetCompatibility = JavaVersion.VERSION_1_8 } - lint { - // Common lint options across all modules - disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor" // We almost never care about this - ) - - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - - } } dependencies { diff --git a/okf-bundle/ci-workflows/android.md b/okf-bundle/ci-workflows/android.md index 92631d23ea..7463b44e76 100644 --- a/okf-bundle/ci-workflows/android.md +++ b/okf-bundle/ci-workflows/android.md @@ -35,10 +35,23 @@ Canonical owner for the CI unit-path step list. Script: [scripts/build.sh](../.. 1. Copy `library/google-services.json` → `app/` and `proguard-tests/` 2. `./gradlew --max-workers=2 clean` 3. `./gradlew --max-workers=2 assembleDebug` -4. `./gradlew --max-workers=2 checkstyle` -5. `./gradlew --max-workers=2 testDebugUnitTest -x :e2eTest:testDebugUnitTest` +4. `./gradlew --max-workers=2 proguard-tests:build` — the R8 gate, [detail below](#proguard-step) +5. `./gradlew --max-workers=2 checkstyle` +6. `./gradlew --max-workers=2 testDebugUnitTest -x :e2eTest:testDebugUnitTest` -Step 4 is **Java-only** (`include("**/*.java")`), so it inspects zero files in the Kotlin modules — [Kotlin blind spot](../testing/agent-command-policy.md#checkstyle-kotlin-blind-spot). Android Lint covers that gap and runs in its own workflow, **not** in `build.sh` — see below. +Step 5 is **Java-only** (`include("**/*.java")`), so it inspects zero files in the Kotlin modules — [Kotlin blind spot](../testing/agent-command-policy.md#checkstyle-kotlin-blind-spot). Android Lint covers that gap and runs in its own workflow, **not** in `build.sh` — see below. + + + +### Step 4: the R8 gate + +`:proguard-tests` is a source-less application module that depends on `:auth`, `:firestore`, `:database` and `:storage` and builds its release variant with `isMinifyEnabled = true`, so `minifyReleaseWithR8` is the only place the libraries' consumer ProGuard rules are ever applied. Each library's own release build sets `isMinifyEnabled = false`. `consumerProguardFiles` is declared by `:auth` (`auth-proguard.pro`), `:firestore` and `:database` (`proguard-rules.pro` each); `:storage` and `:common` declare none, though AGP also merges rules generated by annotation processors. The gate is sensitive: removing `-dontwarn com.facebook.**` from `auth-proguard.pro` fails `minifyReleaseWithR8`, because `:auth` takes `facebook-login` as `compileOnly`. It proves the rules shrink and package, not that the shrunk app behaves correctly at runtime. + +The module's `beforeVariants` filter disables its **debug** variant when `CI=true`, so CI builds the release variant only; a local run of the script builds both. + + + +**Lint footprint.** `build` depends on `check`, which depends on `lint`, so this step runs Android Lint too. On CI that is `:proguard-tests:lintRelease`; locally, with `CI` unset, it is `:proguard-tests:lintDebug` — the ungated debug variant [`lintAll` does not cover](../testing/agent-command-policy.md#canonical-registry), so a debug-only finding can redden a local `build.sh` while CI stays green. Either way lint **analysis** fans out to the library dependencies (`:auth`, `:common`, `:database`, `:firestore`, `:storage`, plus `:internal:lintchecks` locally, since `:auth` takes it as `debugImplementation`), because AGP builds a lint model for each; only the **report** is scoped to `:proguard-tests`. Two consequences: it duplicates analysis [`lintAll`](#lint-workflow) already does, and since the step sits at position 4 of 6 under `set -e`, a finding here aborts the run before `checkstyle` and before every unit-test result — the same masking that kept lint out of this script in the first place (below). Drop to `:proguard-tests:assembleRelease` if that trade stops being worth it. @@ -46,14 +59,12 @@ Step 4 is **Java-only** (`include("**/*.java")`), so it inspects zero files in t Separate workflow, `pull_request` only, running `./gradlew --max-workers=2 lintAll`. -`lintAll` is registered in the root `build.gradle.kts` and gates the 8 modules that configure a `lint { }` block; `:app` and `:e2eTest` are not yet among them (CPRN-433). +`lintAll` is registered in the root `build.gradle.kts` and gates all 10 Android modules. The strictness flags and the common `disable` set live in a shared policy in that same file, applied to every module that applies the application or library plugin; a module's own `lint { }` block carries only its module-specific disables. It is a separate workflow rather than a step in `build.sh` for two reasons. Lint measured **~4-5 minutes** on this repo — the `build` job went from 3-6 min to 8-11 min when it was inline — so running it in parallel roughly halves PR feedback time at about the same total runner cost, since the extra compile the lint job pays is the one `build.sh` stops paying (`lintAnalyze` depends on `compileDebugKotlin`, and there is no remote build cache: Develocity here is configured for build scans only). And under `set -e` an inline lint failure aborted the run **before** `testDebugUnitTest`, so one new finding cost you every test result for that run. The workflow copies `library/google-services.json` into `app/` and `proguard-tests/` before running, because `lintAll` gates `:proguard-tests:lintRelease` and that module applies the `google-services` plugin. -`proguard-tests:build` is currently commented out (re-enable before release). Green Android CI does **not** prove ProGuard/R8 packaging. - ## Agent notes - Match this path locally with `./scripts/build.sh` — [agent command policy](../testing/agent-command-policy.md). diff --git a/okf-bundle/modules/index.md b/okf-bundle/modules/index.md index 7c5f368643..92a6208457 100644 --- a/okf-bundle/modules/index.md +++ b/okf-bundle/modules/index.md @@ -21,7 +21,7 @@ Version and SDK floors: `buildSrc/.../Config.kt` — [repo tooling](../repo-tool | `:library` | Umbrella / publish aggregation (`prepareArtifacts`) | | `:app` | Demo app (Auth Compose sample) | | `:e2eTest` | Auth emulator e2e (Robolectric + Compose UI test) | -| `:proguard-tests` | R8/ProGuard packaging checks (disabled in CI unit path — [Android CI](../ci-workflows/android.md)) | +| `:proguard-tests` | R8/ProGuard packaging gate for the libraries' consumer rules; run by `build.sh` — [Android CI](../ci-workflows/android.md#proguard-step) | | `:lint`, `:internal:lint`, `:internal:lintchecks` | Custom lint detectors — [what each is for](#custom-lint-modules) | | `buildSrc` | Shared `Config` (version, SDK levels, submodule list) | diff --git a/okf-bundle/testing/agent-command-policy.md b/okf-bundle/testing/agent-command-policy.md index 5e052d9246..eded881730 100644 --- a/okf-bundle/testing/agent-command-policy.md +++ b/okf-bundle/testing/agent-command-policy.md @@ -26,12 +26,12 @@ Single source for **which shell commands agents may run** in this repo. E2e is a | Intent | Command | Never use instead | |--------|---------|-------------------| -| Full CI unit path (assemble + checkstyle + unit tests) | `./scripts/build.sh` | Ad-hoc `./gradlew clean assembleDebug test` without checkstyle; inventing a different exclusion set | +| Full CI unit path (assemble + R8 + checkstyle + unit tests) | `./scripts/build.sh` | Ad-hoc `./gradlew clean assembleDebug test` without checkstyle; inventing a different exclusion set | | Unit tests (all library modules; exclude e2eTest) | `./gradlew testDebugUnitTest -x :e2eTest:testDebugUnitTest` | Bare `./gradlew test` (pulls wrong tasks / e2e); IDE-only as the agent gate | | Unit tests (one module with a real `src/test` suite) | `./gradlew ::testDebugUnitTest` (e.g. `:auth:testDebugUnitTest`, `:firestore:…`, `:storage:…`) | `:common:testDebugUnitTest` / `:database:testDebugUnitTest` as “green” evidence (empty suites — [empty unit-suite trap](#empty-unit-suite-trap)); full suite when only one module changed *as a substitute for* the CI path at handoff | | Assemble one module (when no JVM unit suite) | `./gradlew ::assembleDebug` (e.g. `:database`, `:common`) | Treating empty `testDebugUnitTest` as validation | | Checkstyle (**Java only**) | `./gradlew checkstyle` | Invented ktlint/detekt entrypoints; treating a green checkstyle as style coverage for Kotlin sources ([Kotlin blind spot](#checkstyle-kotlin-blind-spot)) | -| Android Lint (all gated modules) — **not** in `build.sh`, own workflow | `./gradlew lintAll` | Bare `./gradlew lint` / `lintDebug` (pulls `:app` and `:e2eTest`, which declare no `lint { }` block yet); assuming a green `build.sh` covered lint | +| Android Lint (all gated modules) — **not** in `build.sh`, own workflow | `./gradlew lintAll` | Bare `./gradlew lint` / `lintDebug` (pulls variants `lintAll` does not gate, e.g. `:proguard-tests` debug); assuming a green `build.sh` covered lint | | Android Lint (one module) | `./gradlew ::lintDebug` (`:proguard-tests` uses `lintRelease`) | Editing Kotlin in a gated module without re-running lint | | Accept new lint debt (**needs a human decision**) | `./gradlew :auth:updateLintBaseline` | Running this to make a red build green — see [lint baseline trap](#lint-baseline-trap) | | Assemble debug | `./gradlew assembleDebug` | Module-scoped assemble as the only CI substitute at handoff | @@ -122,9 +122,10 @@ Single source for **which shell commands agents may run** in this repo. E2e is a - [.github/PULL_REQUEST_TEMPLATE.md](../../.github/PULL_REQUEST_TEMPLATE.md) mentions `./gradlew check` (stale vs current CI). - **Agents:** treat **`./scripts/build.sh`** as the CI-matching unit path — [Android CI](../ci-workflows/android.md). Full handoff (including e2e when Auth UI touched): [validation checklist](validation-checklist.md). -### ProGuard tests disabled in build.sh +### `proguard-tests:build` is the only R8 gate -- See [Android CI § `build.sh`](../ci-workflows/android.md#what-buildsh-runs) — `proguard-tests:build` is commented out; green unit CI does not prove ProGuard/R8. +- `build.sh` runs it, and it is the only task that applies the libraries' consumer ProGuard rules — [Android CI § the ProGuard step](../ci-workflows/android.md#proguard-step). +- Do **not** comment it out to clear a red build. A failure there is a real defect in the consumer rules or in code they must keep; fix the rules. ### Emulator foreground vs CI diff --git a/okf-bundle/testing/validation-checklist.md b/okf-bundle/testing/validation-checklist.md index f8764672e1..f04eca27b4 100644 --- a/okf-bundle/testing/validation-checklist.md +++ b/okf-bundle/testing/validation-checklist.md @@ -26,7 +26,7 @@ Work types and tiers: [change authoring workflow](change-authoring-workflow.md). ## Build and unit tests -Repo root. Full CI unit path (what `build.sh` runs — `assembleDebug`, `checkstyle`, unit tests): [Android CI](../ci-workflows/android.md). Lint and e2e are **separate** workflows; `build.sh` does not run them. +Repo root. Full CI unit path (what `build.sh` runs — `assembleDebug`, `proguard-tests:build`, `checkstyle`, unit tests): [Android CI](../ci-workflows/android.md). Lint and e2e are **separate** workflows; `build.sh` does not run `lintAll` or `e2eTest`. It is not lint-free, though: `proguard-tests:build` pulls that module's lint through `check`, and lint **analysis** with it for the library dependencies (`:auth`, `:common`, `:database`, `:firestore`, `:storage`, and `:internal:lintchecks` on a local run) — [lint footprint](../ci-workflows/android.md#proguard-step-lint). Findings are reported for `:proguard-tests` only, so a lint failure during `build.sh` is still that module's gate, not `lintAll`'s. ```bash ./scripts/build.sh @@ -62,9 +62,9 @@ Instrumented `androidTest` (database/firestore) is **not** in CI or the agent al ./gradlew lintAll # Android Lint — reads Kotlin and resources ``` -`checkstyle` is scoped `include("**/*.java")` from the root `build.gradle.kts`, so on a Kotlin-only diff it inspects **zero files and exits 0**. A green checkstyle is not evidence for a change in `:auth`, `:app` or `:e2eTest` — [Kotlin blind spot](agent-command-policy.md#checkstyle-kotlin-blind-spot). +`checkstyle` is scoped `include("**/*.java")` from the root `build.gradle.kts`, so on a Kotlin-only diff it inspects **zero files and exits 0**. A green checkstyle is not evidence for a change in `:auth`, `:app` or `:e2eTest`; `lintAll` is what covers those — [Kotlin blind spot](agent-command-policy.md#checkstyle-kotlin-blind-spot). -`lintAll` runs Android Lint for the 8 modules that configure a `lint { }` block, each at `checkAllWarnings = true`, `warningsAsErrors = true` and `abortOnError = true` — so any new finding fails the build. It runs in its own workflow ([lint.yml](../ci-workflows/android.md#lint-workflow)), **not** in `build.sh`, so you must run it separately — a green `build.sh` says nothing about lint. `:app` and `:e2eTest` are not yet gated (CPRN-433). Config: each module's `lint { }` block; `library/quality/checkstyle.xml` for checkstyle. +`lintAll` runs Android Lint for all 10 Android modules at `checkAllWarnings = true`, `warningsAsErrors = true` and `abortOnError = true` — so any new finding fails the build. It runs in its own workflow ([lint.yml](../ci-workflows/android.md#lint-workflow)), **not** in `build.sh`, so you must run it separately — a green `build.sh` covers only `:proguard-tests`' own findings (see above), never the other nine modules'. Config: the shared policy in the root `build.gradle.kts` sets those flags and the common `disable` set, and a module's own `lint { }` block adds only its module-specific disables; `library/quality/checkstyle.xml` for checkstyle. `auth/lint-baseline.xml` suppresses 180 pre-existing findings. **Never** run `updateLintBaseline` to clear a failure your change caused — [baseline trap](agent-command-policy.md#lint-baseline-trap). @@ -114,7 +114,7 @@ Before closing **`implementation_gate`**, **`review_gate`**, **`commit_gate`**, ## Handoff checklist -- [ ] `./scripts/build.sh` (or equivalent assemble + checkstyle + unit exclusion path) exit 0 +- [ ] `./scripts/build.sh` (or equivalent assemble + R8 + checkstyle + unit exclusion path) exit 0 - [ ] Module evidence per [module validation matrix](#module-validation-matrix) - [ ] `./gradlew checkstyle` when **Java** sources changed - [ ] `./gradlew lintAll` when Kotlin or resources changed in a gated module diff --git a/proguard-tests/build.gradle.kts b/proguard-tests/build.gradle.kts index 0a419b7402..d76d08a70c 100644 --- a/proguard-tests/build.gradle.kts +++ b/proguard-tests/build.gradle.kts @@ -46,20 +46,11 @@ android { } lint { - // Common lint options across all modules + // Module specific disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor", // We almost never care about this "MediaCapabilities", "MissingApplicationIcon" ) - - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - } androidComponents { diff --git a/scripts/build.sh b/scripts/build.sh index b216f04b76..c57a0cbcad 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -8,10 +8,13 @@ cp library/google-services.json proguard-tests/google-services.json ./gradlew $GRADLE_ARGS clean ./gradlew $GRADLE_ARGS assembleDebug -# TODO(thatfiredev): re-enable before release -# ./gradlew $GRADLE_ARGS proguard-tests:build +./gradlew $GRADLE_ARGS proguard-tests:build ./gradlew $GRADLE_ARGS checkstyle # Android Lint is the Kotlin-capable gate, but it runs in its own workflow # (.github/workflows/lint.yml) so it runs in parallel with this path rather than # adding ~5 minutes to it, and so a lint failure does not mask unit-test results. ./gradlew $GRADLE_ARGS testDebugUnitTest -x :e2eTest:testDebugUnitTest +# :internal:lint is a JVM module, so it has `test` rather than `testDebugUnitTest` and is +# absent from the line above. Its detectors are only exercised here: a green lintAll does +# not prove them, because it passes just as happily if a detector stops reporting. +./gradlew $GRADLE_ARGS :internal:lint:test diff --git a/storage/build.gradle.kts b/storage/build.gradle.kts index 0e6f8af175..af04a43c78 100644 --- a/storage/build.gradle.kts +++ b/storage/build.gradle.kts @@ -23,21 +23,6 @@ android { targetCompatibility = JavaVersion.VERSION_1_8 } - lint { - // Common lint options across all modules - disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor" // We almost never care about this - ) - - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - - } - buildTypes { named("release").configure { isMinifyEnabled = false @@ -55,4 +40,4 @@ dependencies { testImplementation(libs.junit) testImplementation(libs.mockito.core) -} \ No newline at end of file +}