Skip to Content
Localization

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 minimumAgeByCountry config 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.ts supports an alternates.languages field specifically for this (hreflang tags), which our documents.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.

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:

  1. Manually create a new, empty Site per market in the dashboard (the only creation path that exists today).
  2. 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.
  3. 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.
  4. 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.

Last updated on