Cloud infrastructure
Locale & Localization
Several locales in one site, with a primary locale, machine translation that costs no credits, locale-aware delivery and SEO, and the same contract for hosted and headless front ends.
A site has one primary locale and can add more. Every text field keeps its primary-locale value where it is; translations live in a table beside it, one row per record and locale. Adding a locale translates the whole site in the background and publishes it when done. The hosted site and any front end you build read the same data through the same locale parameter.
Core objects
- Locale set. The site's locales with a primary locale, a status (
draft,translating,published,disabled), a URL prefix for each secondary locale (en,ja, or a prefix the owner chose) and an auto-translate switch.GET /api/v1/sitelists them inlocales[]. - Translations. One row per record and locale, with draft and published field data, its own URL slug (one segment) and a
sourceHashof the source record at the time it was translated. The hash is how a stale translation is detected and how an external tool avoids overwriting newer source text. - Translatable fields. A registry derived from each content type's schema says which paths can be translated: titles, rich text, section text, SEO fields, product descriptions, long-form details, FAQs and purchase notes, product options and variant descriptions, course units including text lessons and quiz questions (never the answer key), store-wide processing time and purchase notes, forms and legal pages. Collection fields opt in with
localized: true. Prices, stock, orders, customers and reviews are never translated. - URL slugs. Each locale can have its own slug for pages, posts, categories, tags, authors, products, product categories and collection items. The first machine translation into a locale generates it from the translated title (Latin-script languages get an ASCII slug; other scripts keep the primary slug when it is ASCII), and it never changes on its own after that. Pages translate only their own segment, so a page's path in a locale is built from each level's slug in that locale. Fixed permalink segments such as
/blogand/productare not translated. - Translation jobs. A full-site job runs when a locale is added or a missed batch is retried; later edits to the primary locale are retranslated incrementally. Jobs and single-record translation cost no AI credits; a daily call cap queues work to the next day instead of failing.
How settings relate
Locale, currency, business market and time zone are four independent settings. None is derived from another, so a site can write in English, sell in euros and run on Tokyo time. A customer's locale (Contact.locale) is recorded at checkout, login and form submission and drives the locale of their emails, customer account and certificates.
How the APIs fit
- Read with the Content Delivery API. Every read accepts
locale(the code or the URL prefix). Untranslated fields fall back to the primary text, and the response says what it served inresolvedLocaleand which locales have a translation inavailableLocales. A read by slug accepts any locale's slug and returnsslugin the requested locale; pages and products addlocalePaths(their path in every live locale) and collection items addlocaleSlugs. A locale the site does not have returns400 UNSUPPORTED_LOCALE./seo/*,/feedand search follow the same parameter. See Read content. - Manage with the Management API.
localization.locales.*adds, updates, reorders, removes and sets the primary locale;localization.translations.*lists, reads, writes (upsertwithsourceHash, andslugto set or clear a locale's URL slug), publishes and deletes translations and runs one on demand withtranslate;localization.jobs.*reads progress and retries. Adding or removing a locale changes every URL of that locale and asks forconfirm: true. The locale code is the natural key: adding a locale that is already in the set returnsALREADY_ADDEDand removing one that is gone returnsNOT_FOUND, so a retried call never creates a second locale and no idempotency key is needed. - Listen for webhooks.
content.translation.publishedanddeletedfire per record and locale;localization.locale.published,unpublishedandupdatedwhen the locale set changes;localization.job.completedandfailedfor full-site jobs. ThecacheTagson every content event carry@<locale>, so a translation going live refreshes only that locale. See Webhooks. - Route and tag in your front end.
createKanorioMiddleware({ features: { locales: true } })redirects the primary locale's prefix to the root, serves/<prefix>/llms.txtand/<prefix>/rss.xml, matches shared redirects without the prefix and locale redirects (localeset) against the full path.requestLocaleOf,buildAlternatesandlanguageSwitcher(passlocalePaths) give you the locale, canonical, hreflang and a language menu with the same rules as the hosted site. See Next.js and the SDK reference. - Customer-facing text. The Customer API and
@kanorio/elementsacceptlocale; without it they use the customer's recorded locale. Course and unit titles, order line names and completion certificates follow it. See Courses and members. - Automated emails. Order, payment, shipping, refund, sign-in and form confirmation emails go out in the customer's recorded locale when the site serves it, otherwise in the primary locale. An email the owner never customized uses the built-in wording, which exists for every site language. A customized email is translated like other content: its translation is the
message_templaterecord type (the customized template's id), corrected per locale withlocalization.translations.*and live on write. Until a locale has a translation of a customized email, that email goes out in the primary locale. Template variables (the triple-brace placeholders such as the order number) must stay exactly as in the source; a translation that adds a variable is rejected. - Export. The site export includes
localization/locales.jsonandlocalization/translations.jsonnext to the primary-locale content.
Behaviour to rely on
- The primary locale is served at the root; every other locale under its prefix. The primary locale's own code as a prefix redirects to the root with
301. - A locale that is still translating does not exist for visitors or the Delivery API. When the job finishes, the locale is published in one step.
- Removing a locale that was live redirects its prefix to the primary locale's pages. Its translations are kept for at least 30 days; adding the locale back reuses them and translates only what is missing. Give it the same prefix to bring the old URLs back.
- A page served in fallback (the locale has no translation of it yet) gets the primary locale's canonical and no hreflang, so search engines never index duplicate text.
- Records with a draft and publish lifecycle (pages, global sections, site settings, posts and collection items) keep edited translations as drafts. Publishing the record puts every locale's draft live;
localization.translations.publishputs one locale live on its own and needs the record's publish permission (site:publish,blog:publishorcollections:publish). Editing a translation never marks the primary locale as changed. - A URL that uses another locale's slug, or the primary slug where the locale has its own, redirects to the locale's slug. Changing a live slug adds a 301 from the old URL in that locale; clearing it makes the locale follow the primary slug again.
- The primary locale can be changed while the site has one locale. Changing it on a multilingual site rewrites every URL and is not offered yet.
- The same rules apply to the hosted site and to a headless front end;
npx @kanorio/cli doctorchecks that your front end serves each locale under its prefix with the translated title, lists hreflang for every live locale and serves the per-localellms.txt.
What the APIs expose
- Locale set & translations (add a locale, full-site machine translation, review)Available
- API
- Management API
localization.locales.*(list,add,update,reorder,remove,set_primary),localization.translations.*(list,get,upsertwithsourceHash,publish,delete,translate),localization.jobs.*; REST/sites/:id/locales,/sites/:id/translations - Hosted site
/settingslocale list (status, progress, URL prefix, auto-translate) and the editor's locale switcher for corrections- Webhooks
content.translation.published/deleted,localization.locale.published/unpublished/updated,localization.job.completed/failed
- Locale-aware delivery, SEO files and customer-facing textAvailable
- API
- Delivery
?locale=(code or URL prefix) on/site,/pages,/globals,/collections,/catalog,/forms,/search,/seo/*and/feedwithresolvedLocale/availableLocales/localePaths(collection items:localeSlugs), any locale's slug resolves to the record;/redirectsrules carrylocale; Customer APIlocaleon deliverables, orders and unit open; SDKcreateKanorioMiddleware({ features: { locales } }),buildAlternates,requestLocaleOf,languageSwitcher, locale-scopedrevalidate; CLIdoctori18n rules - Elements
localeon<KanorioProvider>(forms, login, curriculum and unit load the visitor's locale)- Hosted site
- Primary locale at the root, other locales under their prefix (
/en/…), header language menu, per-localellms.txt/rss.xml, one sitemap withxhtml:link, hreflang andx-default - Webhooks
cacheTagson every content event carry@<locale>so a translation going live refreshes only that locale
API reference
Webhook events
- content.translation.published · Fired when one record's translation in one locale goes live on its own (editor correction, Management API write, incremental or on-demand machine translation). cacheTags carry only that locale's tags. Not fired per record during a full-site translation job; see localization.job.completed.
- content.translation.deleted · Fired when a live translation of one record in one locale is deleted; visitors see the primary-locale text until it is translated again.
- localization.locale.published · Fired when a locale goes live for visitors and the Content Delivery API (a full-site translation job finished, or the owner published it). cacheTags is site:<id>: rebuild the language switcher and hreflang on every page.
- localization.locale.unpublished · Fired when a live locale is taken offline or removed (removed: true); its URL prefix now 301-redirects to the primary locale.
- localization.locale.updated · Fired when a live locale's URL prefix (change: path_prefix, previous holds the old prefix), its position in the language switcher (order) or the site's primary locale (primary) changes.
- localization.job.completed · Fired when a full-site translation job finishes. cacheTags is locale:<code>: every response of that locale may have changed. A locale going live for the first time also fires localization.locale.published.
- localization.job.failed · Fired when a full-site translation job fails (the model provider stayed unavailable or retries ran out; the daily limit only pauses a job). Retry it with localization.jobs.retry.
Recent changes
- 2026-11-04 Localization: automated emails follow the customer's language
- 2026-11-04 Localization: publishing one locale needs the record's publish permission
- 2026-11-04 Localization: more translatable fields (product details, variants, lessons, quizzes, store notes)
- 2026-11-04 Localization: each locale can have its own URL slug
- 2026-11-03 Localization is generally available; site export includes locales and translations
For site owners
How to use this product in the dashboard is covered in the help center.
Open the help center