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", });});
Set related translations
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.