Skip to content
PolicyStack
V1
Esc
navigateopen⌘Jpreview
On this page

Internationalization

Render Policy in French, German, Dutch, or Spanish

PolicyStack V1 — current documentation. Supported capabilities and limitations.

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

Locale Tag Status
English en Ships in v1.0
French fr Ships in v1.0
German de Ships in v1.0
Dutch nl Ships in v1.0
Spanish es Ships 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.

Last updated on September 6, 2026