Localized content

Find out how to retrieve your translated content from Kentico Cloud.

Localization in Kentico Cloud allows you to define multiple languages for your project and create culture-specific content. You can find more information about adding project languages or switching between language variants of content items in our Help Center.

In this tutorial, you'll learn how to:

  • Choose a URL pattern for your app
  • Get items in a specific language
  • Get item by its localized URL slug

Choosing a URL pattern

When developing multilingual web apps, it is a good practice to ensure unique URL addresses for each page. One approach is to use language prefixes in URLs, such as en-us or es-es, to differentiate between content cultures. For example, the URLs can be https://myapp.com/en-us/home for English language and https://myapp.com/es-es/home for Spanish language. To retrieve content items in a specific language, see Getting localized content items.

A worked example

See an example of adding multi-lingual support to a .NET Core MVC application in a blog post on Working with Localization in Kentico Cloud.

You can also combine the language prefixes with localized names of your content items. That is, using localized URL slugs. For example, a URL slug of the Home content item translated to Spanish is inicio. In this case, the URL for the Home content item in Spanish language translates to https://myapp.com/es-es/inicio. If you need to retrieve content items by their URL slug value, see Getting items with a specific URL slug.

Project languages

Each language in your Kentico Cloud project is uniquely identified by its codename. This codename can be any string you choose, such as, english, en-US, or en-GB.

Default language

All Kentico Cloud projects contain a single default language that cannot be removed. When getting content without specifying a language, the Delivery API returns content in the default language.

Getting localized content items

To get localized content, specify the language query string and provide the codename of the requested language. For example, in order to get the On Roasts article in Spanish language, you can call the View a content item endpoint like this /items/on_roasts?language=es-ES.

curl --request GET \
  --url 'https://deliver.kenticocloud.com/975bf280-fd91-488c-994c-2f04416e5ee3/items/on_roasts?language=es-ES' \
  --header 'content-type: application/json'
using KenticoCloud.Delivery;

// Initializes a content delivery client
IDeliveryClient client = DeliveryClientBuilder
      .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3")
      .Build();

// Gets an article in Spanish
// Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models
DeliveryItemResponse<Article> response = await client.GetItemAsync<Article>("on_roasts",
    new LanguageParameter("es-ES")
);

ContentItem item = response.Item;
const KenticoCloud = require('kentico-cloud-delivery');

// Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models
class Article extends KenticoCloud.ContentItem {
    constructor() {
        super();
    }
}

const deliveryClient = new KenticoCloud.DeliveryClient({
  projectId: '975bf280-fd91-488c-994c-2f04416e5ee3',
  typeResolvers: [
    new KenticoCloud.TypeResolver('article', () => new Article)
  ]
});

deliveryClient.item('on_roasts')
  .languageParameter('es-ES')
  .getObservable()
  .subscribe(response => console.log(response.item));
import { ContentItem, DeliveryClient, Fields, TypeResolver } from 'kentico-cloud-delivery';

// Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models
export class Article extends ContentItem {
    public title: Fields.TextField;
    public summary: Fields.TextField;
    public post_date: Fields.DateTimeField;
    public teaser_image: Fields.AssetsField;
    public related_articles: Article[];
}

const deliveryClient = new DeliveryClient({
  projectId: '975bf280-fd91-488c-994c-2f04416e5ee3',
  typeResolvers: [
    new TypeResolver('article', () => new Article)
  ]
});

deliveryClient.item<Article>('on_roasts')
  .languageParameter('es-ES')
  .getObservable()
  .subscribe(response => console.log(response.item));
import com.kenticocloud.delivery;

DeliveryClient client = new DeliveryClient("975bf280-fd91-488c-994c-2f04416e5ee3");

List<NameValuePair> params = DeliveryParameterBuilder.params().language("es-ES").build();

// Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models
ArticleItem item = client.getItem("on_roasts", ArticleItem.class, params);
import com.kenticocloud.delivery_core.*;
import com.kenticocloud.delivery_rx.*;

import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;

// Prepares an array to hold strongly-typed models
List<TypeResolver<?>> typeResolvers = new ArrayList<>();

// Registers the type resolver for articles
typeResolvers.add(new TypeResolver<>(Article.TYPE, new Function<Void, Article>() {
    @Override
    public Article apply(Void input) {
        return new Article();
    }
}));

