はじめに
Ionic(Angular)環境でAWS Amplifyの認証画面を多言語対応してみました。
Angular環境の場合は、ionタグの部分を無視して読んで下さい。
AWS Amplifyの環境構築は、こちら
=>[Ionic6 + Angular11]AWS AmplifyとIonicでさくっと認証画面を作成する
2021年2月時点
思い
- できるだけ、コストをかけずに認証画面を作成したい。
- 認証画面を自作するコストをかけたくないが、AWS Amplifyの認証画面がカスタマイズできず結局自作というケースも多いのでは?
完成画面
ライブラリのバージョン
- aws-amplify": 3.3.20
- @aws-amplify/ui-angular: 1.0.2
対応の流れ
- aws-amplifyにある I18nモジュールを読み込む。
- 辞書ファイルを用意し、I18nに渡す。
- 表示言語を設定する。
拡張
- 画面上から言語を切り替えられるようにした。
対応(変更点)
- import { Component, ChangeDetectorRef } from '@angular/core';
+ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import {
onAuthUIStateChange,
CognitoUserInterface,
AuthState,
FormFieldTypes,
} from '@aws-amplify/ui-components';
import { I18n } from 'aws-amplify';
import { amplifyVocabularies } from '../shared/const/amplify.vocabularies.const';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
- export class HomePage {
+ export class HomePage implements OnInit, OnDestroy {
title = 'amplify-angular-auth';
user: CognitoUserInterface | undefined;
authState: AuthState;
/** サインアップフィールド定義 */
signUpformFields: FormFieldTypes = [
{ type: 'username' },
{ type: 'password' },
{ type: 'email' },
];
+ languageOptions = [
+ {
+ label: '日本語',
+ value: 'ja',
+ },
+ {
+ label: 'English',
+ value: 'en',
+ },
+ ];
+ labelSelectLanguage = 'Select Language';
+ selectedLang = 'en';
- constructor(private ref: ChangeDetectorRef) {}
+ constructor(private ref: ChangeDetectorRef) {
+ const lang = sessionStorage.getItem('lang');
+ if (lang === null) {
+ return;
+ }
+ I18n.putVocabularies(amplifyVocabularies);
+ I18n.setLanguage(lang);
+ this.selectedLang = lang;
+ this.labelSelectLanguage = I18n.get('Select Language');
+ }
ngOnInit() {
onAuthUIStateChange((authState, authData) => {
this.authState = authState;
this.user = authData as CognitoUserInterface;
this.ref.detectChanges();
});
}
ngOnDestroy() {
return onAuthUIStateChange;
}
+ onChange(): void {
+ sessionStorage.setItem('lang', this.selectedLang);
+ location.reload();
+ }
+ }
<ion-content [fullscreen]="true">
<div id="container">
<amplify-authenticator *ngIf="authState !== 'signedin'">
<amplify-sign-in slot="sign-in">
+ <div slot="federated-buttons">
+ <div class="select-language">
+ {{labelSelectLanguage}}
+ <select [(ngModel)]="selectedLang" (change)="onChange()">
+ <option *ngFor="let languageOption of languageOptions" [ngValue]="languageOption.value">
+ {{languageOption.label}}
+ </option>
+ </select>
+ </div>
+ </div>
</amplify-sign-in>
<amplify-sign-up slot="sign-up" [formFields]="signUpformFields">
</amplify-sign-up>
</amplify-authenticator>
<div *ngIf="authState === 'signedin' && user" class="App">
<amplify-sign-out></amplify-sign-out>
<div>Hello, {{user.username}}</div>
</div>
</div>
</ion-content>
言語用constと型定義
かなり長いので折りたたみ
// Ref https://github.com/aws-amplify/amplify-js/blob/main/packages/amplify-ui-components/src/common/Translations.ts
/* eslint-disable @typescript-eslint/naming-convention */
const AuthStrings = {
BACK_TO_SIGN_IN: 'Back to Sign In',
CHANGE_PASSWORD_ACTION: 'Change',
CHANGE_PASSWORD: 'Change Password',
CODE_LABEL: 'Verification code',
CODE_PLACEHOLDER: 'Enter code',
CONFIRM_SIGN_UP_CODE_LABEL: 'Confirmation Code',
CONFIRM_SIGN_UP_CODE_PLACEHOLDER: 'Enter your code',
CONFIRM_SIGN_UP_HEADER_TEXT: 'Confirm Sign up',
CONFIRM_SIGN_UP_LOST_CODE: 'Lost your code?',
CONFIRM_SIGN_UP_RESEND_CODE: 'Resend Code',
CONFIRM_SIGN_UP_SUBMIT_BUTTON_TEXT: 'Confirm',
CONFIRM_SMS_CODE: 'Confirm SMS Code',
CONFIRM_TOTP_CODE: 'Confirm TOTP Code',
CONFIRM: 'Confirm',
CREATE_ACCOUNT_TEXT: 'Create account',
EMAIL_LABEL: 'Email Address *',
EMAIL_PLACEHOLDER: 'Enter your email address',
FORGOT_PASSWORD_TEXT: 'Forgot your password?',
LESS_THAN_TWO_MFA_VALUES_MESSAGE: 'Less than two MFA types available',
NEW_PASSWORD_LABEL: 'New password',
NEW_PASSWORD_PLACEHOLDER: 'Enter your new password',
NO_ACCOUNT_TEXT: 'No account?',
USERNAME_REMOVE_WHITESPACE: 'Username cannot contain whitespace',
PASSWORD_REMOVE_WHITESPACE: 'Password cannot start or end with whitespace',
PASSWORD_LABEL: 'Password *',
PASSWORD_PLACEHOLDER: 'Enter your password',
PHONE_LABEL: 'Phone Number *',
PHONE_PLACEHOLDER: '(555) 555-1212',
QR_CODE_ALT: 'qrcode',
RESET_PASSWORD_TEXT: 'Reset password',
RESET_YOUR_PASSWORD: 'Reset your password',
SELECT_MFA_TYPE_HEADER_TEXT: 'Select MFA Type',
SELECT_MFA_TYPE_SUBMIT_BUTTON_TEXT: 'Verify',
SEND_CODE: 'Send Code',
SUBMIT: 'Submit',
SETUP_TOTP_REQUIRED: 'TOTP needs to be configured',
SIGN_IN_ACTION: 'Sign In',
SIGN_IN_HEADER_TEXT: 'Sign in to your account',
SIGN_IN_TEXT: 'Sign in',
SIGN_IN_WITH_AMAZON: 'Sign in with Amazon',
SIGN_IN_WITH_AUTH0: 'Sign in with Auth0',
SIGN_IN_WITH_AWS: 'Sign in with AWS',
SIGN_IN_WITH_FACEBOOK: 'Sign in with Facebook',
SIGN_IN_WITH_GOOGLE: 'Sign in with Google',
SIGN_OUT: 'Sign Out',
SIGN_UP_EMAIL_PLACEHOLDER: 'Email',
SIGN_UP_HAVE_ACCOUNT_TEXT: 'Have an account?',
SIGN_UP_HEADER_TEXT: 'Create a new account',
SIGN_UP_PASSWORD_PLACEHOLDER: 'Password',
SIGN_UP_SUBMIT_BUTTON_TEXT: 'Create Account',
SIGN_UP_USERNAME_PLACEHOLDER: 'Username',
SUCCESS_MFA_TYPE: 'Success! Your MFA Type is now:',
TOTP_HEADER_TEXT: 'Scan then enter verification code',
TOTP_LABEL: 'Enter Security Code:',
TOTP_ISSUER: 'AWSCognito',
TOTP_SETUP_FAILURE: 'TOTP Setup has failed',
TOTP_SUBMIT_BUTTON_TEXT: 'Verify Security Token',
TOTP_SUCCESS_MESSAGE: 'Setup TOTP successfully!',
UNABLE_TO_SETUP_MFA_AT_THIS_TIME: 'Failed! Unable to configure MFA at this time',
USERNAME_LABEL: 'Username *',
USERNAME_PLACEHOLDER: 'Enter your username',
VERIFY_CONTACT_EMAIL_LABEL: 'Email',
VERIFY_CONTACT_HEADER_TEXT: 'Account recovery requires verified contact information',
VERIFY_CONTACT_PHONE_LABEL: 'Phone Number',
VERIFY_CONTACT_SUBMIT_LABEL: 'Submit',
VERIFY_CONTACT_VERIFY_LABEL: 'Verify',
ADDRESS_LABEL: 'Address',
ADDRESS_PLACEHOLDER: 'Enter your address',
NICKNAME_LABEL: 'Nickname',
NICKNAME_PLACEHOLDER: 'Enter your nickname',
BIRTHDATE_LABEL: 'Birthday',
BIRTHDATE_PLACEHOLDER: 'Enter your birthday',
PICTURE_LABEL: 'Picture URL',
PICTURE_PLACEHOLDER: 'Enter your picture URL',
FAMILY_NAME_LABEL: 'Family Name',
FAMILY_NAME_PLACEHOLDER: 'Enter your family name',
PREFERRED_USERNAME_LABEL: 'Preferred Username',
PREFERRED_USERNAME_PLACEHOLDER: 'Enter your preferred username',
GENDER_LABEL: 'Gender',
GENDER_PLACEHOLDER: 'Enter your gender',
PROFILE_LABEL: 'Profile URL',
PROFILE_PLACEHOLDER: 'Enter your profile URL',
GIVEN_NAME_LABEL: 'First Name',
GIVEN_NAME_PLACEHOLDER: 'Enter your first name',
ZONEINFO_LABEL: 'Time zone',
ZONEINFO_PLACEHOLDER: 'Enter your time zone',
LOCALE_LABEL: 'Locale',
LOCALE_PLACEHOLDER: 'Enter your locale',
UPDATED_AT_LABEL: 'Updated At',
UPDATED_AT_PLACEHOLDER: 'Enter the time the information was last updated',
MIDDLE_NAME_LABEL: 'Middle Name',
MIDDLE_NAME_PLACEHOLDER: 'Enter your middle name',
WEBSITE_LABEL: 'Website',
WEBSITE_PLACEHOLDER: 'Enter your website',
NAME_LABEL: 'Full Name',
NAME_PLACEHOLDER: 'Enter your full name',
PHOTO_PICKER_TITLE: 'Picker Title',
PHOTO_PICKER_HINT: 'Ancillary text or content may occupy this space here',
PHOTO_PICKER_PLACEHOLDER_HINT: 'Placeholder hint',
PHOTO_PICKER_BUTTON_TEXT: 'Button',
IMAGE_PICKER_TITLE: 'Add Profile Photo',
IMAGE_PICKER_HINT: 'Preview the image before upload',
IMAGE_PICKER_PLACEHOLDER_HINT: 'Tap to image select',
IMAGE_PICKER_BUTTON_TEXT: 'Upload',
PICKER_TEXT: 'Pick a file',
TEXT_FALLBACK_CONTENT: 'Fallback Content',
CONFIRM_SIGN_UP_FAILED: 'Confirm Sign Up Failed',
SIGN_UP_FAILED: 'Sign Up Failed',
} as const;
const InteractionsStrings = {
CHATBOT_TITLE: 'ChatBot Lex',
TEXT_INPUT_PLACEHOLDER: 'Write a message',
VOICE_INPUT_PLACEHOLDER: 'Click mic to speak',
CHAT_DISABLED_ERROR: 'Error: Either voice or text must be enabled for the chatbot',
NO_BOT_NAME_ERROR: 'Error: Bot name must be provided to ChatBot',
} as const;
const AuthErrorStrings = {
DEFAULT_MSG: 'Authentication Error',
EMPTY_USERNAME: 'Username cannot be empty',
INVALID_USERNAME: 'The username should either be a string or one of the sign in types',
EMPTY_PASSWORD: 'Password cannot be empty',
EMPTY_CODE: 'Confirmation code cannot be empty',
SIGN_UP_ERROR: 'Error creating account',
NO_MFA: 'No valid MFA method provided',
INVALID_MFA: 'Invalid MFA type',
EMPTY_CHALLENGE: 'Challenge response cannot be empty',
NO_USER_SESSION: 'Failed to get the session because the user is empty',
} as const;
type AuthStrings = typeof AuthStrings[keyof typeof AuthStrings];
type InteractionsStrings = typeof InteractionsStrings[keyof typeof InteractionsStrings];
type AuthErrorStrings = typeof AuthErrorStrings[keyof typeof AuthErrorStrings];
export const amplifyVocabularies = {
ja: {
[AuthStrings.BACK_TO_SIGN_IN]: 'サインインに戻る',
[AuthStrings.CHANGE_PASSWORD_ACTION]: '変更',
[AuthStrings.CHANGE_PASSWORD]: 'パスワードの変更',
[AuthStrings.CODE_LABEL]: '検証コード',
[AuthStrings.CODE_PLACEHOLDER]: 'コードを入力してください',
[AuthStrings.CONFIRM_SIGN_UP_CODE_LABEL]: '確認コード',
[AuthStrings.CONFIRM_SIGN_UP_CODE_PLACEHOLDER]: 'コードを入力してください',
[AuthStrings.CONFIRM_SIGN_UP_HEADER_TEXT]: 'サインアップの確認',
[AuthStrings.CONFIRM_SIGN_UP_LOST_CODE]: 'コードを紛失しましたか?',
[AuthStrings.CONFIRM_SIGN_UP_RESEND_CODE]: 'コードを再送',
[AuthStrings.CONFIRM_SIGN_UP_SUBMIT_BUTTON_TEXT]: '確認',
[AuthStrings.CONFIRM_SMS_CODE]: 'SMSコードの確認',
[AuthStrings.CONFIRM_TOTP_CODE]: 'TOTPコードの確認',
[AuthStrings.CONFIRM]: '確認',
[AuthStrings.CREATE_ACCOUNT_TEXT]: 'アカウントを作成',
[AuthStrings.EMAIL_LABEL]: 'メールアドレス*',
[AuthStrings.EMAIL_PLACEHOLDER]: 'メールアドレスを入力してください',
[AuthStrings.FORGOT_PASSWORD_TEXT]: 'パスワードを忘れましたか?',
[AuthStrings.LESS_THAN_TWO_MFA_VALUES_MESSAGE]: '使用可能なMFAタイプが2つ未満',
[AuthStrings.NEW_PASSWORD_LABEL]: '新しいパスワード',
[AuthStrings.NEW_PASSWORD_PLACEHOLDER]: '新しいパスワードを入力してください',
[AuthStrings.NO_ACCOUNT_TEXT]: 'アカウントがありませんか?',
[AuthStrings.USERNAME_REMOVE_WHITESPACE]: 'ユーザー名に空白を含めることはできません',
[AuthStrings.PASSWORD_REMOVE_WHITESPACE]: 'パスワードは空白で開始または終了できません',
[AuthStrings.PASSWORD_LABEL]: 'パスワード*',
[AuthStrings.PASSWORD_PLACEHOLDER]: 'パスワードを入力してください',
[AuthStrings.PHONE_LABEL]: '電話番号*',
[AuthStrings.PHONE_PLACEHOLDER]: '(555)555-1212',
[AuthStrings.QR_CODE_ALT]: 'qrcode',
[AuthStrings.RESET_PASSWORD_TEXT]: 'パスワードをリセット',
[AuthStrings.RESET_YOUR_PASSWORD]: 'パスワードをリセットします',
[AuthStrings.SELECT_MFA_TYPE_HEADER_TEXT]: 'MFAタイプを選択',
[AuthStrings.SELECT_MFA_TYPE_SUBMIT_BUTTON_TEXT]: '検証',
[AuthStrings.SEND_CODE]: 'コードを送信',
[AuthStrings.CONFIRM]: '送信',
[AuthStrings.SETUP_TOTP_REQUIRED]: 'TOTPを構成する必要があります',
[AuthStrings.SIGN_IN_ACTION]: 'サインイン',
[AuthStrings.SIGN_IN_HEADER_TEXT]: 'アカウントにサインインします',
[AuthStrings.SIGN_IN_TEXT]: 'サインイン',
[AuthStrings.SIGN_IN_WITH_AMAZON]: 'Amazonでサインイン',
[AuthStrings.SIGN_IN_WITH_AUTH0]: 'Auth0でサインイン',
[AuthStrings.SIGN_IN_WITH_AWS]: 'AWSでサインイン',
[AuthStrings.SIGN_IN_WITH_FACEBOOK]: 'Facebookでサインイン',
[AuthStrings.SIGN_IN_WITH_GOOGLE]: 'Googleでサインイン',
[AuthStrings.SIGN_OUT]: 'サインアウト',
[AuthStrings.SIGN_UP_EMAIL_PLACEHOLDER]: 'Eメール',
[AuthStrings.SIGN_UP_HAVE_ACCOUNT_TEXT]: 'アカウントをお持ちですか?',
[AuthStrings.SIGN_UP_HEADER_TEXT]: '新しいアカウントを作成します',
[AuthStrings.SIGN_UP_PASSWORD_PLACEHOLDER]: 'パスワード',
[AuthStrings.SIGN_UP_SUBMIT_BUTTON_TEXT]: 'アカウントの作成',
[AuthStrings.SIGN_UP_USERNAME_PLACEHOLDER]: 'ユーザー名',
[AuthStrings.SUCCESS_MFA_TYPE]: '成功! MFAタイプは次のようになります]: ',
[AuthStrings.TOTP_HEADER_TEXT]: 'スキャンして確認コードを入力',
[AuthStrings.TOTP_LABEL]: 'セキュリティコードを入力してください:',
[AuthStrings.TOTP_ISSUER]: 'AWSCognito',
[AuthStrings.TOTP_SETUP_FAILURE]: 'TOTPセットアップが失敗しました',
[AuthStrings.TOTP_SUBMIT_BUTTON_TEXT]: 'セキュリティトークンの確認',
[AuthStrings.TOTP_SUCCESS_MESSAGE]: 'TOTPを正常にセットアップします!',
[AuthStrings.UNABLE_TO_SETUP_MFA_AT_THIS_TIME]: '失敗しました!現時点ではMFAを構成できません',
[AuthStrings.USERNAME_LABEL]: 'ユーザー名*',
[AuthStrings.USERNAME_PLACEHOLDER]: 'ユーザー名を入力してください',
[AuthStrings.VERIFY_CONTACT_EMAIL_LABEL]: 'Eメール',
[AuthStrings.VERIFY_CONTACT_HEADER_TEXT]: 'アカウントの回復には確認済みの連絡先情報が必要です',
[AuthStrings.VERIFY_CONTACT_PHONE_LABEL]: '電話番号',
[AuthStrings.VERIFY_CONTACT_SUBMIT_LABEL]: '送信',
[AuthStrings.VERIFY_CONTACT_VERIFY_LABEL]: '検証',
[AuthStrings.ADDRESS_LABEL]: 'アドレス',
[AuthStrings.ADDRESS_PLACEHOLDER]: '住所を入力してください',
[AuthStrings.NICKNAME_LABEL]: 'ニックネーム',
[AuthStrings.NICKNAME_PLACEHOLDER]: 'ニックネームを入力してください',
[AuthStrings.BIRTHDATE_LABEL]: '誕生日',
[AuthStrings.BIRTHDATE_PLACEHOLDER]: '誕生日を入力してください',
[AuthStrings.PICTURE_LABEL]: '画像のURL',
[AuthStrings.PICTURE_PLACEHOLDER]: '画像のURLを入力してください',
[AuthStrings.FAMILY_NAME_LABEL]: '家族名',
[AuthStrings.FAMILY_NAME_PLACEHOLDER]: '家族の名前を入力してください',
[AuthStrings.PREFERRED_USERNAME_LABEL]: '優先ユーザー名',
[AuthStrings.PREFERRED_USERNAME_PLACEHOLDER]: '希望するユーザー名を入力してください',
[AuthStrings.GENDER_LABEL]: '性別',
[AuthStrings.GENDER_PLACEHOLDER]: '性別を入力してください',
[AuthStrings.PROFILE_LABEL]: 'プロファイルURL',
[AuthStrings.PROFILE_PLACEHOLDER]: 'プロファイルURLを入力してください',
[AuthStrings.GIVEN_NAME_LABEL]: '名',
[AuthStrings.GIVEN_NAME_PLACEHOLDER]: '名を入力してください',
[AuthStrings.ZONEINFO_LABEL]: 'タイムゾーン',
[AuthStrings.ZONEINFO_PLACEHOLDER]: 'タイムゾーンを入力してください',
[AuthStrings.LOCALE_LABEL]: 'ロケール',
[AuthStrings.LOCALE_PLACEHOLDER]: 'ロケールを入力してください',
[AuthStrings.UPDATED_AT_LABEL]: '更新場所',
[AuthStrings.UPDATED_AT_PLACEHOLDER]: '情報が最後に更新された時刻を入力してください',
[AuthStrings.MIDDLE_NAME_LABEL]: 'ミドルネーム',
[AuthStrings.MIDDLE_NAME_PLACEHOLDER]: 'ミドルネームを入力してください',
[AuthStrings.WEBSITE_LABEL]: 'ウェブサイト',
[AuthStrings.WEBSITE_PLACEHOLDER]: 'あなたのウェブサイトを入力してください',
[AuthStrings.NAME_LABEL]: 'フルネーム',
[AuthStrings.NAME_PLACEHOLDER]: 'フルネームを入力してください',
[AuthStrings.PHOTO_PICKER_TITLE]: 'ピッカータイトル',
[AuthStrings.PHOTO_PICKER_HINT]: '補助テキストまたはコンテンツがここのこのスペースを占める場合があります',
[AuthStrings.PHOTO_PICKER_PLACEHOLDER_HINT]: 'プレースホルダーヒント',
[AuthStrings.PHOTO_PICKER_BUTTON_TEXT]: 'ボタン',
[AuthStrings.IMAGE_PICKER_TITLE]: 'プロフィール写真を追加',
[AuthStrings.IMAGE_PICKER_HINT]: 'アップロードする前に画像をプレビューします',
[AuthStrings.IMAGE_PICKER_PLACEHOLDER_HINT]: 'タップして画像を選択',
[AuthStrings.IMAGE_PICKER_BUTTON_TEXT]: 'アップロード',
[AuthStrings.PICKER_TEXT]: 'ファイルを選択',
[AuthStrings.TEXT_FALLBACK_CONTENT]: 'フォールバックコンテンツ',
[AuthStrings.CONFIRM_SIGN_UP_FAILED]: 'サインアップが失敗したことを確認します',
[AuthStrings.SIGN_UP_FAILED]: 'サインアップに失敗しました',
[InteractionsStrings.CHATBOT_TITLE]: 'ChatBot Lex',
[InteractionsStrings.TEXT_INPUT_PLACEHOLDER]: 'メッセージを書く',
[InteractionsStrings.VOICE_INPUT_PLACEHOLDER]: 'マイクをクリックして話します',
[InteractionsStrings.CHAT_DISABLED_ERROR]:
'エラー:チャットボットに対して音声またはテキストのいずれかを有効にする必要があります',
[InteractionsStrings.NO_BOT_NAME_ERROR]: 'エラー:ボット名をChatBotに提供する必要があります',
[AuthErrorStrings.DEFAULT_MSG]: '認証エラー',
[AuthErrorStrings.EMPTY_USERNAME]: 'ユーザー名を空にすることはできません',
[AuthErrorStrings.INVALID_USERNAME]: 'ユーザー名は文字列またはサインインタイプのいずれかである必要があります',
[AuthErrorStrings.EMPTY_PASSWORD]: 'パスワードを空にすることはできません',
[AuthErrorStrings.EMPTY_CODE]: '確認コードを空にすることはできません',
[AuthErrorStrings.SIGN_UP_ERROR]: 'アカウントの作成中にエラーが発生しました',
[AuthErrorStrings.NO_MFA]: '有効なMFAメソッドが提供されていません',
[AuthErrorStrings.INVALID_MFA]: '無効なMFAタイプ',
[AuthErrorStrings.EMPTY_CHALLENGE]: 'チャレンジレスポンスを空にすることはできません',
[AuthErrorStrings.NO_USER_SESSION]: 'ユーザーが空であるため、セッションを取得できませんでした',
'Select Language': '言語選択',
},
};
課題
- Cognitoのエラーに関しては、多言語化できていない。
- エラー一覧があれば、言語用constに追加するだけでよいが見つからなかった。
- 言語切り替えにページリロードをしている。
- モジュールの説明を見る限りロード時に決めておくもののよう。
https://docs.amplify.aws/lib/utilities/i18n/q/platform/js
- モジュールの説明を見る限りロード時に決めておくもののよう。
参考
https://docs.amplify.aws/lib/utilities/i18n/q/platform/js
https://github.com/aws-amplify/amplify-js/blob/main/packages/amplify-ui-components/src/common/Translations.ts