> ## 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 Flutter SDK using the locale prop on ClientProps.

The Dynamic Flutter SDK lets you override almost any text it displays and add translations for additional languages. You pass a `locale` object through `ClientProps` when initializing the SDK.

## 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.

```dart theme={"system"}
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:flutter/material.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  DynamicSDK.init(
    props: ClientProps(
      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',
            },
          },
        },
      },
    ),
  );

  runApp(const MyApp());
}
```

<Tip>
  **Finding keys:** The Flutter SDK renders UI inside a WebView. While you cannot right-click inspect on a mobile device, 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`.

```dart 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',
      },
    },
  },
};
```

Pass the locale object to `ClientProps`:

```dart theme={"system"}
DynamicSDK.init(
  props: ClientProps(
    environmentId: 'your-environment-id',
    appName: 'My App',
    appOrigin: 'https://your-app.com',
    locale: locale,
  ),
);
```

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 `Map<String, dynamic>` whose shape mirrors the SDK's translation resource. You can override nested objects arbitrarily deeply.

```dart 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:

```dart 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>

## 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:

```dart 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`:

```dart 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. Use the `LocaleLanguage` enum so only supported codes can be passed.

```dart theme={"system"}
import 'package:dynamic_sdk/dynamic_sdk.dart';

await DynamicSDK.instance.locale.changeLanguage(LocaleLanguage.it);
```

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

## Complete example

```dart theme={"system"}
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:flutter/material.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  DynamicSDK.init(
    props: ClientProps(
      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': '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',
            },
          },
        },
      },
    ),
  );

  runApp(const MyApp());
}
```

## Troubleshooting

* **Overrides do not appear**: Make sure the language code is supported (`LocaleLanguage` enum or a valid top-level key) 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 a `Map<String, dynamic>` and not a string or list.
