From bd150bbdc550197d38cbe05def38e79d008b0680 Mon Sep 17 00:00:00 2001 From: Marcelo Soares Date: Thu, 16 Jul 2026 00:16:23 -0300 Subject: [PATCH 1/3] docs: Document sign-in localization --- .../04-authentication/06-ui-components.md | 90 ++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/docs/06-concepts/04-authentication/06-ui-components.md b/docs/06-concepts/04-authentication/06-ui-components.md index 7e6202c2..673e0869 100644 --- a/docs/06-concepts/04-authentication/06-ui-components.md +++ b/docs/06-concepts/04-authentication/06-ui-components.md @@ -52,7 +52,93 @@ class SignInPage extends StatelessWidget { } ``` -### Disabling Providers +### Localize sign-in text + +Wrap `SignInWidget`, or individual authentication widgets, in a `SignInLocalizationProvider` to replace their built-in English text. The following example supplies Brazilian Portuguese text for the common UI, the complete email flow, password requirements, and each supported provider button: + +```dart +const basicTexts = BasicSignInTexts( + noAuthenticationProvidersConfigured: + 'Nenhum provedor de autenticação configurado', + orContinueWith: 'ou continue com', +); + +const emailTexts = EmailSignInTexts( + title: 'Entrar com e-mail', + forgotPassword: 'Esqueceu sua senha?', + signIn: 'Entrar', + dontHaveAnAccount: 'Não tem uma conta?', + signUp: 'Criar conta', + signUpTitle: 'Criar conta com e-mail', + continueAction: 'Continuar', + alreadyHaveAnAccount: 'Já tem uma conta?', + verifyAccountTitle: 'Verificar conta', + verifyResetCodeTitle: 'Verificar código de redefinição', + verificationMessage: + 'Enviamos um e-mail de verificação. Confira sua caixa de entrada e ' + 'digite o código abaixo.', + verify: 'Verificar', + setAccountPasswordTitle: 'Definir senha da conta', + passwordLabel: 'Senha', + backToSignUp: 'Voltar para criar conta', + setNewPasswordTitle: 'Definir nova senha', + newPasswordLabel: 'Nova senha', + resetPasswordTitle: 'Redefinir senha', + resetPasswordDescription: + 'Digite o endereço de e-mail para redefinir sua senha.', + requestPasswordReset: 'Solicitar redefinição de senha', + resetPassword: 'Redefinir senha', + backToSignIn: 'Voltar para entrar', + emailLabel: 'E-mail', + termsIntro: 'Li e aceito os ', + termsAndConditions: 'Termos e Condições', + andText: ' e a ', + privacyPolicy: 'Política de Privacidade', +); + +const passwordTexts = PasswordRequirementTexts( + minLengthTemplate: 'Pelo menos {length} caracteres', + maxLengthTemplate: 'No máximo {length} caracteres', + containsLowercase: 'Contém pelo menos uma letra minúscula', + containsUppercase: 'Contém pelo menos uma letra maiúscula', + containsNumber: 'Contém pelo menos um número', + containsSpecialCharacter: 'Contém pelo menos um caractere especial', +); + +SignInLocalizationProvider( + basic: basicTexts, + email: emailTexts, + passwordRequirementTexts: passwordTexts, + apple: const AppleSignInTexts(signInButton: 'Entrar com Apple'), + google: const GoogleSignInTexts(signInButton: 'Entrar com Google'), + github: const GitHubSignInTexts(signInButton: 'Entrar com GitHub'), + microsoft: const MicrosoftSignInTexts(signInButton: 'Entrar com Microsoft'), + facebook: const FacebookSignInTexts(signInButton: 'Entrar com Facebook'), + anonymous: const AnonymousSignInTexts(signInButton: 'Entrar como visitante'), + child: SignInWidget( + client: client, + onAuthenticated: onAuthenticated, + onError: onError, + ), +); +``` + +`BasicSignInTexts` covers shared messages and dividers. `EmailSignInTexts` covers email sign-in, registration, account verification, password reset, and terms and privacy labels. The provider-specific objects cover the Apple, Google, GitHub, Microsoft, Facebook, and anonymous sign-in buttons. `PasswordRequirementTexts` controls the labels shown by built-in password requirement widgets. Keep the `{length}` placeholder in the minimum and maximum length templates. + +Every text object has a `defaults` constant with the built-in English text. If you omit an object from `SignInLocalizationProvider`, its default is used. To change only some values, start from the defaults: + +```dart +email: EmailSignInTexts.defaults.copyWith( + title: 'Entrar com e-mail', + signIn: 'Entrar', +), +``` + +`SignInLocalizationProvider` supplies text to its descendant widgets. It does not select translations from Flutter's current `Locale`. If your app supports several locales, choose the matching text objects through your app's localization setup and rebuild the provider when the locale changes. + +Firebase authentication is not covered by these text objects. The `serverpod_auth_idp_flutter_firebase` package supplies a controller for the Firebase flow, so localize the Flutter UI that you connect to that controller. + +### Disable providers It is not possible to forcefully enable a provider, since this depends on the configuration of the identity providers, as described in the [setup section](./setup#identity-providers-configuration). @@ -75,7 +161,7 @@ SignInWidget( ) ``` -### Customizing SignInWidget +### Customize SignInWidget You can customize individual provider widgets: From 582b1c6ae99d8d04be80b80123c95d84b42b995e Mon Sep 17 00:00:00 2001 From: Marcelo Soares Date: Tue, 21 Jul 2026 15:38:46 -0300 Subject: [PATCH 2/3] docs: Restructure the sign-in localization section Promote localization to its own section, since SignInLocalizationProvider applies to every authentication widget and not only to SignInWidget. List the text objects before the example so the reader can read the code, split the partial translation and locale notes into their own subsections, and keep the existing heading wording intact. --- .../04-authentication/06-ui-components.md | 178 ++++++++++-------- 1 file changed, 97 insertions(+), 81 deletions(-) diff --git a/docs/06-concepts/04-authentication/06-ui-components.md b/docs/06-concepts/04-authentication/06-ui-components.md index 673e0869..9ec4fe4b 100644 --- a/docs/06-concepts/04-authentication/06-ui-components.md +++ b/docs/06-concepts/04-authentication/06-ui-components.md @@ -52,9 +52,88 @@ class SignInPage extends StatelessWidget { } ``` -### Localize sign-in text +### Disabling providers -Wrap `SignInWidget`, or individual authentication widgets, in a `SignInLocalizationProvider` to replace their built-in English text. The following example supplies Brazilian Portuguese text for the common UI, the complete email flow, password requirements, and each supported provider button: +It is not possible to forcefully enable a provider, since this depends on the configuration of the identity providers, as described in the [setup section](./setup#identity-providers-configuration). + +But, even though the `SignInWidget` automatically detects enabled providers, you can disable specific providers if you want to hide them on the client, but still keep them available on the server. + +This is useful if you want to gradually disable a provider, but still keep compatibility with older clients. + +```dart +SignInWidget( + client: client, + disableEmailSignInWidget: false, + disableGoogleSignInWidget: false, + disableAppleSignInWidget: true, // Disable Apple sign-in + onAuthenticated: () { + // Do something when the user is authenticated. + // + // NOTE: You should not navigate to the home screen here, otherwise + // the user will have to sign in again every time they open the app. + }, +) +``` + +### Customizing SignInWidget + +You can customize individual provider widgets: + +```dart +final signInWidget = SignInWidget( + client: client, + emailSignInWidget: EmailSignInWidget( + client: client, + startScreen: EmailFlowScreen.login, + // NOTE: When you opt-out of the internal widget, you need to provide your + // own `onAuthenticated` and `onError` callbacks. + onAuthenticated: _onAuthenticated, + onError: _onError, + // ... custom configuration + ), + googleSignInWidget: GoogleSignInWidget( + client: client, + theme: GSIButtonTheme.filledBlack, + scopes: const [ + ...GoogleAuthController.defaultScopes, + 'https://www.googleapis.com/auth/youtube', + ], + onAuthenticated: _onAuthenticated, + onError: _onError, + // ... custom configuration + ), + appleSignInWidget: AppleSignInWidget( + client: client, + style: AppleButtonStyle.black, + onAuthenticated: _onAuthenticated, + onError: _onError, + // ... custom configuration + ), + onAuthenticated: _onAuthenticated, + onError: _onError, +); + +void _onAuthenticated() { + // Handle successful authentication +} + +void _onError(Object error) { + // Handle errors +} +``` + +For more details on all options of each provider widget, see the "Customizing UI" section of the specific provider documentation. There you will also find information on how to build a custom UI with the controller. For example, see the [Email Provider](./providers/email/customizing-the-ui) documentation. + +## Localization + +All text shown by the authentication widgets is in English by default. To replace it, wrap `SignInWidget`, or any individual provider widget, in a `SignInLocalizationProvider` and pass the text objects for the parts you want to translate: + +- `BasicSignInTexts` for the shared messages and dividers. +- `EmailSignInTexts` for the email flow: sign-in, registration, account verification, password reset, and the terms and privacy labels. +- `PasswordRequirementTexts` for the labels of the built-in password requirement widgets. +- One text object per provider for the sign-in buttons: `AppleSignInTexts`, `GoogleSignInTexts`, `GitHubSignInTexts`, `MicrosoftSignInTexts`, `FacebookSignInTexts`, and `AnonymousSignInTexts`. + +The example below translates the full set into Brazilian Portuguese: ```dart const basicTexts = BasicSignInTexts( @@ -117,95 +196,32 @@ SignInLocalizationProvider( anonymous: const AnonymousSignInTexts(signInButton: 'Entrar como visitante'), child: SignInWidget( client: client, - onAuthenticated: onAuthenticated, - onError: onError, + onAuthenticated: _onAuthenticated, + onError: _onError, ), ); ``` -`BasicSignInTexts` covers shared messages and dividers. `EmailSignInTexts` covers email sign-in, registration, account verification, password reset, and terms and privacy labels. The provider-specific objects cover the Apple, Google, GitHub, Microsoft, Facebook, and anonymous sign-in buttons. `PasswordRequirementTexts` controls the labels shown by built-in password requirement widgets. Keep the `{length}` placeholder in the minimum and maximum length templates. - -Every text object has a `defaults` constant with the built-in English text. If you omit an object from `SignInLocalizationProvider`, its default is used. To change only some values, start from the defaults: - -```dart -email: EmailSignInTexts.defaults.copyWith( - title: 'Entrar com e-mail', - signIn: 'Entrar', -), -``` - -`SignInLocalizationProvider` supplies text to its descendant widgets. It does not select translations from Flutter's current `Locale`. If your app supports several locales, choose the matching text objects through your app's localization setup and rebuild the provider when the locale changes. - -Firebase authentication is not covered by these text objects. The `serverpod_auth_idp_flutter_firebase` package supplies a controller for the Firebase flow, so localize the Flutter UI that you connect to that controller. - -### Disable providers - -It is not possible to forcefully enable a provider, since this depends on the configuration of the identity providers, as described in the [setup section](./setup#identity-providers-configuration). - -But, even though the `SignInWidget` automatically detects enabled providers, you can disable specific providers if you want to hide them on the client, but still keep them available on the server. - -This is useful if you want to gradually disable a provider, but still keep compatibility with older clients. - -```dart -SignInWidget( - client: client, - disableEmailSignInWidget: false, - disableGoogleSignInWidget: false, - disableAppleSignInWidget: true, // Disable Apple sign-in - onAuthenticated: () { - // Do something when the user is authenticated. - // - // NOTE: You should not navigate to the home screen here, otherwise - // the user will have to sign in again every time they open the app. - }, -) -``` +The `{length}` placeholder in the two length templates is replaced with the configured limit, so keep it in the translated string. -### Customize SignInWidget +### Translating part of the text -You can customize individual provider widgets: +Any text object you leave out keeps its built-in English text, so you only need to pass the ones you are translating. To change a few values inside an object, start from its `defaults` constant: ```dart -final signInWidget = SignInWidget( - client: client, - emailSignInWidget: EmailSignInWidget( - client: client, - startScreen: EmailFlowScreen.login, - // NOTE: When you opt-out of the internal widget, you need to provide your - // own `onAuthenticated` and `onError` callbacks. - onAuthenticated: _onAuthenticated, - onError: _onError, - // ... custom configuration - ), - googleSignInWidget: GoogleSignInWidget( - client: client, - theme: GSIButtonTheme.filledBlack, - scopes: const [ - ...GoogleAuthController.defaultScopes, - 'https://www.googleapis.com/auth/youtube', - ], - onAuthenticated: _onAuthenticated, - onError: _onError, - // ... custom configuration - ), - appleSignInWidget: AppleSignInWidget( - client: client, - style: AppleButtonStyle.black, - onAuthenticated: _onAuthenticated, - onError: _onError, - // ... custom configuration +SignInLocalizationProvider( + email: EmailSignInTexts.defaults.copyWith( + title: 'Entrar com e-mail', + signIn: 'Entrar', ), - onAuthenticated: _onAuthenticated, - onError: _onError, + child: signInWidget, ); +``` -void _onAuthenticated() { - // Handle successful authentication -} +### Switching locale -void _onError(Object error) { - // Handle errors -} -``` +`SignInLocalizationProvider` hands the text objects to the widgets below it. It does not read Flutter's current `Locale` or pick a translation for you. In an app that supports several locales, select the text objects with your existing localization setup and rebuild the provider when the locale changes. -For more details on all options of each provider widget, see the "Customizing UI" section of the specific provider documentation. There you will also find information on how to build a custom UI with the controller. For example, see the [Email Provider](./providers/email/customizing-the-ui) documentation. +:::note +The Firebase provider is not covered by these text objects. The `serverpod_auth_idp_flutter_firebase` package supplies a controller rather than a finished UI, so translate the widgets you build on top of it. +::: From 356cdc97d2ef5a8d5566f5ac3ed9722d57ccbaa6 Mon Sep 17 00:00:00 2001 From: Marcelo Soares Date: Tue, 21 Jul 2026 16:42:12 -0300 Subject: [PATCH 3/3] fix: Remove portuguese examples --- .../04-authentication/06-ui-components.md | 99 +++++++++---------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/docs/06-concepts/04-authentication/06-ui-components.md b/docs/06-concepts/04-authentication/06-ui-components.md index 9ec4fe4b..aef21013 100644 --- a/docs/06-concepts/04-authentication/06-ui-components.md +++ b/docs/06-concepts/04-authentication/06-ui-components.md @@ -133,67 +133,70 @@ All text shown by the authentication widgets is in English by default. To replac - `PasswordRequirementTexts` for the labels of the built-in password requirement widgets. - One text object per provider for the sign-in buttons: `AppleSignInTexts`, `GoogleSignInTexts`, `GitHubSignInTexts`, `MicrosoftSignInTexts`, `FacebookSignInTexts`, and `AnonymousSignInTexts`. -The example below translates the full set into Brazilian Portuguese: +:::note +The Firebase provider has no texts because it only supplies a controller rather than a finished UI. +::: + +The example below shows how to pass every text object explicitly: ```dart const basicTexts = BasicSignInTexts( noAuthenticationProvidersConfigured: - 'Nenhum provedor de autenticação configurado', - orContinueWith: 'ou continue com', + 'No authentication providers configured', + orContinueWith: 'or continue with', ); const emailTexts = EmailSignInTexts( - title: 'Entrar com e-mail', - forgotPassword: 'Esqueceu sua senha?', - signIn: 'Entrar', - dontHaveAnAccount: 'Não tem uma conta?', - signUp: 'Criar conta', - signUpTitle: 'Criar conta com e-mail', - continueAction: 'Continuar', - alreadyHaveAnAccount: 'Já tem uma conta?', - verifyAccountTitle: 'Verificar conta', - verifyResetCodeTitle: 'Verificar código de redefinição', + title: 'Sign In with email', + forgotPassword: 'Forgot password?', + signIn: 'Sign in', + dontHaveAnAccount: "Don't have an account?", + signUp: 'Sign up', + signUpTitle: 'Sign Up with email', + continueAction: 'Continue', + alreadyHaveAnAccount: 'Already have an account?', + verifyAccountTitle: 'Verify account', + verifyResetCodeTitle: 'Verify reset code', verificationMessage: - 'Enviamos um e-mail de verificação. Confira sua caixa de entrada e ' - 'digite o código abaixo.', - verify: 'Verificar', - setAccountPasswordTitle: 'Definir senha da conta', - passwordLabel: 'Senha', - backToSignUp: 'Voltar para criar conta', - setNewPasswordTitle: 'Definir nova senha', - newPasswordLabel: 'Nova senha', - resetPasswordTitle: 'Redefinir senha', - resetPasswordDescription: - 'Digite o endereço de e-mail para redefinir sua senha.', - requestPasswordReset: 'Solicitar redefinição de senha', - resetPassword: 'Redefinir senha', - backToSignIn: 'Voltar para entrar', - emailLabel: 'E-mail', - termsIntro: 'Li e aceito os ', - termsAndConditions: 'Termos e Condições', - andText: ' e a ', - privacyPolicy: 'Política de Privacidade', + 'A verification email has been sent. Please check your email and ' + 'enter the verification code below.', + verify: 'Verify', + setAccountPasswordTitle: 'Set account password', + passwordLabel: 'Password', + backToSignUp: 'Back to sign up', + setNewPasswordTitle: 'Set new password', + newPasswordLabel: 'New Password', + resetPasswordTitle: 'Reset password', + resetPasswordDescription: 'Enter the email address to reset password.', + requestPasswordReset: 'Request password reset', + resetPassword: 'Reset password', + backToSignIn: 'Back to sign in', + emailLabel: 'Email', + termsIntro: 'I have read and accept the ', + termsAndConditions: 'Terms and Conditions', + andText: ' and ', + privacyPolicy: 'Privacy Policy', ); const passwordTexts = PasswordRequirementTexts( - minLengthTemplate: 'Pelo menos {length} caracteres', - maxLengthTemplate: 'No máximo {length} caracteres', - containsLowercase: 'Contém pelo menos uma letra minúscula', - containsUppercase: 'Contém pelo menos uma letra maiúscula', - containsNumber: 'Contém pelo menos um número', - containsSpecialCharacter: 'Contém pelo menos um caractere especial', + minLengthTemplate: 'At least {length} characters', + maxLengthTemplate: 'At most {length} characters', + containsLowercase: 'Contains at least one lowercase letter', + containsUppercase: 'Contains at least one uppercase letter', + containsNumber: 'Contains at least one number', + containsSpecialCharacter: 'Contains at least one special character', ); SignInLocalizationProvider( basic: basicTexts, email: emailTexts, passwordRequirementTexts: passwordTexts, - apple: const AppleSignInTexts(signInButton: 'Entrar com Apple'), - google: const GoogleSignInTexts(signInButton: 'Entrar com Google'), - github: const GitHubSignInTexts(signInButton: 'Entrar com GitHub'), - microsoft: const MicrosoftSignInTexts(signInButton: 'Entrar com Microsoft'), - facebook: const FacebookSignInTexts(signInButton: 'Entrar com Facebook'), - anonymous: const AnonymousSignInTexts(signInButton: 'Entrar como visitante'), + apple: const AppleSignInTexts(signInButton: 'Sign in with Apple'), + google: const GoogleSignInTexts(signInButton: 'Sign in with Google'), + github: const GitHubSignInTexts(signInButton: 'Sign in with GitHub'), + microsoft: const MicrosoftSignInTexts(signInButton: 'Sign in with Microsoft'), + facebook: const FacebookSignInTexts(signInButton: 'Sign in with Facebook'), + anonymous: const AnonymousSignInTexts(signInButton: 'Sign in as guest'), child: SignInWidget( client: client, onAuthenticated: _onAuthenticated, @@ -211,8 +214,8 @@ Any text object you leave out keeps its built-in English text, so you only need ```dart SignInLocalizationProvider( email: EmailSignInTexts.defaults.copyWith( - title: 'Entrar com e-mail', - signIn: 'Entrar', + title: 'Sign in with email', + signIn: 'Log in', ), child: signInWidget, ); @@ -221,7 +224,3 @@ SignInLocalizationProvider( ### Switching locale `SignInLocalizationProvider` hands the text objects to the widgets below it. It does not read Flutter's current `Locale` or pick a translation for you. In an app that supports several locales, select the text objects with your existing localization setup and rebuild the provider when the locale changes. - -:::note -The Firebase provider is not covered by these text objects. The `serverpod_auth_idp_flutter_firebase` package supplies a controller rather than a finished UI, so translate the widgets you build on top of it. -:::