# Management SDK - Localization

> Source: https://agilitycms.com/docs/dotNet/management-sdk-dotnet-localization

Two clients cover localization: `client.Locales` manages the instance's locales, and `client.Localization` copies or machine-translates pages and content into other locales.

> **Note:** This page covers version 2.0 of the .NET Management SDK. Both clients are new in 2.0. Upgrading from 1.x? See [Migrating to 2.0](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-migrating-to-2).

## Before you start

The examples assume a `client` and an instance GUID (`guid`). To set up the client, see the [Intro](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-intro).

```csharp
using Agility.Management.Sdk;
using Agility.Management.Sdk.Clients;
using Agility.Management.Sdk.Models;
```

Every method takes the instance GUID first. Every method also takes an optional `CancellationToken cancellationToken` as its last parameter. The signatures below leave it out.

## Method list

| Method | Description |
|---|---|
| `client.Locales.GetLocalesAsync` | List the enabled locales |
| `client.Locales.GetAllLocalesAsync` | List every locale, enabled or not |
| `client.Locales.GetLocaleAsync` | Get a locale by ID |
| `client.Locales.SaveLocaleAsync` | Create or update a locale |
| `client.Locales.EnableLocaleAsync` | Enable a locale |
| `client.Locales.DisableLocaleAsync` | Disable a locale |
| `client.Locales.SetSortOrderAsync` | Set the order locales appear in |
| `client.Localization.InitializePagesAsync` | Copy pages into other locales |
| `client.Localization.InitializeContentListsAsync` | Copy whole content lists into another locale |
| `client.Localization.InitializeContentItemsAsync` | Copy content items into other locales |
| `client.Localization.TranslatePagesAsync` | Copy and machine-translate pages |
| `client.Localization.TranslateContentListsAsync` | Copy and machine-translate whole content lists |
| `client.Localization.TranslateContentItemsAsync` | Copy and machine-translate content items |

---

## Locales

### GetLocalesAsync

List the instance's enabled locales.

```csharp
List<Locale> enabled = await client.Locales.GetLocalesAsync(guid);

foreach (var locale in enabled)
{
    Console.WriteLine($"{locale.LocaleCode} - {locale.LocaleName} (ID: {locale.LocaleID})");
}
```

**Signature:** `Task<List<Locale>> GetLocalesAsync(string instanceGuid)`

---

### GetAllLocalesAsync

List every locale, grouped into enabled and disabled.

```csharp
LocalesResponse all = await client.Locales.GetAllLocalesAsync(guid);

Console.WriteLine($"{all.EnabledLocales?.Count ?? 0} enabled, {all.DisabledLocales?.Count ?? 0} disabled");
```

**Signature:** `Task<LocalesResponse> GetAllLocalesAsync(string instanceGuid)`

---

### GetLocaleAsync

Get a locale by its ID.

```csharp
Locale locale = await client.Locales.GetLocaleAsync(guid, localeId);
```

**Signature:** `Task<Locale> GetLocaleAsync(string instanceGuid, int localeId)`

---

### SaveLocaleAsync

Create a locale, or update an existing one. `LocaleName` and `LocaleCode` are required.

```csharp
Locale added = await client.Locales.SaveLocaleAsync(guid, new Locale
{
    LocaleName = "French (Canada)",
    LocaleCode = "fr-ca",
}) ?? throw new InvalidOperationException("The API didn't return the saved locale.");

Console.WriteLine($"Saved locale ID: {added.LocaleID}");
```

The method returns the saved locale, or `null` if the API sends back no body.

**Signature:** `Task<Locale?> SaveLocaleAsync(string instanceGuid, Locale locale)`

---

### EnableLocaleAsync

Enable a locale.

```csharp
await client.Locales.EnableLocaleAsync(guid, added.LocaleID!.Value);
```

**Signature:** `Task<Locale?> EnableLocaleAsync(string instanceGuid, int localeId)`

---

### DisableLocaleAsync

Disable a locale.

```csharp
await client.Locales.DisableLocaleAsync(guid, added.LocaleID!.Value);
```

**Signature:** `Task<Locale?> DisableLocaleAsync(string instanceGuid, int localeId)`

---

### SetSortOrderAsync

Set the order locales appear in. Pass every locale ID, in the order you want.

```csharp
await client.Locales.SetSortOrderAsync(guid, [1, added.LocaleID!.Value, 3]);
```

Pass at least one ID. An empty list throws an `ArgumentException`.

**Signature:** `Task SetSortOrderAsync(string instanceGuid, IEnumerable<int> orderedLocaleIds)`

---

## Copying content into other locales

`client.Localization` copies pages, content lists and content items from one locale into others.

- **Initialize** copies them as they are.
- **Translate** copies them and machine-translates them.

