Setting up Contentful Content Localization
Contentful supports multiple locales at platform level: each Entry field stores separate value for each language. Locales are configured in Space Settings, not in Content Model — this is important architectural difference from plugin-based localization in WordPress.
Configuring Locales in Space
In Contentful Web App: Settings → Locales → Add locale. Default locale determines fallback: if field is not translated — value of default locale is shown.
When configuring programmatically via CMA:
import { createClient } from 'contentful-management';
const env = await cmaClient.getSpace(spaceId).then(s => s.getEnvironment('master'));
// Add locale
await env.createLocale({
name: 'Ukrainian',
code: 'uk',
fallbackCode: 'en-US', // fallback to English
optional: true, // field not required to translate
});
Data Structure for Localized Fields
In API response, each field returns as object with locale codes as keys:
{
"fields": {
"title": {
"en-US": "How to build a CMS",
"ru": "Как создать CMS",
"uk": "Як створити CMS"
},
"heroImage": {
"en-US": { "sys": { "type": "Link", "id": "abc123" } }
}
}
}
Fields with localization disabled in Content Type return single value without locale key.
Requesting with Specific Locale
// Request content in specific language
const entries = await client.getEntries<TypeBlogPostSkeleton>({
content_type: 'blogPost',
locale: 'uk', // SDK automatically selects uk-values
});
// entries.items[0].fields.title — already string, not object
Next.js i18n + Contentful
// next.config.ts
i18n: {
locales: ['en', 'ru', 'uk'],
defaultLocale: 'en',
}
// app/[locale]/blog/page.tsx
export default async function BlogPage({ params }) {
const contentfulLocale = localeMap[params.locale]; // 'en' -> 'en-US'
const posts = await client.getEntries({
content_type: 'blogPost',
locale: contentfulLocale,
order: ['-fields.publishedAt'],
});
return <BlogList posts={posts.items} />;
}
Fallback behavior: if uk locale is not filled, Contentful returns en-US value automatically when fallbackCode is configured. On frontend this is transparent — no need to handle missing translations.
Marking Fields as Not Requiring Translation
In Content Type Editor each field has Enable localization for this field checkbox. Fields like slug, publishedAt, numeric IDs — usually not localized. This reduces translator workload and decreases API response size.
Typical timeline for setting up localization on existing project with 3–5 locales and 5–10 Content Types — 1–2 days, including frontend logic update and routing configuration.







