Internationalization and localization for P1 developers
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.
Architecture at a glance
P1 separates localization into four concerns:
- Content relationships connect a localized document to its original source document.
- Locale metadata identifies the language and regional variant of each document.
- Drift detection identifies changes in the original source document that may require translation or review.
- Frontend routing maps visitor-facing URLs to the correct locale document.
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.
Document model
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.
Site locale registry
The site-level locale registry defines the bounded set of locales that editors and automation can use. It includes:
- The source locale.
- An ordered list of market locales.
- A fallback policy for a page that has no translation in a requested market.
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.
Template locale ownership
Templates define how each component and prop behaves across locales.
- canonical means a value is inherited from the canonical document and should be considered when the canonical changes.
- locale means each locale owns its value independently. Examples include currency, regional imagery, regulatory copy, and market-specific dates.
- If a prop has no explicit ownership entry, the current proposal defaults it to canonical.
- Pinned components preserve structural parity across locales.
- Unpinned slots are locale-composable, so a locale may add, remove, or reorder optional components.
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.
Frontend routing
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.
Creating and editing locale variants
The supported editorial workflow is:
- Confirm that the target locale is configured for the site.
- Create a localized variant from the canonical page, either by copying the source content or starting empty.
- Apply translation and regional adaptations on a branch.
- Validate template conformance and locale ownership rules.
- Preview the localized page.
- Review and merge the branch.
The same operations are intended to be available through the visual editor, APIs, and MCP.
Detecting drift
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:
- in-sync: No update is required.
- needs-translation: A canonical-owned prop changed and should be translated or reviewed.
- locale-override: A locale-owned prop has its own value; the canonical change is advisory.
- structural-gap: A non-pinned component exists in one document but not the other; the locale decides whether to adapt it.
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.
Branch and review workflow
Localization work should not write directly to the live translated document.
- Detect canonical changes and identify out-of-sync variants.
- Open one branch for a localization run, rather than one branch per document.
- Fetch the locale diff for each affected document.
- Translate canonical-owned properties.
- Preserve locale-owned overrides unless the locale team intentionally changes them.
- Adapt optional structural changes for the target market.
- Write changes through the standard edit-session API.
- Run content validation and structural conformance checks.
- Provide a document-level and branch-level diff for human review.
- Merge only after approval.
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.
MCP integration
The current design identifies two MCP operations for locale-aware automation:
- list_locale_variants: Return the locale variants associated with a document and their synchronization status.
- get_locale_diff: Return the classified difference between a canonical document and a locale variant.
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.
Implementation guardrails
- Do not assume every locale is a translation. Support locale-native content explicitly.
- Do not encode locale routing rules in canonical-document relationships.
- Do not overwrite locale-owned properties during automatic synchronization.
- Do not remove pinned components from a locale variant.
- Treat non-pinned structural differences as reviewable adaptations, not automatic errors.
- Use branches and human review for translation changes.
- Use the released API schema rather than copying illustrative payloads from this page.
- Keep the language switcher mapped to equivalent localized pages when they exist.