> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Adapt Copy With Translations

> Customize and localize the text displayed by the Dynamic React Native SDK using the locale prop on createClient.

<Card title="Recommended: JavaScript SDK for React Native" icon="react" color="#4779FE">
  While this SDK is still supported, we recommend using newer [JavaScript SDK](/docs/javascript/reference/react-native-quickstart), which is optimized for React Native, but also comes with a host of other benefits.
</Card>

The Dynamic React Native SDK lets you override almost any text it displays and add translations for additional languages. You pass a `locale` object to `createClient` when initializing the SDK, and you can switch the active language at runtime through `client.locale.changeLanguage`.

## Quick override

Change UI text by overriding the copy key in `locale`. The object is keyed by language code, and each language value contains nested translation overrides that match the SDK's default English copy structure.

```typescript theme={"system"}
import { createClient } from '@dynamic-labs/legacy-client';
import { ReactNativeExtension } from '@dynamic-labs/legacy-react-native-extension';

export const dynamicClient = createClient({
  environmentId: 'YOUR_ENVIRONMENT_ID',
  appName: 'My App',
  appOrigin: 'https://your-app.com',
  locale: {
    en: {
      dyn_widget: {
        connect: 'Sign in with wallet',
      },
      dyn_login: {
        title: {
          all_wallet_list: 'Choose a wallet',
        },
      },
    },
  },
}).extend(ReactNativeExtension());
```

<Tip>
  **Finding keys:** The React Native SDK renders UI inside a WebView. You can mirror the same `copykey` attribute names used in the web SDK. The nested object path matches the dot-separated key (for example, `dyn_login.title.all_wallet_list`).
</Tip>

## Supported languages

The top-level keys of `locale` must be one of the following language codes:

| Code | Language   |
| ---- | ---------- |
| `ar` | Arabic     |
| `da` | Danish     |
| `de` | German     |
| `en` | English    |
| `es` | Spanish    |
| `fi` | Finnish    |
| `fr` | French     |
| `he` | Hebrew     |
| `it` | Italian    |
| `ja` | Japanese   |
| `nl` | Dutch      |
| `pl` | Polish     |
| `pt` | Portuguese |
| `ru` | Russian    |
| `uk` | Ukrainian  |
| `zh` | Chinese    |

<Note>
  Any top-level language key that is not in the supported list is dropped. Other valid entries are still applied.
</Note>

## Full guide

In this guide we will override the "Select your wallet" text to "Find your favorite" in English and add an Italian translation. The key for that text is `dyn_login.title.all_wallet_list`.

```typescript theme={"system"}
const locale = {
  en: {
    dyn_login: {
      title: {
        all_wallet_list: 'Find your favorite',
      },
    },
  },
  it: {
    dyn_login: {
      title: {
        all_wallet_list: 'Trova il tuo preferito',
      },
    },
  },
};

export const dynamicClient = createClient({
  environmentId: 'YOUR_ENVIRONMENT_ID',
  appName: 'My App',
  appOrigin: 'https://your-app.com',
  locale,
}).extend(ReactNativeExtension());
```

The SDK merges your overrides with the default English translations at runtime, so you only need to specify the keys you want to change.

### Translation structure

The `locale` value is a nested object whose shape mirrors the SDK's translation resource. You can override nested objects arbitrarily deeply.

```typescript theme={"system"}
const locale = {
  en: {
    dyn_login: {
      title: {
        all: 'Welcome',
        all_wallet_list: 'Find your favorite',
      },
    },
  },
};
```

This is equivalent to the dot-separated path `dyn_login.title.all` and `dyn_login.title.all_wallet_list`.

### Adding a new language

To add a translation for a language that is not yet bundled, provide the same keys under that language code:

```typescript theme={"system"}
const locale = {
  it: {
    dyn_widget: {
      connect: 'Accedi',
    },
    dyn_login: {
      title: {
        all_wallet_list: 'Trova il tuo preferito',
      },
    },
  },
};
```

<Note>
  The SDK falls back to `en` for any key that is not provided in the chosen language. You do not need to mirror the entire translation tree.
</Note>

## Changing language at runtime

You can change the active language after initialization using the `locale` module on the client:

```typescript theme={"system"}
await dynamicClient.locale.changeLanguage('it');
```

This switches the SDK UI to Italian (or whichever supported language you pass) without re-creating the client.

## Validation and sanitization

The `locale` object is sent to the webview controller through the SDK manifest. The webview validates the structure before applying it:

* The root value must be an object.
* Each top-level key must be a supported language code.
* The value for each language must be an object (translation overrides).
* Invalid top-level keys are dropped, and the rest of the object is still applied.
* If the root value is not an object, the whole locale is ignored.

For example, the following object keeps the valid `en` entry and drops the unsupported `xx` key:

```typescript theme={"system"}
const locale = {
  en: {
    dyn_widget: {
      connect: 'Sign in with wallet',
    },
  },
  xx: {
    dyn_widget: {
      connect: 'Invalid language',
    },
  },
};
```

When an invalid entry is dropped, the SDK instruments the event so you can detect it in your logs.

### Validation messages and form errors

You can also override validation copy. For example, the email and phone form validation keys live under `dyn_login`:

```typescript theme={"system"}
const locale = {
  en: {
    dyn_login: {
      email_form: {
        invalid_email: 'Please enter a valid email address',
      },
      phone_number_form: {
        invalid_phone: 'Please enter a valid phone number',
        too_short: 'Phone number is too short',
      },
    },
  },
};
```

Other common validation keys include:

* `dyn_login.email_form.validation`
* `dyn_login.phone_number_form.validation`
* `dyn_login.phone_number_form.too_short`
* `dyn_active_wallet_info.testnet_warning`
* `dyn_send.error.invalid_balance`

## Runtime language changes

You can switch the active language at runtime after the SDK has initialized. The language code must match a top-level key in the `locale` object you passed to `createClient`.

```typescript theme={"system"}
await dynamicClient.locale.changeLanguage('it');
```

The WebView loads the translations provided for that language and falls back to `en` for any missing keys.

## Complete example

```typescript theme={"system"}
import { createClient } from '@dynamic-labs/legacy-client';
import { ReactNativeExtension } from '@dynamic-labs/legacy-react-native-extension';

const locale = {
  en: {
    dyn_widget: {
      connect: 'Sign in with wallet',
    },
    dyn_login: {
      title: {
        all: 'Welcome',
        all_wallet_list: 'Find your favorite',
      },
      email_form: {
        validation: 'Email is not valid',
      },
      phone_number_form: {
        validation: 'Phone number is not valid',
        too_short: 'Phone number is too short',
      },
    },
  },
  it: {
    dyn_widget: {
      connect: 'Accedi con il portafoglio',
    },
    dyn_login: {
      title: {
        all: 'Benvenuto',
        all_wallet_list: 'Trova il tuo preferito',
      },
    },
  },
};

export const dynamicClient = createClient({
  environmentId: 'YOUR_ENVIRONMENT_ID',
  appName: 'My App',
  appOrigin: 'https://your-app.com',
  locale,
}).extend(ReactNativeExtension());

// Later, for example when the user toggles language:
await dynamicClient.locale.changeLanguage('it');
```

## Troubleshooting

* **Overrides do not appear**: Make sure the language code is supported and the key path matches the dot-separated copy key exactly.
* **Invalid language is ignored**: The webview controller drops unsupported top-level language keys. Check that you are using one of the 17 supported codes.
* **Entire locale is ignored**: If the root value is not a valid object, the whole locale is discarded. Ensure `locale` is an object and not a string or array.
