Introduction#
If you’ve read my previous articles, I’ll keep this one shorter. We’ve learned how to customize messages and add Amplify to our React app.
It’s good for users when the app speaks their language. You can use https://react.i18next.com/ to translate your app. I might write more about this later.
Now, how do we change the messages users get from Cognito?
Identifying the language#
One method of identifying the user’s preferred language is by asking for input, while another is automating the process and retrieve the language through browser settings.
Every modern browser provides an object accessible to developers, this object is the navigator.
Specifically, we’re interested in navigator.language.
The Navigator.language read-only property returns a string representing the preferred language of the user, usually the language of the browser UI.
The string follows the RFC 5646 standard and may resemble:
en
en-US
en-GB
de
- or any other language
The first part, such as en
, defines the language, while the second part resembles a specific region, such as British English en-GB
or American English en-US
.
Depending on your needs, you can translate it to the general language en
or a regional dialect en-GB
or en-US
.
For our use case, we require either English or German based on the browser settings. Here’s how we can retrieve it:
const language = navigator.language.split('-')[0]; // en, de, ...
Now we only need to forward this to cognito, so it knows which language we need.
Integration#
1. Updating the User Pool#
Following my previous articles, you should be able to create a Cognito User Pool with a React Amplify integration.
What you now need to do is to add locale
to standardAttributes
, so Cognito will persist this information on the User.
import {aws_cognito as cognito, CfnOutput, Stack, StackProps} from 'aws-cdk-lib';
import {Construct} from 'constructs';
export class CognitoStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const customMessageLambda = new NodejsFunction(this, 'custom-message', {
entry: 'functions/customMessage.ts',
handler: 'handler',
});
const userPool = new cognito.UserPool(this, 'advanced-user-pool', {
selfSignUpEnabled: true,
standardAttributes: {
givenName: { required: true },
familyName: { required: true },
locale: { required: true },
},
lambdaTriggers: {
customMessage: customMessageLambda
}
});
const userPoolClient = userPool.addClient('user-pool-client');
new CfnOutput(this, 'UserPoolId', {value: userPool.userPoolId});
new CfnOutput(this, 'UserPoolClientId', {value: userPoolClient.userPoolClientId});
}
}
2. Forwarding the browser language#
Following previous articles, we might have already something similar to this:
import React from 'react';
import {Amplify} from 'aws-amplify';
import {Authenticator} from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
Amplify.configure({
Auth: {
Cognito: {
userPoolId: 'UserPoolId from your stack output',
userPoolClientId: 'UserPoolClientId from your stack output',
}
}
});
export default function App() {
return (
<Authenticator
signUpAttributes={['given_name', 'family_name']}
>
{({signOut, user}) => (
<main>
<h1>Hello {user.username}</h1>
<button onClick={signOut}>Sign out</button>
</main>
)}
</Authenticator>
);
}
Since we’ve decided to inject the browser language into the sign-up process, instead of letting the user select the preferred language,
we need to overwrite the signUp
function.
export default function App() {
const handleSignUp = async (input: SignUpInput): Promise<SignUpOutput> => {
const language = navigator.language.split('-')[0];
input.options!.userAttributes.locale = language;
return signUp(input);
}
return (
<Authenticator
services={{handleSignUp}}
signUpAttributes={['given_name', 'family_name']}
>
{({signOut, user}) => (
<main>
<h1>Hello {user.username}</h1>
<button onClick={signOut}>Sign out</button>
</main>
)}
</Authenticator>
);
}
Here you can learn more about overwriting function calls.
3. Updating the custom message Lambda#
The last step is to modify the custom message Lambda, you could also add more languages if you want.
Here is my example:
import {CustomMessageTriggerHandler} from 'aws-lambda';
export const handler: CustomMessageTriggerHandler = async (event) => {
if (event.triggerSource === 'CustomMessage_SignUp') {
const userAttributes = event.request.userAttributes;
const userLanguage = userAttributes.locale as 'en' | 'de';
const userDisplayName = `${userAttributes.given_name} ${userAttributes.family_name}`;
if (userLanguage === 'de') {
event.response.emailSubject = 'Verifiziere deine E-Mail fΓΌr unsere tolle superluminar-App!';
event.response.emailMessage = `<h3>Moin ${userDisplayName} π</h3>
Danke, dass du dich bei unserer tollen superluminar-App registriert hast!<br/>
Dein Verifizierungscode ist <code>${event.request.codeParameter}</code>`;
}
if (userLanguage === 'en') {
event.response.emailSubject = 'Verify your email for our awesome superluminar app!';
event.response.emailMessage = `<h3>Hi ${userDisplayName} π</h3>
Thanks for signing up to our awesome superluminar app!<br/>
Your verification code is <code>${event.request.codeParameter}</code>`;
}
}
return event;
};
Conclusion#
As users proceed through the sign-up process, they should receive a tailored email in their preferred language. Customizing messages boosts relevance, user experience, engagement, conversion rates, and fosters better relationships, ultimately aiming for an improved product experience π.
I hope you found value in my articles; we’ve concluded that topic for now.
Should you have any feedback, I welcome you to reach out to me through LinkedIn or email. Your input is greatly appreciated.