Was this page helpful?

Localization with Contentful

If you are working with content that needs to be available in multiple languages, locales let you define localizations of content and select a specific locale when querying the Content Delivery API.

Every Space has its own set of locales, and each locale is uniquely identified by its ISO code (e.g., en-US or de-AT). There's always one default locale defined when you create a space, shown by default in the Contentful web app and used for Content Delivery API queries that do not request a specific locale.

Locales are environment-level entities.

Add a locale

If you are writing scripts or applications, use the Content Management API to add a locale to a space use the following POST request with the name and ISO code contained within the body:

curl -X POST
  -H "Authorization: Bearer <CONTENT_MANAGEMENT_KEY>"
  -H "Content-Type: application/vnd.contentful.management.v1+json"
  -d '{
   "name":"English (British)",
   "code":"en-GB"
}'
"https://api.contentful.com/spaces/<SPACE_ID>/locales"

There are a couple of other options you can send with the API call, read our API documentation to find out more. You can also enable or disable the locale in the API response and in the entry editor. If you disable the locale in the response, the content is still visible and editable in the entry editor, but isn't delivered to your application. Disabling in the entry editor means the locale fields aren't visible or editable, but the content continues to be delivered via the API.

Optional locales

It may be the case that you want the default locale to be required but the other locales to be optional. For example, if you want to ensure that the English locale fields are always filled out but it’s okay to publish the entry with the Chinese field empty. In that case, remember to select the Allow empty fields for this locale checkbox.

This is a particularly useful setting for working with multiple translators. Is your Spanish translator a bit quicker than your French translator? If the Spanish market is more important to your business, you can publish the entry first in Spanish and let the French catch up later.

Enable locales

After adding a new locale and adjusting the settings, you must enable it. Locales are enabled on a per-space and per-field basis, and you can also toggle the visibility of the locale fields within your entries. This sounds complicated but it’s really a good thing! It gives you more control over what content is localized and when. After adding a locale to a space, you can define which fields in your content types you want localized.

Enable on fields

It's possible to use the Content Management API to update content types and localize fields.

The following PUT request enables localization for the fields productName and productDescription of the content type Product by setting their localized property to true:

curl -X PUT
  -H "Authorization: Bearer <CONTENT_MANAGEMENT_KEY>"
  -H "Content-Type: application/vnd.contentful.management.v1+json"
  -d '{
  "name": "Product",
  "displayField": "productName",
  "fields": [
    {
      "name": "Description",
      "id": "productDescription",
      "type": "Text",
      "localized": true,
      "validations": []
    },
    {
      "name": "Product name",
      "id": "productName",
      "type": "Text",
      "localized": true,
      "validations": []
    },
    // Other fields in content type
  ]
}'
"https://api.contentful.com/spaces/<SPACE_ID>/content_types/<CONTENT_TYPE_ID>"
Note: The example above doesn't show all fields in the content type, but you need to include all, even those that you are not changing.

Edit the default locale

You can edit the code and name of the default locale as long as no other locale points to it as a fallback.

NOTE: When using entry-level localization, change the default locale to a custom locale named Default (default) instead of a language code such as en-US. For more information, see Localization strategies.

Use the following PUT request to edit the custom code and custom name of the default locale:

curl -X PUT 
-H "Authorization: Bearer CMA_TOKEN" "https://api.contentful.com/spaces/SPACE_ID/environments/ENV_ID/locales/LOCALE_ID" 
-d '{"code": "customCode", "name": "Custom Name"}' 
-H "X-Contentful-Version: 1"
NOTE: The version number depends on whether the locale has been edited before.

Handling the missing translations

The biggest problem editorial teams face is how to handle the missing translations for particular languages in a scalable and elegant way. To help you deal with that, you can turn to configuring custom fallback locales and understand the technical implications of leaving localized fields empty.

Custom fallback locales

By default, whenever Contentful comes across an entry with a missing localized content, it reverts to a default fallback locale (e.g. English). But English is not always the most appropriate language to default to, because Swiss visitors usually prefer to see a German or French version of your website, while Argentinians are more comfortable with accessing content in standard Spanish. To accommodate the needs of your audience, you can define a custom fallback logic for your locales.

To construct a fallback tree, start with the lowest branch, say, Swiss German (locale code de-CH) and pick standard German (de-DE) as its fallback locale. You can repeat the same step for the Austrian German (de-AT), Luxembourgish German (de-LU), and Belgian German (de-BE), provided you actively use these locales. For the standard German, you can repeat the step and add another language as its fallback, or you can specify that no further fallback should be invoked.

