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

The Dynamic Kotlin 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.

```kotlin theme={"system"}
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.core.ClientProps
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

val locale = buildJsonObject {
    put("en", buildJsonObject {
        put("dyn_widget", buildJsonObject {
            put("connect", "Sign in with wallet")
        })
        put("dyn_login", buildJsonObject {
            put("title", buildJsonObject {
                put("all_wallet_list", "Choose a wallet")
            })
        })
    })
}

val props = ClientProps(
    environmentId = "your-environment-id",
    appName = "Your App",
    appOrigin = "https://your-app.com",
    locale = locale
)

DynamicSDK.initialize(props, applicationContext, this)
```

<Tip>
  **Finding keys:** The Kotlin 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`.

```kotlin theme={"system"}
val locale = buildJsonObject {
    put("en", buildJsonObject {
        put("dyn_login", buildJsonObject {
            put("title", buildJsonObject {
                put("all_wallet_list", "Find your favorite")
            })
        })
    })
    put("it", buildJsonObject {
        put("dyn_login", buildJsonObject {
            put("title", buildJsonObject {
                put("all_wallet_list", "Trova il tuo preferito")
            })
        })
    })
}

val props = ClientProps(
    environmentId = "your-environment-id",
    appName = "Your App",
    appOrigin = "https://your-app.com",
    locale = locale
)

DynamicSDK.initialize(props, applicationContext, this)
```

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 mirrors the SDK's translation resource. You can override nested objects arbitrarily deeply.

```kotlin theme={"system"}
val locale = buildJsonObject {
    put("en", buildJsonObject {
        put("dyn_login", buildJsonObject {
            put("title", buildJsonObject {
                put("all", "Welcome")
                put("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:

```kotlin theme={"system"}
val locale = buildJsonObject {
    put("it", buildJsonObject {
        put("dyn_widget", buildJsonObject {
            put("connect", "Accedi")
        })
        put("dyn_login", buildJsonObject {
            put("title", buildJsonObject {
                put("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:

```kotlin theme={"system"}
val locale = buildJsonObject {
    put("en", buildJsonObject {
        put("dyn_widget", buildJsonObject {
            put("connect", "Sign in with wallet")
        })
    })
    put("xx", buildJsonObject {
        put("dyn_widget", buildJsonObject {
            put("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`:

```kotlin theme={"system"}
val locale = buildJsonObject {
    put("en", buildJsonObject {
        put("dyn_login", buildJsonObject {
            put("email_form", buildJsonObject {
                put("invalid_email", "Please enter a valid email address")
            })
            put("phone_number_form", buildJsonObject {
                put("invalid_phone", "Please enter a valid phone number")
                put("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.

```kotlin theme={"system"}
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.LocaleLanguage
import kotlinx.coroutines.launch

lifecycleScope.launch {
    DynamicSDK.getInstance().locale.changeLanguage(LocaleLanguage.it)
}
```

`changeLanguage` is a `suspend` function, so it must be called from a coroutine. The WebView loads the translations provided for that language and falls back to `en` for any missing keys.

## Complete example

```kotlin theme={"system"}
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.core.ClientProps
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

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

val props = ClientProps(
    environmentId = "your-environment-id",
    appName = "Your App",
    appOrigin = "https://your-app.com",
    locale = locale
)

DynamicSDK.initialize(props, applicationContext, this)
```

## 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 `JsonObject` and not a string or array.
