# Localization

Every text in the web components is completely customizable and localizable, multiple languages are supported and the default language of the components can be changed with the `lang` attribute.

There are also two more attributes regarding localization which are:

| **Attribute**                                                                    | **Type**                        | **Explanation**                                                                                                                                             |
| -------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p><code>localization-packs</code><br><code>localizationPacks</code></p>         | `LocalizationPack[]`            | You can pass an array of localization packs to this attribute, if you pass a localization pack that already exists it will be merged with the existing one. |
| <p><code>localization-variables</code><br><code>localizationVariables</code></p> | `Record<number \| string, any>` | The localization variables are usually dynamic values that can be injected inside the localization strings through the `{ variable }` syntax.               |

### LocalizationPack Interface

```typescript
interface LocalizationPack {
    data: LocalizationPackData;
    language: string;
}

interface LocalizationPackData {
    [key: string]: string | LocalizationPackData;
}
```

As a reference, here’s our english language pack:

```typescript
{
  auth: {
    step: {
      bootstrap: {
        headline: 'Authentication Process',
        text: 'Take a selfie to authenticate through Keyless on the platform.'
      },
      'camera-instructions': {
        headline: 'Authentication Process',
        text: 'Take a selfie to authenticate through Keyless on the platform.',
        button: "All set. I'm ready"
      },
      'camera-permission': {
        prompt: {
          headline: 'Allow Camera Access',
          text: 'We need access to your camera to authenticate you. Please allow the browser to access your camera.',
          button: 'Continue'
        },
        granted: {
          headline: 'Allow Camera Access',
          text: 'We need access to your camera to authenticate you. Please allow the browser to access your camera.',
          button: 'Continue'
        },
        denied: {
          headline: 'Camera Access Denied',
          text: 'Looks like you denied camera access on this website, we need access to your camera to authenticate you.\n\nPlease allow the browser to access your camera.',
          button: 'Retry'
        }
      },
      'camera-stream-boot': {
        tip: 'Preparing your camera'
      },
      done: {
        headline: 'Authentication Successful',
        text: 'You can now proceed to the platform.'
      },
      error: {
        headline: 'Something went wrong',
        text: 'We were unable to authenticate you. {message} <code style="font-size: 12px">[{code}]</code>',
        button: 'Retry'
      },
      'microphone-permission': {
        prompt: {
          headline: 'Allow Microphone Access',
          text: 'We need access to your microphone to authenticate you. Please allow the browser to access your microphone.',
          button: 'Continue'
        },
        granted: {
          headline: 'Allow Microphone Access',
          text: 'We need access to your microphone to authenticate you. Please allow the browser to access your microphone.',
          button: 'Continue'
        },
        denied: {
          headline: 'Microphone Access Denied',
          text: 'Looks like you denied microphone access on this website, we need access to your microphone to authenticate you.\n\nPlease allow the browser to access your microphone.',
          button: 'Retry'
        }
      },
      'server-computation': {
        headline: 'Authenticating...',
        text: 'Your selfie was captured correctly, we are processing your photo'
      },
      'stm-choice': {
        headline: 'Authentication Process',
        text: 'Take a selfie to authenticate through Keyless on the platform.',
        button: 'Continue on Phone',
        secondary_button: 'Authenticate on Desktop'
      },
      'stm-qrcode': {
        headline: 'Scan the QR Code',
        text: 'Scan the QR code with your phone to continue the authentication process.',
        button: {
          false: 'Copy Link',
          true: 'Copied to Clipboard'
        }
      }
    }
  },
  biometric_filter: {
    face_has_mask: 'Remove any face masks',
    face_is_occluded: 'Remove any obstructions from your face',
    face_missing: 'Show your face in the frame',
    face_multiple: 'Show only one face in the frame',
    face_partial: 'Show your full face in the frame',
    face_too_small: 'Move your face closer to the device',
    image_black_and_white: 'Move in a well-lit area',
    none: 'Keep your face still'
  },
  camera_instructions: {
    alignment: 'Center your face in the frame',
    look: 'Look directly at the screen',
    lighting: 'Ensure you are in a well-lit area',
    accessories: 'Remove any eyewear or hats'
  },
  camera_select: {
    headline: 'Select a Camera',
    text: 'We detected more than one webcam connected to your device. Choose one for the process.'
  },
  enroll: {
    step: {
      bootstrap: {
        headline: 'Enrollment Process',
        text: 'Take a selfie to create an account on Keyless and easily register on the platform.'
      },
      'camera-instructions': {
        headline: 'Enrollment Process',
        text: 'Take a selfie to create an account on Keyless and easily register on the platform.',
        button: 'Continue'
      },
      'camera-permission': {
        prompt: {
          headline: 'Allow Camera Access',
          text: 'We need access to your camera to create your account. Please allow the browser to access your camera.',
          button: 'Continue'
        },
        granted: {
          headline: 'Allow Camera Access',
          text: 'We need access to your camera to create your account. Please allow the browser to access your camera.',
          button: 'Continue'
        },
        denied: {
          headline: 'Camera Access Denied',
          text: 'Looks like you denied camera access on this website, we need access to your camera to create your account.\n\nPlease allow the browser to access your camera.',
          button: 'Retry'
        }
      },
      'camera-stream-boot': {
        tip: 'Preparing your camera'
      },
      done: {
        headline: 'Account created',
        text: 'You can now access and authenticate simply by using your face.'
      },
      error: {
        headline: 'Something went wrong',
        text: 'We were unable to create your account. {message} <code style="font-size: 12px">[{code}]</code>',
        button: 'Retry'
      },
      'microphone-permission': {
        prompt: {
          headline: 'Allow Microphone Access',
          text: 'We need access to your microphone to create your account. Please allow the browser to access your microphone.',
          button: 'Continue'
        },
        granted: {
          headline: 'Allow Microphone Access',
          text: 'We need access to your microphone to create your account. Please allow the browser to access your microphone.',
          button: 'Continue'
        },
        denied: {
          headline: 'Microphone Access Denied',
          text: 'Looks like you denied microphone access on this website, we need access to your microphone to create your account.\n\nPlease allow the browser to access your microphone.',
          button: 'Retry'
        }
      },
      'server-computation': {
        headline: 'Crafting your private key',
        text: 'Hold on, it will just take a moment'
      },
      'stm-choice': {
        headline: 'Enrollment Process',
        text: 'Take a selfie to create an account on Keyless and easily register on the platform.',
        button: 'Continue on Phone',
        secondary_button: 'Enroll on Desktop'
      },
      'stm-qrcode': {
        headline: 'Scan the QR Code',
        text: 'Please scan the QR code with your phone to continue the enrollment process.',
        button: {
          false: 'Copy Link',
          true: 'Copied to Clipboard'
        }
      }
    }
  },
  error: {
    FRAME_RESULTS_SET_UNSET: 'Please contact our support.',
    OPTIONS_UNSET: 'Please contact our support.',
    VIDEO_ELEMENT_UNSET: 'Please contact our support.',
    VIDEO_ELEMENT_EVENT_LISTENERS_UNSET: 'Please contact our support.',
    WEB_SOCKET_MESSAGE_SET_UNSET: 'Please contact our support.',

    USER_LOCKED_OUT: 'Too many attempts. You can try again in {lockout_expiration}.',

    MEDIA_DEVICES_EMPTY_AUDIO_INPUT_LABEL: 'Please allow the browser to access your microphone and try again.',
    MEDIA_DEVICES_EMPTY_VIDEO_INPUT_LABEL: 'Please allow the browser to access your camera and try again.',
    MEDIA_DEVICES_NO_VIDEO_INPUTS: 'No camera was found. Please connect a camera and try again.',

    MEDIA_STREAM_ABORT: 'Please make sure your camera is not being used by another application and try again.',
    MEDIA_STREAM_INVALID_STATE: 'Please make sure your camera is not being used by another application and try again.',
    MEDIA_STREAM_NOT_ALLOWED: 'Please make sure you granted camera accession permission and that no other application is using your camera, then try again.',
    MEDIA_STREAM_NOT_FOUND: 'No camera was found. Please connect a camera and try again.',
    MEDIA_STREAM_NOT_READABLE: 'Please make sure your camera is not being used by another application and try again.',
    MEDIA_STREAM_OVERCONSTRAINED: 'The camera does not meet our minimum requirements. Please use a different camera and try again.',
    MEDIA_STREAM_SECURITY: 'Please contact our support.',
    MEDIA_STREAM_TYPE: 'Please contact our support.',
    MEDIA_STREAM_UNSET: 'Please contact our support.',

    SERVER_CUSTOMER_NOT_FOUND: 'Please contact our support.',
    SERVER_FACE_DOES_NOT_MATCH: 'Make sure you are in a well-lit environment, possibly without any eyewear or hats.',
    SERVER_FORBIDDEN: 'Please try again or contact our support if the problem persists.',
    SERVER_IMAGE_ENCRYPT_FAILED: 'Please contact our support.',
    SERVER_INTERNAL_ERROR: 'Please try again or contact our support if the problem persists.',
    SERVER_NO_ATTEMPTS_LEFT: 'Please contact our support.',
    SERVER_RECOGNITION_FAILED: 'Make sure you are in a well-lit environment, possibly without any eyewear or hats.',
    SERVER_TIMEOUT: 'Please try again or contact our support if the problem persists.',
    SERVER_UNAVAILABLE_SERVICE: 'Please try again or contact our support if the problem persists.',
    SERVER_UNPROCESSABLE_EVENT: 'Please contact our support.',
    SERVER_USER_ALREADY_ENROLLED: 'The account {username} already exists.',
    SERVER_USER_LOCKED_OUT: 'Too many attempts. You can try again in {lockout_expiration}.',
    SERVER_USER_NOT_FOUND: 'The account {username} does not exist.',
    SERVER_VALIDATION_FAILED: 'Please contact our support.',

    SESSION_MANAGER_NOT_NULL: 'Please contact our support.',
    SESSION_MANAGER_NULL: 'Please contact our support.',

    EXCEPTION: 'Please contact our support.',
    RUNTIME_VIOLATION: 'Please contact our support.',
    SYMBOL_DESCRIPTION_UNSET: 'Please contact our support.',

    SESSION_ID_UNSET: 'Please contact our support.',

    CUSTOMER_UNSET: 'Please contact our support.',
    KEY_DECODE_FAILED: 'Please contact our support.',
    KEY_UNSET: 'Please contact our support.',
    KEY_ID_UNSET: 'Please contact our support.',
    USERNAME_UNSET: 'Please contact our support.',
    WEB_SOCKET_URL_PARSE_FAILED: 'Please contact our support.',
    WEB_SOCKET_URL_UNSET: 'Please contact our support.',

    WEB_ASSEMBLY_ABORTED: 'Please try again or contact our support if the problem persists.',
    WEB_ASSEMBLY_FACTORY_FAILED: 'Please contact our support.',
    WEB_ASSEMBLY_IMPORT_FAILED: 'Please contact our support.',
    WEB_ASSEMBLY_NOT_READY: 'Please contact our support.',
    WEB_ASSEMBLY_MODULE_NOT_FOUND: 'Please contact our support.',

    WEB_SOCKET_ERROR: 'Please try again or contact our support if the problem persists.',
    WEB_SOCKET_OPEN: 'Please try again or contact our support if the problem persists.',
    WEB_SOCKET_TIMEOUT: 'Please try again or contact our support if the problem persists.',
    WEB_SOCKET_UNEXPECTED_CLOSE: 'Please try again or contact our support if the problem persists.',

    QUEUE_UNSET: 'Please contact our support.',
    SYMBOL_UNSET: 'Please contact our support.',

    NONCANCELABLE: 'Please try again or contact our support if the problem persists.'
  }
}
```

{% hint style="info" %}
Remember that the `language` specified in the pack must match with the `lang` attribute set in the web component for the localization to work.
{% endhint %}

Here’s an example on changing the instructions screen headline:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-auth
      customer="CUSTOMER_NAME"
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      transaction-data='DATA_FROM_CUSTOMER_SERVER_TO_BE_SIGNED'
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-auth>
    <script src="./node_modules/@keyless/sdk-web-components/elements/auth/auth-element.iife.js"></script>
    <script>
      const auth = document.querySelector('kl-auth')

      auth.localizationPacks = [
        {
          data: {
            auth: {
              step: {
                'camera-instructions': {
                  headline: 'Custom Camera Instructions Title',
                }
              }
            }
          },
          language: 'en'
        }
      ]
    </script>
  </body>
</html>
```
