Skip to content

Add localization

In general, there are two types of localization in CMS data: document-level and field-level. You may use both simultaneously, or choose to use one or the other depending on the localization features of your data source.

Define locales

Before localizing your connector, you must define all available locales in your data source.

connector.model(({ define }) => {
define.locales([
{
code: "en-US",
default: true,
},
{
code: "ca-FR",
},
]);
});

Note that if you do not flag one of your locales as the default locale, the first locale in the array will be set as the default. If you set more than one locale as the default, the SDK will throw an error.

Document-level localization

In document-level localization, a new document is inserted for each localized version of content.

For example, you might have an English language document with the ID page-1-en and it’s corresponding French translation might be a document with the ID page-1-fr.

connector.sync(({ models }) => {
models.Page.insert({
id: "page-1-en",
title: "Hello world",
_locale: "en-US",
});
models.Page.insert({
id: "page-1-fr",
title: "Bonjour le monde",
_locale: "ca-FR",
});
});

Enable document-level localization

For each document model you intend to localize, you must set localized: true on the document model definition.

connector.model(({ define }) => {
define.document({
name: "Page",
title: { type: "String" },
localized: true, // <-- this turns on localization for this document model
});
});

Default document locale

Note that the default locale will apply to all inserted documents that don’t have a locale set.

In the following code example, the inserted document will automatically have _locale: "en-US" set on it.

connector.model(({ define }) => {
define.locales([{ code: "en-US", default: true }]);
define.document({ name: "Page", localized: true, title: { type: "String" } });
});
connector.sync(({ models }) => {
models.Page.insert({
id: "page-1-en",
title: "Hello world",
});
});

Since localized documents typically have other translations of the same content, you can set the _translations field to link together related translations.

connector.sync(({ models }) => {
models.Page.insert({
id: "page-1-en",
title: "Hello world",
_locale: "en-US",
_translations: ["page-1-fr"],
});
models.Page.insert({
id: "page-1-fr",
title: "Bonjour le monde",
_locale: "ca-FR",
_translations: ["page-1-en"],
});
});

Query document-level localized data

Now that your connector supports document localization, you can query localized data in GraphQL.

When you query for all documents of a certain type, all translated documents are returned together.

query {
allLocalizedPage {
nodes {
id
title
_locale
}
}
}

Result:

{
"allLocalizedPage": {
"nodes": [
{
"id": "page-1-en",
"title": "Hello world",
"_locale": "en-US"
},
{
"id": "page-1-fr",
"title": "Bonjour le monde",
"_locale": "ca-FR"
}
]
}
}

You can use filters to return documents in a specific locale:

query {
allLocalizedPage(filter: { _locale: { eq: "ca-FR" } }) {
nodes {
id
title
_locale
}
}
}

Result:

{
"allLocalizedPage": {
"nodes": [
{
"id": "page-1-fr",
"title": "Bonjour le monde",
"_locale": "ca-FR"
}
]
}
}

You can query for any related translations using the _translations field:

query {
allLocalizedPage(filter: { _locale: { eq: "ca-FR" } }) {
nodes {
title
_locale
_translations {
title
_locale
}
}
}
}

Result:

{
"allLocalizedPage": {
"nodes": [
{
"title": "Bonjour le monde",
"_locale": "ca-FR",
"_translations": [
{
"title": "Hello world",
"_locale": "en-US"
}
]
}
]
}
}

Field-level localization

In field-level localization, a document is inserted with multiple values for each field to support each locale.

For example, you might have a Product document with a title field that includes a value for en-US and another value for fr-CA.

Enable field-level localization

For each field you intend to localize, you must define locales, set a default locale, and then set localized: true on the model definition for the document that contains the field.

connector.model(({ define }) => {
// locales must be defined if any fields are marked as localized
define.locales([{ code: `en-US`, default: true }, { code: `fr-CA` }]);
define.document({
name: `Product`,
fields: {
title: {
type: `String`,
// each localized field must have this set
localized: true,
},
},
});
});

Insert localized field data

After you enable field-level localization, you can insert data into the fields using an object where the keys are locale codes and the values are the localized field values.

connector.sync(({ models }) => {
await models.Product.insert({
id: `product-1`,
title: {
"en-US": "Hello world",
"fr-CA": "Bonjour le monde",
},
});
});

