P1 localization connects locale-specific documents to canonical content while leaving URL routing and translation orchestration flexible. This page describes the current design contract, frontend responsibilities, and the APIs and workflows used to detect and review localization changes.
P1 separates localization into four concerns:
The content backend is routing-agnostic. A single site can contain documents in multiple locales; developers can implement locale prefixes, subdomains, or domain-based routing in the site frontend.
A document has a locale and may reference a canonical document also referenced as source document.
Document type | locale | canonical_document_id | Meaning |
Canonical | en-US or another source locale | null | Source content for other locale variants |
Localized variant | fr-FR, es-ES, or another target locale | Canonical document ID | A translation or regional/market adaptation of canonical content |
Locale-native | fr-CA, ja-JP, or another locale | null | Content that exists only in that locale |
A localized variant also tracks the canonical version from which it was last synchronized. This allows the platform to enumerate variants that may be behind the source. Locale-native documents are not compared to a canonical document because no source relationship exists.
An illustrative document relationship looks like this:
{
"canonical": {
"path": "/products/widget",
"locale": "en-US",
"canonical_document_id": null
},
"localizedVariant": {
"path": "/products/widget",
"locale": "fr-FR",
"canonical_document_id": "<canonical-document-id>",
"canonical_synced_version": 12
}
}
The property names and identifiers above describe the model; use the released API schema for the exact request and response shape.
The site-level locale registry defines the bounded set of locales that editors and automation can use. It includes:
A registry accepts well-formed BCP 47 tags and prevents duplicate language markets that differ only by deprecated aliases. Page-level localization should reject locales that are not configured for the site.
An illustrative configuration is:
{
"sourceLocale": "en-US",
"marketLocales": ["en-GB", "fr-FR", "es-ES"],
"fallbackPolicy": "canonical"
}
Treat this as conceptual until the released site-settings API defines the final field names and fallback enum values.
Templates define how each component and prop behaves across locales.
The current proposal represents ownership as a per-prop template field:
export interface TemplateComponentProp {
name: string;
localeOwnership: "canonical" | "locale";
}
export interface TemplateComponent {
type: string;
pinned: boolean;
defaultProps: Record<string, unknown>;
props?: TemplateComponentProp[];
}
Do not treat localeOwnership as a translation-language detector. A value can be locale-owned without requiring linguistic translation, and a canonical-owned value can require review without being natural-language text.
The frontend is responsible for mapping a request to a locale and document path. Keep routing separate from content relationships so the same content model can support different deployment patterns.
For a locale-prefix pattern, a minimal route helper could look like this:
const DEFAULT_LOCALE = "en-US";
export function localizedPath(locale: string, path: string): string {
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
if (locale === DEFAULT_LOCALE) {
return normalizedPath;
}
return `/${locale.toLowerCase()}${normalizedPath}`;
}
For a subdomain pattern, keep the locale-to-host mapping in frontend configuration rather than in the content backend:
export function localizedHost(locale: string, hostname: string): string {
const normalizedLocale = locale.toLowerCase();
if (locale === DEFAULT_LOCALE) {
return hostname;
}
return `${normalizedLocale}.${hostname}`;
}
Your route resolver should also define what happens when a localized document does not exist. The site locale registry stores the fallback policy, but the edge or frontend layer enforces the visitor-facing behavior.
The supported editorial workflow is:
The same operations are intended to be available through the visual editor, APIs, and MCP.
A localized variant is out of sync when its recorded canonical version is behind the current canonical version.
The localization layer classifies differences into categories that help determine the correct action:
The locale diff endpoint is:
GET /api/sites/{siteId}/branches/{branchId}/documents/{encodedPath}/locale-diff
Example request:
curl \
-H "Authorization: Bearer ${P1_TOKEN}" \
"${P1_API_BASE_URL}/api/sites/${SITE_ID}/branches/${BRANCH_ID}/documents/${ENCODED_PATH}/locale-diff"
A representative response shape is:
{
"canonical": {
"documentId": "<canonical-document-id>",
"locale": "en-US",
"version": 13,
"path": "/products/widget"
},
"translation": {
"documentId": "<translation-document-id>",
"locale": "fr-FR",
"version": 8,
"canonicalSyncedVersion": 12,
"path": "/products/widget",
"syncStatus": "prop-drift"
},
"structural": {
"added": [],
"removed": [],
"reordered": [],
"props": [
{
"path": "/content/0/props/title",
"componentType": "HeadingBlock",
"propName": "title",
"canonicalValue": "Widget for every team",
"translationValue": "Widget pour toutes les équipes",
"ownership": "canonical",
"status": "needs-translation"
}
]
}
}
Use the exact released schema for all production integrations.
Localization work should not write directly to the live translated document.
The proposed edit-session sequence uses start_edit_session, apply_document_edits, and complete_edit_session. No special agent permission or localization bypass is required; localization writers use the normal validation and branch workflow.
The current design identifies two MCP operations for locale-aware automation:
Automation should limit locale choices to the site’s configured locale registry. It should not create or translate into an arbitrary locale that the site does not publish. Verify the released MCP schema and authorization behavior before enabling customer-managed agents.