To get the hang of it, imagine we have an entry with three locales - Spanish (Mexico)(es-MX), Spanish (Spain)(es-ES), and English (US)(en-US). The fallback logic is defined as (es-MX) > (es-ES) > (en-US) and here is how API would respond in different scenarios:

CMA call CDA call for locale es-MX Fallback logic
"title": {
"en-US": "Hello NYC"
}
"title": "Hello NYC" Falls back to es-ES which falls back to en-US which is "Hello NYC"
"title": {
"en-US": "Hello NYC",
"es-ES": "Hola Barcelona"
}
"title": "Hola Barcelona" Falls back to es-ES which is "Hola Barcelona"
"title": {
"en-US": "Hello NYC",
"es-ES": "Hola Barcelona",
"es-MX": "Hola Mexico"
}
"title": "Hola Mexico" No fallback implied, returning es-MX value which is “Hola Mexico”

When the same setup is modified to stop the fallback at (es-ES), the API output behaves as follows:

CMA call CDA call for locale es-MX Fallback logic
"title": {
"en-US": "Hello NYC"
}
No “title” field returned in the payload since no value present and the fallback chain stops at es-ES.

Remember that once you designate a particular locale as a part of a fallback chain, you cannot disable it in a CDA response. We also didn't want you to accidentally get stuck in an infinite loop, so the dropdown for a new locale automatically excludes all the locales already used in the current fallback chain.

Considerations on fallback locales:

  • If you don't want a locale to fallback to anything, set its fallbackCode property to null.
  • You can create fallback chains. For example de-CH (Swiss German) fallbacks to de-AT (Austrian German) and de-AT fallbacks to de-DE (German German) is a valid setup. If a locale in the chain doesn't have content, the API requests the next one.
  • Your fallback chain can't contain cycles, for example a fallback chain where de-CH fallbacks to de-AT and de-AT fallbacks to de-CH.
  • Contentful uses the content of the fallback locale only when the requested one is not present in the entry or asset (i.e. you request 'en-US' but the entry has only content for 'de-DE'). This means that values like null or an empty string ("") don't trigger the system to use the value of the fallback locale. This can only be set via the API, and not with the web app or client libraries.
  • If you don't specify a locale in your request, the entry from the default locale (en-US in this example) is delivered:
curl -X GET "https://cdn.contentful.com/spaces/<SPACE_ID>/entries/<ENTRY_ID>?access_token=<CONTENT_DELIVERY_KEY>"

Empty fields

Another important nuance to master when handling edge cases in your web app is to understand when the CDA uses the defined fallback locale to return a value. The CDA fallback chain kicks in only when no value is defined for a given locale. To use the previous example, if es-MX locale has no value, then the CDA looks into es-ES. But what do we mean exactly by "no value"? Programmers know that this can take many shapes from undefined to "" to null.

Whenever you interact with Contentful via the CMA, we follow the "what you set is what you get" rule, meaning that setting either a null or an empty string value for the es-MX locale, returns this same value in the CDA payload and prevent the fallback mechanism from happening. By contrast, not setting any value for the locale results in the CDA returning the value of a fallback locale.

The web app takes a more conservative approach towards handling empty values. Whenever you have an entry with an empty field, it triggers the fallback chain or, to put it in technical terms, editors cannot set a field value to "" or null from within the web app. This ensures a smoother editing experience and avoids accidental edits leading to undesired behavior. Also, note that any value set using the CMA is preserved by the web app unless an editor edits the field.

Retrieve entries with a specific locale

If you want to retrieve fields from a specific locale (e.g de-AT), use the locale=de-AT parameter in your request:

curl -X GET "https://cdn.contentful.com/spaces/<SPACE_ID>/entries/<ENTRY_ID>?access_token=<CONTENT_DELIVERY_KEY>&locale=de-AT"

Retrieve all translations for an entry

You can retrieve all localized versions of an entry by using the 'wildcard' locale=* parameter:

URL of request

curl -X GET "https://cdn.contentful.com/spaces/<SPACE_ID>/entries/<ENTRY_ID>?access_token=<CONTENT_DELIVERY_KEY>&locale=*"

Locales and the Sync API

No matter which locale you specify, the synchronization API always includes all localized content, using the same structure as the wildcard locale option above:

URL of request

curl -X GET "https://cdn.contentful.com/spaces/<SPACE_ID>/entries/<ENTRY_ID>sync?initial=true?access_token=<CONTENT_DELIVERY_KEY>&locale=de-AT"

Additional resources

To learn how to set up and manage locales in the Contentful web app, refer to Manage locales.