For discussion

Localisation Service & API Gateway

Draft for review · PADI-536

One place decides a visitor's region and language, and every PADI site reads that answer instead of working it out for itself. Here is what we propose, what we need you to decide, and what we need to know.

Launch November 2026 Source of truth Country sheet ↗ Content regions 5 Selector regions 15 Detailed design Edge doc ↗
01

What we are proposing

Every PADI site currently decides on its own what language and region to show. This replaces that with one answer, worked out once.

Today the store sets currency its own way, Chinese, Japanese, Korean and Taiwanese visitors get sent to separate sites, and padi.com has its own selector. Those answers drift further apart over time because nothing holds them together.

Instead, a small piece of code runs at Cloudflare — which already sits in front of most PADI sites — and works out two things from the visitor's country: which region's content they should see, and which language to show it in. It attaches that answer to the request, and each site simply reads it.

CLOUDFLARE EDGE Runs inside the existing TLS handshake. No added network hop. ORIGIN Visitor in Germany Classify what kind of request decide() cf.country · path Accept-Language session · config BLOCK 5 sanctioned countries SERVE edge cache lookup REDIRECT 302, roots only PADI property renders, never re-decides Content API serves that region’s content GET / bots, assets, APIs, POST — passed through untouched on miss x-padi-* region + language Accept-Language 302 to the localised URL · Cache-Control: private, no-store
One request, one decision. decide() is pure — it reads signals and config and returns an answer; it calls nothing. Note what does not happen: classified traffic skips the decision entirely, a served page is answered from the edge cache without reaching the origin, and a redirect never enters the cache. The origin only ever receives an answer, never a question.

The request, step by step

  1. Classify it first. A search engine, an image, an API call or a form submission passes straight through untouched. Getting this wrong is how you redirect Google or break a form.
  2. Read the signals. Any country and language already in the address — those win outright — then the visitor's country, their browser's language, and their session.
  3. Decide. One self-contained function with its data built in, so there is nothing to look up and nothing that can be unavailable. Data changes ship like code, and roll back the same way.
  4. Act on the answer: serve, redirect or block — and redirect only from the homepage and the country domains.
  5. Check the cache, filed by address and region. If the address ends up naming the country, the region drops out of the key and the address alone is enough. This is where the speed comes from: pages answered at Cloudflare without touching the origin at all.
  6. If it is not cached, pass it back to the site — discarding anything the visitor tried to set themselves, and attaching our own answer instead.
  7. The property builds the page using that answer, and asks its content API for that region in that language. Neither of them works it out again.
  8. On the way back, add the answer where JavaScript-based sites can read it, add the tags search engines need, and store the page.
  9. Anything specific to one visitor happens after that — the "you're viewing Japan" bar, signed-in details — so the page itself stays cacheable.

Three things this assumes

PrincipleWhat it means in practice
Detection is never savedA region we work out from someone's location is never saved. We work it out again on every request. What we do remember is a region someone actually chose — a member's account setting, or a pick from the menu. If the address ends up naming the country, a chosen region becomes part of the URL rather than something we store — which would remove even that.
Country-level, not just EMEA / Americas / APWe resolve the visitor's actual country, even though only five content sets sit behind them.
Language is a choice that winsA region suggests a language; the visitor's own choice beats it and travels with them.

What happens for a signed-in member

Their request arrives like anyone else's, with their session alongside it.

What happens
At the homepageTheir stored language wins, above their browser and their country.
At any other addressThe address still decides. padi.com/fr/fr/courses is France in French for everyone.
Which regionTheir stored region beats where they are, read once at sign-in and carried the same way a manual pick is.
The page itselfThe same page everyone gets at that address. Personalised parts are layered on in the browser, on top of it.

When the address and the visitor disagree

Someone in France opens a link to padi.com/jp/ja/about. They land on that page and see it — Japan's content, in Japanese, because the address decides. A small bar appears at the top: "You're viewing Japan. Switch to France?" It names their country, not the region behind it — that is our plumbing, not theirs. One click takes them to padi.com/fr/fr/about — the same page for their own country; ignore it and they carry on reading. Dismiss it and we do not ask again that visit. Anything else, they use the region selector. The same bar covers a signed-in member whose account region differs from the address.

02

The one decision we need first

This cannot be changed quietly once the site is live, because it is what search engines index and what people share. It is still open, and it needs product, SEO and engineering to land in one place.

What should a page's web address look like? The vanity domain redirecting to the SEO URL is agreed — padi.ar → padi.com/ar. What that second address looks like is not. Three shapes are in play.

Option A

Country only

padi.com/ar/…

  • Shortest, and what column D of the sheet shows today.
  • But padi.com/ar is ambiguous: ar is Argentina, and also the language code for Arabic.
  • The language then has to be remembered in the visitor's browser.