Each call runs as a batch and returns a `BatchResult`. Pass `waitForBatch: false` to return as soon as the batch is queued. See [Batches](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-intro#batches).

### What to pass

Every request names a source locale (`LanguageCodeSource`) and one or more target locales. The items to copy are identified like this:

| Method | Request type | Items | Targets |
|---|---|---|---|
| `InitializePagesAsync` / `TranslatePagesAsync` | `InitializePageRequest` / `TranslatePageRequest` | `PageVersionIds` | `LanguageCodeTargets` |
| `InitializeContentListsAsync` / `TranslateContentListsAsync` | `InitializeContentListRequest` / `TranslateContentListRequest` | `ContentViewIds` | `LanguageCodeTarget` (one locale) |
| `InitializeContentItemsAsync` / `TranslateContentItemsAsync` | `InitializeContentRequest` / `TranslateContentRequest` | `ContentVersionIds` | `LanguageCodeTargets` |

> ⚠️ **Pages and content items are identified by version ID, not by page or content ID.** Use the `VersionID` on the item's `Properties`. Content lists are identified by their container IDs (`ContentViewID`).

---

### InitializeContentItemsAsync

Copy content items into other locales, as they are.

```csharp
var item = await client.Content.GetContentItemAsync(guid, "en-us", 42);

await client.Localization.InitializeContentItemsAsync(guid, new InitializeContentRequest
{
    LanguageCodeSource = "en-us",
    LanguageCodeTargets = ["fr-ca", "es-us"],
    ContentVersionIds = [item.Properties!.VersionID!.Value],
});
```

**Signature:** `Task<BatchResult> InitializeContentItemsAsync(string instanceGuid, InitializeContentRequest request, bool waitForBatch = true)`

---

### TranslateContentItemsAsync

Copy content items into other locales and machine-translate them.

```csharp
var item = await client.Content.GetContentItemAsync(guid, "en-us", 42);

await client.Localization.TranslateContentItemsAsync(guid, new TranslateContentRequest
{
    LanguageCodeSource = "en-us",
    LanguageCodeTargets = ["fr-ca", "es-us"],
    ContentVersionIds = [item.Properties!.VersionID!.Value],
});
```

**Signature:** `Task<BatchResult> TranslateContentItemsAsync(string instanceGuid, TranslateContentRequest request, bool waitForBatch = true)`

---

### InitializePagesAsync

Copy pages into other locales, as they are.

```csharp
var page = await client.Pages.GetPageAsync(guid, "en-us", 7);

await client.Localization.InitializePagesAsync(guid, new InitializePageRequest
{
    LanguageCodeSource = "en-us",
    LanguageCodeTargets = ["fr-ca", "es-us"],
    PageVersionIds = [page.Properties!.VersionID!.Value],
});
```

Use `LanguageCodeTargets`. The page requests also have a single `LanguageCodeTarget`, which the API ignores when `LanguageCodeTargets` is set.

**Signature:** `Task<BatchResult> InitializePagesAsync(string instanceGuid, InitializePageRequest request, bool waitForBatch = true)`

---

### TranslatePagesAsync

Copy pages into other locales and machine-translate them.

```csharp
var page = await client.Pages.GetPageAsync(guid, "en-us", 7);

await client.Localization.TranslatePagesAsync(guid, new TranslatePageRequest
{
    LanguageCodeSource = "en-us",
    LanguageCodeTargets = ["fr-ca"],
    PageVersionIds = [page.Properties!.VersionID!.Value],
});
```

**Signature:** `Task<BatchResult> TranslatePagesAsync(string instanceGuid, TranslatePageRequest request, bool waitForBatch = true)`

---

### InitializeContentListsAsync

Copy whole content lists into another locale, as they are. Content list requests take one target locale.

```csharp
var container = await client.Containers.GetContainerByReferenceNameAsync(guid, "blogposts");

await client.Localization.InitializeContentListsAsync(guid, new InitializeContentListRequest
{
    LanguageCodeSource = "en-us",
    LanguageCodeTarget = "fr-ca",
    ContentViewIds = [container.ContentViewID!.Value],
});
```

**Signature:** `Task<BatchResult> InitializeContentListsAsync(string instanceGuid, InitializeContentListRequest request, bool waitForBatch = true)`

---

### TranslateContentListsAsync

Copy whole content lists into another locale and machine-translate them.

```csharp
var container = await client.Containers.GetContainerByReferenceNameAsync(guid, "blogposts");

await client.Localization.TranslateContentListsAsync(guid, new TranslateContentListRequest
{
    LanguageCodeSource = "en-us",
    LanguageCodeTarget = "fr-ca",
    ContentViewIds = [container.ContentViewID!.Value],
});
```

**Signature:** `Task<BatchResult> TranslateContentListsAsync(string instanceGuid, TranslateContentListRequest request, bool waitForBatch = true)`

---

## Error handling

```csharp
try
{
    await client.Localization.TranslateContentItemsAsync(guid, request);
}
catch (AgilityBatchException ex)
{
    // The batch ran, but some items failed.
    foreach (var failed in ex.Batch?.Items?.Where(i => i.ErrorMessage is not null) ?? [])
    {
        Console.Error.WriteLine($"{failed.ItemID}: {failed.ErrorMessage}");
    }
}
catch (AgilityManagementException ex)
{
    Console.Error.WriteLine($"Error {(int?)ex.StatusCode}: {ex.ApiMessage ?? ex.Message}");
}
```

See [Errors](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-intro#errors) for the full list of exceptions.
