Help us improve
Share bugs, ideas, or general feedback.
From contentful
Design and operate Contentful localization — choose the right locales and fallback chains, mark fields as localizable correctly, integrate translation workflow apps, query locale-aware via REST and GraphQL, route locales in Next.js, and avoid the cache-fragmentation cliffs that come with multi-locale sites. Use this skill any time multi-locale enters a project; when an existing locale strategy is brittle; when adding a new locale; when "the French site shows English text"; or when the team is debating field-level localization vs. duplicate spaces. Trigger on any localization decision.
npx claudepluginhub bpainter/composable-dxp-claude-marketplace --plugin contentfulHow this skill is triggered — by the user, by Claude, or both
Slash command
/contentful:contentful-localizationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Localization decisions made in week one constrain every model and every page for the life of the platform. Contentful makes the right thing easy if you set up locales and fallback chains correctly; brittle if you don't.
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
Localization decisions made in week one constrain every model and every page for the life of the platform. Contentful makes the right thing easy if you set up locales and fallback chains correctly; brittle if you don't.
This skill owns locale strategy and operations. Pair with contentful-content-model (which fields should be localizable), contentful-graphql (locale-aware queries), contentful-react-wrapper (locale routing in Next.js), contentful-migrations (adding/removing locales is a migration), and contentful-delivery-optimization (locale × cache).
en-US (or whatever is canonical); translation flows from there./2026/04/composable-architecture-2026 is locale-prefixed, not locale-translated). Decide deliberately.Start with the actual content commitments, not aspirations. Common patterns:
en-US). Default. Most US-only or English-language-only Slalom builds.en-US, es-MX). Common in retail and CPG with US Hispanic markets.en-US, en-GB, en-CA, en-AU). When tone, currency, and legal differ but vocabulary is mostly shared. Consider variant-style fields rather than full locales.en-GB, fr-FR, de-DE, es-ES, it-IT). A real localization program with translation vendors.en-US, fr-FR, de-DE, es-ES, pt-BR, ja-JP, zh-CN, ko-KR). Enterprise scale; usually a Translation Management System (TMS) drives the workflow.Don't add a locale you can't keep current. A perpetually-out-of-date locale is worse than no locale.
Use ICU-style locale codes: en-US, fr-FR, de-DE, pt-BR, zh-CN. Two parts (language + region). Contentful accepts both BCP 47 forms; pick one and stick with it.
Every space has one. Once set, changing it is painful (every entry has values keyed by the default). Pick deliberately:
en-US.en-GB.pt-BR for Brazil-led, es-MX for North-of-Mexico LatAm, etc.).Each locale has a fallback. The chain resolves missing field values:
fr-FR → fr (generic) → en-US (default)
es-MX → es-ES → en-US
ja-JP → en-US
Configure deliberately:
Every field has a "localizable" boolean.
| Localize | Don't localize |
|---|---|
| Body copy, headlines, descriptions | References (the linked entry has its own locale dimension) |
| SEO title, meta description | Slugs in some patterns (see "URL strategy" below) |
| CTA labels | Boolean flags, dates, IDs |
| Asset alt text | Asset references themselves (the asset has its own locale-resolved URL via the Images API) |
| Localized media (a hero image with translated text baked in) | Most images |
Set localizable when a field has a per-locale meaning. Otherwise leave it global; the default-locale value is shared.
Mark Media (asset reference) fields as localizable when:
Otherwise leave global; the asset is shared.
Cross-entry references can be marked localizable, but think hard. A localized relatedArticles field means each locale specifies its own related articles. Sometimes correct (regional editorial curation); often unnecessary (the related articles inherit, each comes back in its own locale on read).
Slalom default: leave references global. Localize only when there's a specific editorial reason.
Three integration patterns, in order of cost:
Editors translate by hand in the Contentful UI. The locale-switcher in the entry editor lets them flip and edit per locale.
Pros: zero setup. Cons: manual; no progress tracking; quality varies.
Use for: small projects with a single bilingual editor.
Apps like Smartling, Phrase, Lokalise, GlobalLink Connect integrate as App Framework apps. They:
Pros: real translation workflow with vendor-quality translators. Cons: per-character cost; setup overhead; vendor lock-in.
Use for: most multi-locale Slalom builds. Pick the TMS based on the client's existing relationships.
Build via the App Framework if no marketplace app fits. Rare. See contentful-app-framework.
// migrations/2026-05-09_add_fr_fr_locale.cjs
module.exports = function (migration) {
migration.createLocale("fr-FR")
.name("French (France)")
.fallbackCode("en-US")
.optional(true);
};
Then:
contentful-migrations).contentful-space-architect.Removing a locale is much harder — Contentful supports it, but you lose all the values keyed to that locale. Soft-deprecate first (keep the locale, mark it not-publicly-routed) before hard-removing.
GET /spaces/{id}/environments/{env}/entries?content_type=article&locale=fr-FR
Pass locale=* to get all locales in one response (CMA always returns all; CDA needs the wildcard).
query Article($slug: String!, $locale: String!, $preview: Boolean) {
articleCollection(
where: { slug: $slug }
locale: $locale
preview: $preview
limit: 1
) {
items { title body { json } }
}
}
Locale is per query, applied at the field level inside Contentful's resolver.
Two main patterns:
URLs are /{locale}/...:
/en-US/products/composable
/fr-FR/products/composable
app/
[locale]/
layout.tsx
page.tsx
products/
[slug]/
page.tsx
Pros: clear, SEO-friendly, easy to share. Cons: middleware redirects on root visit.
// middleware.ts
export function middleware(req: NextRequest) {
const { pathname } = req.nextUrl;
if (pathname === "/" || !pathname.match(/^\/[a-z]{2}(-[A-Z]{2})?\//)) {
const locale = pickLocaleFromHeaders(req.headers);
return NextResponse.redirect(new URL(`/${locale}${pathname}`, req.url));
}
}
fr.example.com, de.example.com. Less common; harder ops; better for distinct brand identities per region.
For most Slalom builds, path-prefixed.
Three options:
/en-US/composable-architecture and /fr-FR/architecture-composable. Best UX in-locale. Hardest to operate — every slug is a content decision per locale./en-US/composable-architecture and /fr-FR/composable-architecture. Easier ops. SEO discounts the localized search potential.Hybrid is the Slalom default. Make the model decision per content type:
Product → shared slug. Easier inventory matching.Article → translated slug for top-content pages, shared otherwise.LandingPage → translated slug always (campaign-specific).Locale fragments cache space. For a 5-locale site, naive caching is 5× the cache footprint and 1/5 the hit ratio. Mitigations:
revalidateTag("article:abc:fr-FR") instead of article:abc. Pair with the wrapper / webhook layers to set/invalidate locale-specific tags.Locale × variant (personalization) × preview can multiply. Be deliberate.
Webhook payloads identify the modified entry but don't carry locale context — a publish event affects all locales of that entry. The handler decides whether to invalidate a specific locale or all.
Slalom default: invalidate aggregate (article:abc) and trust per-locale fetch tags to scope. If you find aggregate invalidation is too coarse, build a per-locale invalidation by reading which locales have field-level changes (CMA exposes this on the entry version diff).
locale=* on the front end fetch path. Pulls every locale's value for every read. Use specific locale in the query.# Locale Plan: [Project]
## Locales
| Code | Name | Default | Fallback chain | Optional? |
|---|---|---|---|---|
## Per-content-type localization
| Content type | Localizable fields | Non-localizable fields | Notes |
|---|---|---|---|
## URL strategy
- Routing: path-prefixed | domain | subdomain
- Slug strategy: shared | translated | hybrid (per content type)
## Translation workflow
- App: {Smartling | Phrase | Lokalise | manual}
- Workflow: source → target → review → publish
- Vendor: {if external}
## Cache strategy
- Tagging: per-locale | aggregate
- Invalidation: how locale changes propagate
## Migration plan
- Initial seed: how existing content gets translated
- Add-locale process: per-environment promotion
## Risks
- Out-of-date translations, slug ambiguity, cache fragmentation
contentful-content-model.contentful-graphql.contentful-delivery-optimization.contentful-migrations.contentful-react-wrapper and software-engineering-nextjs-scaffold.contentful-app-framework and references/marketplace-apps.md.contentful-personalization.contentful-webhooks.../../references/contentful-foundations.md