Localization
There’s nothing built in. Any multi-market P1 site needs real localization: Herradura’s own Book a Tour page states a different legal drinking age (18, Mexico) than the US default (21), and jackdaniels.com serves genuinely different content across eight-plus locales, but localization isn’t a P1 platform feature. This page documents what P1 actually provides for this (not much) and the approach for building the rest, using our two Brown-Forman brand sites as the concrete evidence.
This is a P1 platform gap, not a Brown-Forman-specific one: any multi-market P1 site hits this. Filed here at the top level rather than under Brown-Forman for that reason, alongside Managing the Header & Footer, Form Builder, and Email Delivery, the same “nothing built in, here’s the pattern to build on” shape.
What we confirmed: no native content localization
Searched every installed @pantheon-systems/* package for a locale/i18n/translation
concept. The only real hit was og:locale in SeoMetadata: a single Open Graph
metadata field, not a content-localization mechanism. Documents, branches, versions,
and templates (see Workflows) carry no
locale field anywhere in their data model. A P1 “Site” is not locale-scoped; a
branch is not locale-scoped; nothing in the CCR schema represents “this document, in
this language.”
Puck’s own “Localization” feature is a false lead: checked directly, it’s not
this. Puck’s docs describe a dictionary prop for translating the editor’s own
UI (button labels, tooltips). Useful if a non-English-speaking editor is using the
Puck interface, irrelevant to serving translated content to site visitors. Confirmed
directly from Puck’s documentation, not assumed from the feature name.
How jackdaniels.com actually does it (reverse-engineered, not documented anywhere)
Since P1 provides nothing, we checked what a real Brown-Forman brand with genuine
multi-locale content actually does. jackdaniels.com serves at least eight locales
(/en-us, /de-de, /pl-pl, /en-au, /en-gb, /ja-jp, /fr-fr, /pt-br), each
with real, distinct translated content, confirmed by fetching /de-de directly and
finding genuine German copy (“Unsere Whiskeys,” “Dürfen wir vorstellen,” “ZUM REZEPT”),
not just a cosmetic lang attribute change.
The /en-us and /de-de builds share the same Next.js buildId. Confirmed by
comparing the build identifier embedded in both pages’ HTML: identical. This rules
out separate deployments or separate P1 sites per locale; it’s one Next.js app,
one deployment, serving multiple locales.
Given P1’s document model has no locale concept, the only mechanism left that explains
this is the simplest one: locale is just a path prefix, and each locale’s pages are
separate documents at separate paths within the same site: /en-us/whiskey/old-no-7
and /de-de/whiskey/old-no-7 are two unrelated documents as far as the CCR is
concerned, the same way /whiskey/old-no-7 and /recipes/horseshoe-margarita are
unrelated documents. Nothing platform-specific makes this work; it’s the same “documents
live at whatever path you give them” primitive behind everything else in this section
(see Managing the Header & Footer and
templates-as-code for the
same pattern applied elsewhere).
We also checked our own scaffold’s middleware.ts for any locale-detection or
redirect logic: there is none. createP1Middleware handles only P1’s own
auth/session concerns, with one siteId. Locale routing, if any, would need to be
built entirely separately.
Approach: path-prefix locales, not branches
There’s a second option worth naming and rejecting deliberately: since branches are
real, forkable content sets (see
Branches),
one branch per locale is technically possible. We recommend against it: branches
exist for a draft-then-merge workflow, and you would never want to “merge” German
content into the English branch the way you’d merge a workstream’s edits into main.
Repurposing a review/merge primitive as a permanent parallel-content mechanism fights
the tool it’s built on. Path-prefixing, matching what Jack Daniel’s already does in
production, is the pattern to build on.
/en-us/whiskey/old-no-7 (English, US legal age: 21)
/de-de/whiskey-old-no-7 (German: separate document, separate content)Practical implications:
- Every page effectively gets built once per locale, as a genuinely separate document. There’s no “translate this field” workflow at the platform level. Editors (or a translation pipeline) author each locale’s content independently.
- Templates (see Products and Cocktails) need to exist per locale too, or a single template’s pages need a locale-agnostic structure with locale only affecting the content within it: worth deciding explicitly per brand rather than assuming.
- The Age Gate’s
minimumAgeByCountryconfig already anticipates this: country/locale-driven behavior is already part of that design, not something new to retrofit. - Sitemap, Robots & Redirects’s sitemap plan
needs locale awareness: Next.js’s
sitemap.tssupports analternates.languagesfield specifically for this (hreflangtags), which ourdocuments.list()-driven sitemap builder should populate once a locale convention is settled.
How we’d actually build this: three real options
The question worth answering precisely: is a new market a few translated pages within the existing site, an entirely separate site cloned from the base one (the pattern we’ve used on past WordPress engagements: clone the base multisite install, stand up a subsite, hand it to the market team to run independently), or something new we’d need to build to get that same clone-and-hand-off workflow on P1? All three are real options; they trade off differently, and P1 supports them very unevenly.
Option A: page-by-page, path-prefixed, one Site (recommended default)
This is the pattern documented above: /en-us/whiskey/old-no-7 and
/de-de/whiskey/old-no-7 as separate documents on the same Site, matching what
jackdaniels.com already does in production. One deployment, one dashboard, one
Salesforce/analytics config, one team of developers maintaining one codebase.
Best fit: a market that needs translated content but not operational
independence: the scenario Herradura’s own Age Gate
already anticipates (minimumAgeByCountry), which is country-aware behavior, a
narrower bar than a fully separate site.
Real limits: no “duplicate this page into German and start translating” shortcut : every locale’s pages are authored (by hand or by a scripted import) from scratch. No side-by-side translation UI; P1’s editor has no concept of “this document’s sibling in another locale.”
Option B: one P1 Site per market, the literal WordPress Multisite analog
This is the closer match to what “clone the base site, stand up a subsite, let the market team run it independently” actually meant on past WordPress engagements. It does not map onto P1 cleanly, and the gap is worth being precise about:
Confirmed: P1 has no site-cloning or multi-site capability at all.
SitesEndpoint (the actual API surface for Sites) exposes only get() and
list(). No create(), no duplicate(), no site-template mechanism. Pantheon’s
own “Getting Started” dev guide confirms site creation is a manual, one-at-a-time
dashboard action (“Create your site in P1 dashboard”). Neither that guide nor
the P1 Architecture overview mentions multi-site, cloning, or localization
anywhere. This isn’t a documentation gap on our end; we checked the platform’s own
primary sources directly.
The deeper reason it doesn’t map: WordPress Multisite is one codebase and one
database serving many independently-managed subsites, with Network Admin tooling
built for exactly that. A P1 Site pairs 1:1 with one Next.js codebase and one CCR
content backend (confirmed from the Site type’s pantheonSiteId field, which links
to a single traditional Pantheon-hosted deployment). It’s architecturally closer to
one normal WordPress install than to a multisite network node. There’s no shared
codebase serving many sites, and no network-admin layer above individual Sites.
If a market genuinely needs that level of independence (its own team, own editorial calendar, potentially its own domain and Salesforce segment, the same bar that decides when a market gets its own WP Multisite subsite today), building it on P1 would mean:
- Manually create a new, empty Site per market in the dashboard (the only creation path that exists today).
- Script a content-copy tool ourselves, since nothing in the platform does this:
list every document on the source Site/branch (
documents.list()) and replay it onto the new Site (documents.create()/versions.create()). This gets you content parity, not a true clone. - Rebuild every Template’s layout by hand in the new Site’s Puck editor. This is a hard wall, not a scripting inconvenience: Template layout has no write API at all (confirmed in Templates) : a scripted clone can carry over content, but never a template’s pinned component structure.
- Deploy and maintain a separate Next.js codebase per market Site: N Vercel/ Pantheon deployments, N sets of secrets, and no built-in way to propagate a shared component/design-system update across them the way one WP Multisite codebase update reaches every subsite at once.
Real limits: every one of the four steps above is us building infrastructure P1 doesn’t provide, not configuring a feature. Ongoing maintenance cost scales linearly with market count in a way WP Multisite’s shared-codebase model doesn’t.
Option C: request (or build) a “clone this site” feature
Since neither the SDK nor the dashboard exposes site duplication today, there are two honest paths, not one:
- Ask Pantheon directly. True site-cloning has implications our SDK access can’t reach (dashboard UX, billing/quota per site, DNS/hosting orchestration), making Pantheon the natural owner of a real “New site from existing site” feature. Worth raising explicitly, since our research (SDK, official dev guide, architecture page) found zero indication it exists or is planned.
- Build the scoped version ourselves in the meantime: exactly the content-copy script described in Option B, step 2. It’s buildable today on existing API primitives, but it will always stop at the Template-layout wall in step 3; that’s a platform limitation no script on our side can route around.
Recommendation
Default to Option A for any market that needs translated content but not operational independence, which, on current evidence, is every Brown-Forman market we’ve looked at so far (the real, confirmed need is country-aware behavior like the age gate, not full site autonomy). Reserve Option B for a market that clears the same bar that earns a WordPress Multisite subsite today, and if that happens, budget for building the Option C content-copy tool ourselves, while getting an explicit answer from Pantheon on whether real site-cloning is on their roadmap, since building around a permanent platform gap and building around a temporary one are very different bets.
Open questions for this page now live on Outstanding Questions, tracked centrally across all pages rather than repeated per page.