// Prepares the DeliveryService configuration object
String projectId = "975bf280-fd91-488c-994c-2f04416e5ee3";
IDeliveryConfig config = DeliveryConfig.newConfig(projectId)
    .withTypeResolvers(typeResolvers);

// Initializes a DeliveryService for Java projects
IDeliveryService deliveryService = new DeliveryService(config);

// Gets the Spanish variant of an article using a simple request
Article article = deliveryService.<Article>item("on_roasts")
    .languageParameter("es-ES")
    .get()
    .getItem();

// Gets the Spanish variant of an article using RxJava2
deliveryService.<Article>item("on_roasts")
    .languageParameter("es-ES")
    .getObservable()
    .subscribe(new Observer<DeliveryItemResponse<Article>>() {
        @Override
        public void onSubscribe(Disposable d) {
        }

        @Override
        public void onNext(DeliveryItemResponse<Article> response) {
            // Gets the article
            Article article = response.getItem();
        }

        @Override
        public void onError(Throwable e) {
        }

        @Override
        public void onComplete() {
        }
    });
<?php

// Defined by Composer to include required libraries
require __DIR__ . '/vendor/autoload.php';

use KenticoCloud\Delivery\DeliveryClient;
use KenticoCloud\Delivery\QueryParams;

$client = new DeliveryClient('975bf280-fd91-488c-994c-2f04416e5ee3');

$items = client->getItem('on_roasts', (new QueryParams())
            ->language('es-ES'));
import KenticoCloud

let client = DeliveryClient.init(projectId: "975bf280-fd91-488c-994c-2f04416e5ee3")

// More about strongly-typed models https://github.com/Kentico/cloud-sdk-swift#using-strongly-typed-models
client.getItem(modelType: Article.self, itemName: "on_roasts", language: "es-ES") { (isSuccess, itemResponse, error) in
    if isSuccess {
        if let article = itemResponse?.item {
            // Use your item here
        }
    } else {
        if let error = error {
            print(error)
        }
    }
}

If the specified content item does not exist in the requested language, the Delivery API checks whether the content is present in the fallback language configured for the requested language. In case the query does not match any content, the API returns a 404 error response.

Getting items by localized URL slug

Depending on how you implement routing in your app, you might want to request content items based on the knowledge of their URL slugs.

To retrieve a content item that is specified by a localized URL slug, you need to call the List content items endpoint and apply appropriate filtering parameters.

For example, in order to get a Home content item whose URL slug in Spanish is inicio, you can use a query like this /items?language=es-ES&system.type=home&elements.url_pattern=inicio.

Here's what the query strings do:

  • language=es-ES – specifies the codename of the requested language.
  • system.type=home – filters content items by their content type.
  • elements.url_pattern=inicio – filters content items by a value of a specific content element.
curl --request GET \
  --url 'https://deliver.kenticocloud.com/975bf280-fd91-488c-994c-2f04416e5ee3/items?language=es-ES&system.type=home&elements.url_pattern=inicio' \
  --header 'content-type: application/json'
using KenticoCloud.Delivery;

// Initializes a content delivery client
IDeliveryClient client = DeliveryClientBuilder
      .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3")
      .Build();

// Gets the Home content item in Spanish based on the item's URL slug value
// Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models
DeliveryItemListingResponse<Home> response = await client.GetItemsAsync<Home>(
    new LanguageParameter("es-ES"),
    new EqualsFilter("system.type", "home"),
    new EqualsFilter("elements.url_pattern", "inicio")
    );

var items = response.Items;
const KenticoCloud = require('kentico-cloud-delivery');

// Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models
class Home extends KenticoCloud.ContentItem {
    constructor() {
        super();
    }
}

const deliveryClient = new KenticoCloud.DeliveryClient({
    projectId: '975bf280-fd91-488c-994c-2f04416e5ee3',
    typeResolvers: [
        new KenticoCloud.TypeResolver('home', () => new Home)
    ]
});

deliveryClient.items('home')
  .type('home')  
  .languageParameter('es-ES')
  .depthParameter(0)
  .equalsFilter('elements.url_pattern', 'inicio')
  .getObservable()
  .subscribe(response => console.log(response));
import { ContentItem, DeliveryClient, Fields, TypeResolver } from 'kentico-cloud-delivery';

// Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models
import { Article } from './models/article';
export class Home extends ContentItem {
    public url_pattern: Fields.UrlSlugField;
    public contact: Fields.TextField;
    public articles: Article[];
}

const deliveryClient = new DeliveryClient({
    projectId: '975bf280-fd91-488c-994c-2f04416e5ee3',
    typeResolvers: [
        new TypeResolver('home', () => new Home)
    ]
});

deliveryClient.items<Home>()
    .type('home')  
    .languageParameter('es-ES')
    .depthParameter(0)
    .equalsFilter('elements.url_pattern', 'inicio')
    .getObservable()
    .subscribe(response => console.log(response));
import com.kenticocloud.delivery;

DeliveryClient client = new DeliveryClient("975bf280-fd91-488c-994c-2f04416e5ee3");

List<NameValuePair> params = DeliveryParameterBuilder.params()
    .language("es-ES")
    .filterEquals("system.type", "home")
    .filterEquals("elements.url_pattern", "inicio")
    .build();

// Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models
List<HomeItem> items = client.getItems(HomeItem.class, params);
import com.kenticocloud.delivery_core.*;
import com.kenticocloud.delivery_rx.*;

import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;

// Prepares an array to hold strongly-typed models
List<TypeResolver<?>> typeResolvers = new ArrayList<>();

// Registers the type resolver for articles
typeResolvers.add(new TypeResolver<>(Home.TYPE, new Function<Void, Home>() {
    @Override
    public Home apply(Void input) {
        return new Home();
    }
}));

// Prepares the DeliveryService configuration object
String projectId = "975bf280-fd91-488c-994c-2f04416e5ee3";
IDeliveryConfig config = DeliveryConfig.newConfig(projectId)
    .withTypeResolvers(typeResolvers);

// Initializes a DeliveryService for Java projects
IDeliveryService deliveryService = new DeliveryService(config);

// Gets the Spanish variant of a Home content item that has "inicio" in its "URL pattern" element using a simple request
List<Home> home = deliveryService.<Home>items()
    .languageParameter("es-ES")
    .equalsFilter("system.type", "home")
    .equalsFilter("elements.url_pattern", "inicio")
    .get()
    .getItems();

// Gets the Spanish variant of a Home content item that has "inicio" in its "URL pattern" element using RxJava2
deliveryService.<Home>items()
    .languageParameter("es-ES")
    .equalsFilter("system.type", "home")
    .equalsFilter("elements.url_pattern", "inicio")
    .getObservable()
    .subscribe(new Observer<DeliveryItemListingResponse<Home>>() {
        @Override
        public void onSubscribe(Disposable d) {
        }

        @Override
        public void onNext(DeliveryItemResponse<Home> response) {
            // Gets the content items
            Home item = response.getItem();
        }

        @Override
        public void onError(Throwable e) {
        }

        @Override
        public void onComplete() {
        }
    });
<?php

// Defined by Composer to include required libraries
require __DIR__ . '/vendor/autoload.php';

use KenticoCloud\Delivery\DeliveryClient;
use KenticoCloud\Delivery\QueryParams;

$client = new DeliveryClient('975bf280-fd91-488c-994c-2f04416e5ee3');

$items = $client->getItems((new QueryParams())
            ->language('es-ES')
            ->equals('system.type', 'home')
            ->equals('elements.url_pattern', 'inicio'));
import KenticoCloud
 
let client = DeliveryClient.init(projectId:"975bf280-fd91-488c-994c-2f04416e5ee3")
 
let customQuery = "items?language=es-ES&system.type=home&elements.url_pattern=inicio"

// More about strongly-typed models https://github.com/Kentico/cloud-sdk-swift#using-strongly-typed-models
client.getItems(modelType: Home.self, customQuery: customQuery) { (isSuccess, itemsResponse, error) in
      if isSuccess {
           if let items = itemsResponse?.items {
               // Use your items here
           }
       } else {
           if let error = error {
               print(error)
           }
       }

If the specified query does match any content items, the Delivery API returns an empty listing response.

What's next?

In this article, you learned how you can handle URLs in multilingual apps and two ways of retrieving translated content. If you know the content item's codename, you can request it directly and specify a language. When you need to request an item by its localized URL slug, use filters to get the item you need.