[01] Policy

Internationalization

Render Policy in French, German, Dutch, or Spanish

Policy emits around 125 strings into every compiled policy — headings, table headers, GDPR/CCPA boilerplate, formatted dates. Set a locale and those strings switch language. Your company name, processing purposes, retention text, and third-party descriptions pass through as you wrote them.

Supported locales#

LocaleTagStatus
EnglishenShips in v1.0
FrenchfrShips in v1.0
GermandeShips in v1.0
DutchnlShips in v1.0
SpanishesShips in v1.0

English is the ground truth — every other dictionary is checked against it at compile time via the Dictionary type, so a missing key fails tsc rather than silently falling back to English at runtime.

Setting a locale on the config#

Pass locale to defineConfig and every emitted string in both the privacy and cookie policies renders in that language:

// policystack.ts
import { ContractPrerequisite, defineConfig, LegalBases } from "@policystack/sdk";

export default defineConfig({
	company: {
		name: "Acme, Inc.",
		legalName: "Acme, Inc.",
		address: "123 Main St, San Francisco, CA",
		contact: { email: "privacy@acme.com" },
	},
	effectiveDate: "2026-01-01",
	jurisdictions: ["eea"],
	locale: "fr",
	data: {
		collected: {
			"Account Information": ["Name", "Email"],
		},
		context: {
			"Account Information": {
				purpose: "To authenticate users and send service notifications.",
				lawfulBasis: LegalBases.Contract,
				retention: "Until account deletion",
				provision: ContractPrerequisite("We cannot operate your account."),
			},
		},
	},
});

locale is the rendering language; it is independent of jurisdictions (which is the regulatory surface). locale is optional and defaults to "en".

Per-render override (React)#

<PrivacyPolicy /> and <CookiePolicy /> accept a locale prop that overrides config.locale at render time. The same config can drive multiple languages side-by-side:

import { PolicyStack } from "@policystack/react/provider";
import { PrivacyPolicy } from "@policystack/react/policy";
import policy from "@/policy";

export function PrivacyPolicyPage() {
	return (
		<PolicyStack config={policy}>
			<PrivacyPolicy /> {/* uses config.locale */}
			<PrivacyPolicy locale="fr" /> {/* override → French */}
		</PolicyStack>
	);
}

Useful for multilingual sites that want a language switcher, or for serving an English fallback alongside a regional translation.

Dates#

effectiveDate renders through Intl.DateTimeFormat with the locale's BCP-47 tag (en-US, fr-FR, de-DE, nl-NL, es-ES), pinned to UTC so the same input produces the same output across build servers in any timezone. The string "2026-01-01" becomes:

  • English — January 1, 2026
  • French — 1 janvier 2026
  • German — 1. Januar 2026
  • Dutch — 1 januari 2026
  • Spanish — 1 de enero de 2026

What does not translate#

A handful of strings stay English by design — they're not user-facing policy content:

  • reason: audit metadata on heading nodes (e.g. "Required by GDPR Article 13(1)(c)") — threaded into the document tree for compliance tooling, not rendered to end users.
  • Validation messages from validate.ts, validate-config.ts, and validate-cookie.ts — surface in build logs to the developer integrating Policy, not in the published document.
  • Section IDs ("introduction", "data-collected", …) — stable identifiers used by tests and framework integrations to target sections.
  • Renderer format output (markdown syntax, HTML tags, PDF bullet glyphs) — already locale-agnostic.
  • Internal Error messages thrown when configs are malformed — developer-facing.

Versions are per-locale#

locale feeds into both computePrivacyVersion and computeCookieVersion, so a French build and an English build of the same config produce distinct 8-character version hashes. This is intentional: the Consent bridge re-prompts consent when the cookie-policy version changes, so users see a fresh prompt the first time they're served a different-language policy. See the Consent docs.

Compliance caveat#

The translated GDPR/CCPA/UK-GDPR boilerplate is first-pass legal text. Have a native-speaking compliance reviewer or counsel sign off on the rendered output before relying on a non-English locale in production — the same posture as Policy's English output, where the policy is a document, not legal advice.

See also#

  • Configuration — the full defineConfig reference, including how locale interacts with effectiveDate and jurisdictions.
  • Adding a locale — internal contributor guide for adding new languages.