Option B

Country, then language

padi.com/ar/es/…

  • The shape column F of the sheet shows — /ca/fr, /de/de, /jp/ja.
  • No ambiguity: country and language sit in different positions.
  • Two segments before the page path, which is unusual for search engines.

Option C

Language and country together

padi.com/es-ar/…

  • The common internationalisation standard, and what our SEO team leans toward.
  • One segment, unambiguous, and already how browsers name a locale.
  • Would mean the sheet's column F examples are rewritten.
Where we are

Every shape except Option A removes the padi.com/ar ambiguity, which is the part we would not want to discover after launch. Between B and C we have no strong preference — B matches what the sheet already says, C is the wider convention.

Worked examples elsewhere in this document use Option B so they read consistently. That is for legibility, not a decision.

03

Open questions

Eight decisions about countries, languages, regions and URLs. Most are product calls; Q7 also wants the SEO team and Q8 is about who owns the data. Each one blocks a specific line of what we build. Answers go in the Localisation Q&A tab of the country sheet, keyed to the IDs.

Q1

When a country’s default language is not one PADI supports, what language should we serve?

This affects 50 of the 231 countries. Armenia is served UK content with Armenian as its default; Bulgaria is UK content with Bulgarian; Bhutan is Australia content with Dzongkha. None of those three exist in the CMS.

Q2

What is the expected behaviour for visitors from a blocked or restricted country?

Five countries are marked Blocked and carry no content region: Cuba, Iran, North Korea, Sudan and Syria. We need to know what those visitors should be shown — a message, a redirect, or something else.

Ukraine is a different case, marked “Restrictions for Crimea” — we need the expected behaviour for visitors from Crimea, and whether the rest of Ukraine is unaffected.

Q3

Hong Kong and Taiwan have no RHQ and no content region — should both be China, as Macao is?

They are the only two non-blocked countries missing both. Macao, grouped with them historically, is RHQ China and serves China content.

Q4

What is the expected behaviour for visitors from the 73 countries where “Supported today” is blank?

73 of the 231 rows have this column empty — Bhutan, for example.

Q5

Can you confirm Canada is no longer its own content region?

In the 10 September sheet Canada’s content region is United States, and no country lists Canada — which takes the model from six content regions to five: UK, United States, Australia, China and Japan. We read that as intentional and would just like it confirmed.

Q6

When someone picks a region from the menu and we have no stored preference or device signal, what language should they get?

The sheet has a default language per country and nothing per region. If the answer is the region’s own default, we need one value per content region and none exist today. Keeping the language they were already reading needs nothing added, which is what we would propose.

Q7

Which URL structure should we build to?

Three shapes are in play — padi.com/ar, padi.com/ar/es and padi.com/es-ar, set out in section 02. The sheet itself is not consistent: column D shows the first, column F the second, and the UK row shows padi.com/gb with no language at all. We also need to know whether padi.com/{country} is ever served on its own, or always redirects on.

This is the one that blocks the most: redirects, caching and every address search engines index all follow from it, and it is the hardest to change afterwards.

Q8

Can the country mapping sheet move into the reference API?

api.padi.com/r/references/countries/ already publishes 226 countries, but only names and ISO codes — none of the fields this service needs. If those fields were added to the endpoint, we would check it weekly and ship any change the way we ship code. If that is not realistic before November we will work from the sheet — but then we need to know who maintains it and how we hear about a change.

04

How we would build it

In stages, and nothing changes for a visitor until late.

First the contract between Cloudflare, the sites and their content APIs, so the teams can work in parallel. Then it runs at Cloudflare in silent mode — working out the answer and recording it, changing nothing for anyone. That is the cheapest place to find a wrong mapping. The sites and their content APIs start using the answer next. Redirects come last, one region at a time, with a go or no-go each time.

05

What it costs to run

Cloudflare bills per request, not per page view — which is the only thing that makes this worth a section.

A page load is one request for the page plus its scripts, fonts and images — around 21 in total. If the service sits in front of all of them it runs 21 times per page view. Scoped to the page itself, it runs once.

Scoping is a simple Cloudflare configuration, and set correctly it is the whole difference:

Page views / monthScoped to pagesIn front of everything
1 million$5$8
5 million$5$35
10 million$5$69
25 million$10$174
On Acquia Edge

PADI runs on Acquia Edge, which is managed Cloudflare. These are Cloudflare’s public rates, so if this usage is already covered by that contract they may not apply at all. Worth confirming before anyone plans around the figures.

Where the detail lives

This document is deliberately short. The full architecture — the request flow step by step, the redirect rules, how each PADI property consumes the answer, and the reasoning behind every recommendation — is kept separately so it does not crowd out the discussion.