If you insert data in the non-localized format, it will be translated to use the default locale. This allows you to build a non-localized Connector and then later enable field localization and incrementally localize field data.

For example, if you insert data with the following format:

connector.sync(({ models }) => {
await models.Product.insert({
id: `product-1`,
title: `Hello world`,
});
});

This data will be translated internally and stored as the following, using the default locale specified for fields:

connector.sync(({ models }) => {
await models.Product.insert({
id: `product-1`,
title: {
"en-US": `Hello world`,
},
});
});

Query field-level localized data

Now that your Connector supports field localization, you can query localized data in GraphQL.

When you query for all documents of a certain type, all translated documents are returned together, but you must select the queried locale for each field.

query {
allLocalizedPage {
nodes {
id
title @locale(code: "fr-CA")
}
}
}

Result:

{
"allLocalizedPage": {
"nodes": [
{
"id": "page-1",
"title": "Bonjour le monde"
}
]
}
}

If you leave out the locale, Connect and the visual editor will return the value for the default locale for each field:

query {
allLocalizedPage {
nodes {
id
title
}
}
}

Result:

{
"allLocalizedPage": {
"nodes": [
{
"id": "page-1",
"title": "Hello world"
}
]
}
}

You can set the @locale directive on any field and it will cascade down through your query:

query {
allLocalizedPage @locale(code: "fr-CA") {
nodes {
title
contentBlocks {
title
content
}
}
}
}

Result:

{
"allLocalizedPage": {
"nodes": [
{
"id": "page-1",
"title": "Bonjour le monde",
"contentBlocks": [
{
"title": "Bonjour",
"content": "le monde"
}
]
}
]
}
}

You can override any nested field with a different @locale:

query {
allLocalizedPage @locale(code: "fr-CA") {
nodes {
title
contentBlocks @locale(code: "en-US") {
title
content
}
}
}
}

Result:

{
"allLocalizedPage": {
"nodes": [
{
"id": "page-1",
"title": "Bonjour le monde",
"contentBlocks": [
{
"title": "Hello",
"content": "world"
}
]
}
]
}
}

You can also query multiple locales at once using GraphQL field aliases:

query {
allLocalizedPage {
nodes {
enTitle: title @locale(code: "en-US")
frTitle: title @locale(code: "fr-CA")
}
}
}

Result:

{
"allLocalizedPage": {
"nodes": [
{
"id": "page-1",
"enTitle": "Hello world",
"frTitle": "Bonjour le monde"
}
]
}
}

Combine field-level and document-level localization

In most cases, your data model will only use one kind of localization — field or document level. However, there is support for using both simultaneously if your data model requires it.

A common use case for using both simultaneously is to narrow localized content to a specific region.

For example, you might use document-level localization to insert localized documents for French and English languages. Then, within each localized document, further narrow the translated field data to regional variations of French for Canada and France. Similarly, you may want to narrow translated fields to regional variations of English for Canada and the UK.

connector.sync(({ models }) => {
models.Product.insert({
id: "en-1",
_locale: "en", // <- document-level
title: {
CA: "Chips", // <- field-level
UK: "Crisps",
},
packaging: {
CA: "Bag",
UK: "Packet",
},
});
models.Product.insert({
id: "fr-1",
_locale: "fr",
title: {
CA: "Chips",
FR: "Chips",
},
packaging: {
CA: "Sac",
FR: "Paquet",
},
});
});

With this example data, you could then query for a specific language and region:

query {
frFrProducts: allProduct(filter: { _locale: { eq: "fr" } }) {
nodes @locale(code: "FR") {
title
packaging
}
}
enUkProducts: allProduct(filter: { _locale: { eq: "en" } }) {
nodes @locale(code: "UK") {
title
packaging
}
}
}

Result:

{
"frFrProducts": {
"nodes": [
{
"title": "Chips",
"packaging": "Paquet"
}
]
},
"enUkProducts": {
"nodes": [
{
"title": "Crisps",
"packaging": "Packet"
}
]
}
}

This is a simplified example for illustration purposes. In the real world, you may want to narrow localized content for different regions within a single country. For example, fr-CA to set Canadian French as the document locale. Then, fr-CA-BC and fr-CA-QC as field locale codes where each region, in this case the Canadian provinces of British Columbia and Quebec, may have different product information for the same language.

Your locale codes may be any string value that suits your use case, they aren’t limited to official language codes.

Got it!

Your feedback helps us improve our docs.