Amplify UI を使ったログイン画面（Amplify UIその2） Authenticator

前回その1（https://qiita.com/ssugimoto/items/73e119d73296819afe01 ）の続き
Amplify UI でのui-components、ui-react、Authenticatorを使いオーバーライドする例です

## 記事内でのカスタム要素の説明
- この記事では、独自のカスタマイズ・拡張ではなく、イメージとしては継承です。cssのオーバーライド、文字列表記の変更、ヘッダー、フッターに独自のコンポーネントを追加、UIライブラリ自体はそのまま使う。
- Amplify UI でのui-components、ui-react、Authenticatorをオーバーライド（継承）していく。
- 自前で以下のAuthライブラリを自前で呼び出すようなことはしません( https://docs.amplify.aws/lib/auth/emailpassword/q/platform/js/ )

```js
import { Auth } from 'aws-amplify';

async function signUp() {
    try {
        const { user } = await Auth.signUp({
            username,
            password,
            attributes: {
                email,          // optional
            }
        });
        console.log(user);
    } catch (error) {
        console.log('error signing up:', error);
    }
}

async function signIn() {
    try {
        const user = await Auth.signIn(username, password);
    } catch (error) {
        console.log('error signing in', error);
    }
}
```

--- 

## Authenticator多言語対応

### 日本語のみならば以下にすることで予め用意された辞書に基づいた日本語表示ができる

```tsx
import { I18n } from 'aws-amplify';
import { translations } from '@aws-amplify/ui';


I18n.putVocabularies(translations);
I18n.setLanguage('ja');
```

言語切替えでの日本語のデフォルト辞書は以下が使われる
https://github.com/aws-amplify/amplify-ui/blob/main/packages/ui/src/i18n/dictionaries/authenticator/ja.ts

### 上記の対応ではもの足りない
- 実際に動かしてみると、日本語表記を変えたい
    - 「サインインに戻る」を 「ログインに戻る」に変えたい
- 日本語でなく英語のままの箇所がある

### 日本語化の参考
-  https://dev.classmethod.jp/articles/try-amplify-ui-angular-translations/ こちらの記事にて、デフォルトでは日本語化が不足している辞書を追加してくれています。

- 辞書のキーとなる定数enum定義
    - https://github.com/aws-amplify/amplify-js/blob/main/packages/amplify-ui-components/src/common/Translations.ts

- 言語用の辞書を作成して読み込ませる方法
   ドキュメント https://docs.amplify.aws/ui/customization/translations/q/framework/react/ より抜粋

    ```tsx
    import { I18n } from "aws-amplify";
    import { Translations } from "@aws-amplify/ui-components";
    
    I18n.putVocabulariesForLanguage("en-US", {
      [Translations.SIGN_IN_HEADER_TEXT]: "Custom Sign In Header Text",
      [Translations.SIGN_IN_ACTION]: "Custom Click Here to Sign In"
    });
    ```

### V2系の公式ドキュメント

- Amplify UIのログインで使われる画面（Sign In、Sign Up、Reset Password、 Setup TOTP）での言語変更の例が載っています

https://ui.docs.amplify.aws/components/authenticator#internationalization-i18n

#### サインインの項目を日本語jaで用意する場合

- 英語（en）

```tsx
I18n.putVocabulariesForLanguage('en', {
  'Sign In': 'Login', // Tab header
  'Sign in': 'Log in', // Button label
  'Sign in to your account': 'Welcome Back!',
  Username: 'Enter your username', // Username label
  Password: 'Enter your password', // Password label
  'Forgot your password?': 'Reset Password',
});
```

- 日本語(ja）

```tsx
I18n.putVocabulariesForLanguage('ja', {
  'Sign In': 'ログイン', // Tab header
  'Sign in': 'Log in', // Button label
  'Sign in to your account': 'ようこそ！',
  Username: 'username(ユーザー名)', // Username label
  Password: 'password（パスワード）', // Password label
  'Forgot your password?': 'パスワードリセット',
});
```

- 独自の辞書を用意することで、以下のようなことができる
    - フォーム内のラベル表記文字列内容の変更、placeholderの文字変更、ラベルを独自の辞書使って変更
    - input要素のplaceholderの表記を独自の辞書使って変更

---


## 日本語化対応のサンプル実装

- 言語設定を呼び出す

```App.tsx

SetUIVocabularies('ja');
```

### 英語等の言語はライブラリをそのまま使い、作成するアプリケーションで必要な箇所を独自の日本語辞書を使う
- 日本語化対象は、サインイン、パスワードリセットで、TOTPの認証のみ（TOTPの設定は含まず）
- cognitoのAWSクラウド側から戻させるメッセージの日本語化もある程度対応（TOTPでのメッセージも対応）
- メッセージcognitoの設定に合わせる必要がある、最小は6文字のためcognitoで最小6文字ならばそのように変更、大文字小文字、英数字、記号の場合も同様にcognitoの設定に合わせる

```UIVocabularies.tsx
import { I18n } from 'aws-amplify';
import { Translations } from "@aws-amplify/ui-components";

export const SetUIVocabularies = (lng: string): void => {
    I18n.setLanguage(lng);
    I18n.putVocabulariesForLanguage('ja', {
        // ---
        // サインイン
        // ---
        [Translations.SIGN_IN_ACTION]:  'ログイン',  // 'Login', // Tab header
        [Translations.SIGN_IN_TEXT]: 'ログイン', // Button label
        [Translations.SIGN_IN_HEADER_TEXT]: 'アカウントにログイン', //'Sign in to your account', 
        Username: 'ユーザーID', // Username label
        Password: 'パスワード', // Password label
        [Translations.FORGOT_PASSWORD_TEXT]: 'パスワードリセット', 

        // ---
        // サインアップ、一部他と兼用
        // ---
        [Translations.CREATE_ACCOUNT_TEXT]: 'アカウント作成', // Tab header
        'Create Account':  'アカウント作成', // 'Register', // Tab header
        [Translations.SIGN_UP_HEADER_TEXT]: 'New User', // Header text
        'Confirm Password': '確認用パスワード', // Confirm Password label
        Email: 'メールアドレス',
        // 'Phone Number': 'Enter your phone number',

        // ---
        // パスワードリセット
        // ---
        [Translations.RESET_YOUR_PASSWORD]: 'パスワードをリセット', // 画面説明 lable
        [Translations.USERNAME_PLACEHOLDER]: 'ユーザーIDまたはメールアドレス', // リセットするユーザー名 lable
        // SEND_CODE型だとなぜか認識されない → [Translations.SEND_CODE]: 'コード送信', // コード送信ボタン
        'Send code': 'コード送信', // パスワードリセットコードの送信時の説明タイトル
        [Translations.BACK_TO_SIGN_IN]: 'ログインに戻る', // 前画面に戻るリンク表示文字列

        // パスワードリセット後
        [Translations.CONFIRM_SIGN_UP_RESEND_CODE]: '再送信',   // Resend Code 再送信
        'Code':'確認コード', //パスワードリセット時のコード（Your verification codeを入れる）、TOTPでのコード入力のplaceholder
        [Translations.NEW_PASSWORD_LABEL]: "新しいパスワード", //新しいパスワード
        // パスワード確認用のplaceholerは、サインアップと同じ

        // Submit はどの型定義か不明
        'Submit': '送信',

        // ---
        // パスワード変更
        // ---
        [Translations.CHANGE_PASSWORD]: 'パスワード変更',
        
        //--- アカウント復元用のメールアドレス確認
        [Translations.VERIFY_CONTACT_VERIFY_LABEL]: '確認',   // Verify
        'Skip': 'スキップ', //今は確認せずにスキップ

        // --- TOTPログイン認証のパスワード認証後
        [Translations.CONFIRM_TOTP_CODE]: '2段階認証 認証コード', // TOTPのワンタイムトークンの入力画面説明ラベル
        [Translations.CONFIRM]: '確認', //TOTPの送信ボタン
        

        // ---
        // 
        // ---
        'Signing in': 'logning in...', // サインイン（ログイン）ボタン後の処理中
        //定義にsubmitting がない


        //--- cognitoのサーバーからのメッセージ
        // Cognito Server Side Error Messages
        // ref.) https://github.com/aws-amplify/amplify-js/issues/867
        //---
        'User does not exist.': 'ユーザーが存在しません',
        'Incorrect username or password.': 'ユーザー名またはパスワードが違います',
        'User is not confirmed.': 'ユーザーは検証されていません',
        'User already exists': 'ユーザーは既に存在します',
        'Invalid verification code provided, please try again.': '指定された確認コードが無効です。もう一度お試しください',
        'Invalid password format': 'パスワードのフォーマットが不正です',
        'Account recovery requires verified contact information': 'アカウントの復旧には確認済みの連絡先情報が必要です',
        'Invalid phone number format': '不正な電話番号フォーマットです。 電話番号は次のフォーマットで入力してください: +12345678900',
        'An account with the given email already exists.': 'そのメールアドレスは既に存在します',
        'Username cannot be empty': 'ユーザー名は必須です',
        'Password attempts exceeded': 'ログイン試行回数が上限に達しました',
        'Attempt limit exceeded, please try after some time.': '試行制限を超過しました。しばらくしてからもう一度お試しください',
        'Username/client id combination not found.': 'ユーザーが存在しません',
        'CUSTOM_AUTH is not enabled for the client.': 'パスワードは必須です', // 本来の意味とは異なるが、パスワード未入力時に発生するのでこの訳としている
        'Password does not conform to policy: Password not long enough': 'パスワードは8文字以上を入力してください (8文字以上の大文字小文字を含む英数字)', // 適宜修正
        'Password does not conform to policy: Password must have uppercase characters': 'パスワードには大文字を含めてください (8文字以上の大文字小文字を含む英数字)', // 適宜修正
        'Password does not conform to policy: Password must have lowercase characters': 'パスワードには小文字を含めてください (8文字以上の大文字小文字を含む英数字)', // 適宜修正
        'Password does not conform to policy: Password must have numeric characters': 'パスワードには数字を含めてください (8文字以上の大文字小文字を含む英数字)', // 適宜修正
        "1 validation error detected: Value at 'password' failed to satisfy constraint: Member must have length greater than or equal to 6": 'パスワードは8文字以上、大文字小文字を含む英数字を指定してください', // 適宜修正。本来の意味とは異なるがこれで明示している。
        "2 validation errors detected: Value at 'password' failed to satisfy constraint: Member must have length greater than or equal to 6; Value at 'password' failed to satisfy constraint: Member must satisfy regular expression pattern: ^[\S]+.*[\S]+$": 'パスワードは8文字以上、大文字小文字を含む英数字を指定してください', // 適宜修正。本来の意味とは異なるがこれで明示している。
        'Temporary password has expired and must be reset by an administrator.': '一時パスワードは無効です。管理者によるリセットが必要です',
        "1 validation error detected: Value null at 'attributeName' failed to satisfy constraint: Member must not be null": '入力チェックエラー、必須項目がNULLです', //アカウント復旧でのメールアドレスのラジオをチェックONにせず、送信した場合
        'Invalid code received for user': '無効なコードです', // TOTPでのトークンに誤りがある
        'Invalid session for the user, session is expired.' : '無効なセッション、セッションは有効期限切れです。ログインからやり直してください' // ログインセッション無効です、ログインからやり直し
    });
};

```

---

## 上記の日本語化を使ったサンプルログイン画面

### ボタン名や入力テキスト
- いくつかの項目を日本語化してみます
    - タブ、placeholder（inputのplaceholder）、ログイン用のボタン、パスワード忘れ用のリンク

- ログイン
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/14ab439f-adc0-8449-4762-aafcd1e0dd9e.png" width="500" />

- アカウント作成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/5794b0ba-2fb3-72cf-f106-90561921197e.png" width="400" />

##### パスワードリセット用画面
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/eb581611-be87-dfa7-534c-3ed6f38d0d5f.png" width="300" />


##### ログイン認証エラー
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/14a639e6-0316-6aef-3ed8-7a9687c559b7.png" width="300" />


##### TOTPログイン認証
- ユーザー名とパスワードで認証が通った後
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/37e21281-77cb-d2b0-598c-747173a21008.png" width="300" />

- TOTP一時トークンで不一致の場合（AWSクラウド側のCognitoから返ってきた英語を日本語化）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/bdeb89f6-62f6-c91d-5c6c-10c12807209b.png" width="300" />


---

その３では、次のようなことをやってみます

ヘッダーやフッター等のカスタマイズ、AWSブログのものが出来栄えがよく、そのまんま使える

https://aws.amazon.com/jp/blogs/mobile/amplify-uis-new-authenticator-component-makes-it-easy-to-add-customizable-login-pages-to-your-react-angular-or-vue-app/

https://codesandbox.io/s/match-your-brand-vdwk0?file=/src/App.js