How to Build a Web Scraping Pipeline for Localized Sites and Regional Variants
Build a deterministic locale-aware scraping pipeline that discovers regional variants, controls request state, normalizes localized fields, and validates every capture.
Localized scraping is not the same as extracting text in multiple languages. A product page can vary by country even when its visible language is identical: prices, stock, legal copy, shipping rules, addresses, date formats, and available products may all change.
The common failure mode is deceptively simple: a scraper requests one URL, gets a successful response, and assumes it collected the intended market. In practice, it may have received a default locale, a cached representation selected for an earlier request, or a page whose country selector changes only after client-side code runs.
A dependable approach is to make locale selection explicit, reproducible, and auditable. Treat every market capture as a run with defined inputs and evidence—not as an incidental property of the page.
Model a locale as a complete request profile
Do not use en, fr, or de as the only dimension in your data model. A language is one component of a locale, but it does not uniquely identify a market. en-GB and en-US, for example, can have different currency, inventory, legal content, and date conventions.
Create a locale matrix where each row is a target representation:
id: de-DE
languageTag: de-DE
country: DE
currency: EUR
timezone: Europe/Berlin
geolocation:
latitude: 52.52
longitude: 13.405
urlStrategy: path
startUrl: https://example.com/de-de/products
acceptLanguage: de-DE,de;q=0.9,en;q=0.5
selectorState:
country: Germany
language: Deutsch
Use canonical language-region identifiers rather than informal labels such as German-Germany. Unicode locale guidance builds on BCP 47 language tags and supplies locale data concepts for matching and formatting. It also distinguishes locale-sensitive data such as dates, numbers, currencies, and measurement units. See the Unicode Locale Data Markup Language specification.
Keep the matrix versioned. A changed URL pattern, cookie name, or consent flow should be a configuration change that can be reviewed and replayed, not an undocumented edit in a scraping script.
Discover explicit regional variants before emulating behavior
Start with the site’s declared variant structure. Explicit URLs are generally more stable and easier to test than inferring a region from a redirect or an IP-dependent response.
For each seed page, collect potential variants from:
- Country-code domains, regional subdomains, and locale subdirectories
hreflangalternate links in HTML- HTTP
Linkheaders - XML sitemaps
- Language and country selectors
- Fully qualified links to alternate storefronts
Google’s internationalization documentation describes these structures and recommends connecting localized pages through alternate-version annotations. Its guidance also notes that a localized page should reference itself and its alternates. Localized versions of pages is a useful reference when designing the discovery pass.
Store discovered relationships as data, not merely as links to crawl. For example:
{
"canonicalProductKey": "sku-4921",
"variants": [
{ "locale": "en-US", "url": "https://example.com/us/p/sku-4921" },
{ "locale": "en-GB", "url": "https://example.com/uk/p/sku-4921" },
{ "locale": "de-DE", "url": "https://example.com/de-de/p/sku-4921" }
],
"discoveredFrom": "hreflang"
}
A site may not expose every version through metadata. In that case, inspect its visible selector and record the navigation or API calls it triggers. But retain a distinction between declared variants and behaviorally discovered variants. The former are direct targets; the latter need more state control and verification.
Make HTTP negotiation part of the cache key
Some server-rendered sites choose a representation through request headers. The important request field is often Accept-Language, but it may not be the only input. Cookies, URL parameters, and authentication state can matter too.
HTTP defines Accept-Language as a language-preference field. It also defines Vary as the response mechanism that names request fields used in selecting a representation. If a response says Vary: Accept-Language, a cache must not treat an English response as reusable for a German request just because the URL matches. Review the details in RFC 9110.
For every HTTP fetch, persist at least:
{
"requestedUrl": "https://example.com/products",
"finalUrl": "https://example.com/de-de/products",
"requestHeaders": {
"accept-language": "de-DE,de;q=0.9,en;q=0.5"
},
"responseHeaders": {
"content-language": "de-DE",
"vary": "Accept-Language, Cookie"
},
"localeRun": "de-DE"
}
Build cache keys from the URL plus every known representation input. A practical key may include normalized Accept-Language, relevant cookies, a market header if the site uses one, and the locale-matrix version. If a response contains Vary, record it and evaluate whether your existing key includes the named fields.
Do not make IP-derived location the sole determinant of a crawl. Google cautions that location-based routing can be unreliable and that crawlers may not originate from locations needed to discover all versions. Explicit localized URLs, sitemaps, and visible links are better primary discovery signals. See Google’s guidance on multi-regional sites.
Use isolated browser contexts for JavaScript locale flows
An HTTP client is often sufficient when a locale has a stable URL and server-rendered content. Use a browser when the site renders its selector in JavaScript, stores market choice in client state, or calculates content after page load.
The key design decision is isolation. Create a fresh browser context for each locale run. Configure locale, timezone, geolocation, headers, cookies, and storage deliberately. Playwright exposes these controls at the browser-context level, including locale behavior that affects navigator.language, the Accept-Language header, and locale-sensitive browser formatting. Its Browser API and BrowserContext API document the available controls.
A conceptual workflow looks like this:
const context = await browser.newContext({
locale: "de-DE",
timezoneId: "Europe/Berlin",
geolocation: { latitude: 52.52, longitude: 13.405 },
permissions: ["geolocation"],
extraHTTPHeaders: {
"Accept-Language": "de-DE,de;q=0.9,en;q=0.5"
}
});
const page = await context.newPage();
await page.goto("https://example.com/");
await page.getByRole("button", { name: /country|region/i }).click();
await page.getByText("Germany", { exact: true }).click();
The selectors above are illustrative; each site needs its own tested flow. After a selection, wait for the specific state transition you expect: navigation, a cookie update, an API response, or a visible currency change. Never assume a successful click changed the market.
Then persist the resulting URL, relevant cookies, storage state, visible selector text, and response metadata. That evidence makes it possible to reproduce a capture and diagnose whether a site changed its behavior.
Build locale-aware captures with PagePith
PagePith’s supplied fetch proof shows a successful fetch of Google’s multi-regional-site documentation at its requested URL. The result recorded the page title, a
fetchtier, and 12,140 characters of content, including text distinguishing multilingual from multi-regional sites. This demonstrates a useful first step for explicit documentation or server-rendered pages: retain the requested URL and captured content as run evidence. It does not demonstrate browser-based selector interaction, geolocation, or automatic locale switching. Try PagePith.
Preserve raw values before normalization
Localized values require context. The string 1.234,56 could represent a decimal-comma price, while 1,234.56 follows another convention. A currency symbol alone is also insufficient: symbols can be shared by multiple currencies.
Extract into a two-layer schema:
{
"price": {
"raw": "1.234,56 €",
"displayCurrencySymbol": "€",
"locale": "de-DE",
"declaredCurrency": "EUR",
"normalizedAmount": "1234.56",
"normalizedCurrency": "EUR"
},
"capturedAt": "2026-10-13T12:00:00Z"
}
The raw field is evidence. The normalized field is what downstream analytics should use. Do not discard the original string, because it helps diagnose parsing errors and preserves what the site displayed.
Use the intended locale and a confirmed currency code to guide parsing. Unicode’s LDML numbers specification documents locale-specific decimal symbols, grouping, number patterns, currency formats, and currency identifiers. When a page does not explicitly declare currency, use multiple signals—structured data, ISO code, selector state, and page context—before normalizing.
Apply the same principle to dates and availability windows. Record the source text, page locale, timezone used for the run, and the normalized timestamp or date. Otherwise, a promotion ending at midnight in one market can be misinterpreted after aggregation.
Validate the representation, not just the status code
A 200 OK proves only that a server returned a representation. It does not prove that it was the representation you asked for.
Define assertions for each locale capture. A good validation suite combines independent signals:
- URL identity: Does the final URL match the expected URL strategy or known alternate URL?
- Language evidence: Do
langattributes,Content-Language, visible copy, orhreflangreferences support the target language? - Market evidence: Do displayed currency, address, phone format, shipping region, or legal entity align with the target country?
- State evidence: Does the visible selector agree with the requested market, and do persisted cookies or storage confirm it where applicable?
- Cross-variant evidence: Where differences are expected, do the variants actually differ in an expected field such as currency or availability?
Google identifies language, currency, local address and phone information, country-targeted domains, and alternate annotations as signals associated with regional targeting. That makes a multi-signal validator more robust than any single heuristic. See Managing Multi-Regional and Multilingual Sites.
Classify validation failures explicitly. For example, wrong_currency, language_mismatch, selector_not_persisted, and unexpected_redirect are much more actionable than a generic scrape_failed.
Add observability and crawl controls
A locale pipeline needs run-level observability because a site can change routing, consent behavior, or markup without warning. Log the locale matrix version, request profile, final URL, redirect chain, response headers, browser state artifact references, extraction schema version, validation outcomes, and content hash.
This data answers operational questions quickly: Did only fr-CA fail? Did the final URL begin redirecting to a global storefront? Did the content change while headers stayed the same?
Crawl responsibly. Check robots.txt, applicable terms and access restrictions, rate-limit requests, and identify your crawler where appropriate. Google explains that robots.txt communicates crawler access preferences and can help manage crawl traffic, while also noting that it is not an access-control mechanism. Read the robots.txt introduction before treating it as either permission or security.
Choose the simplest architecture that proves the market
Use direct HTTP fetching when an explicit locale URL yields the intended content and response evidence supports that conclusion. Escalate to a browser context only when locale selection or rendering requires it. This keeps the system less fragile while preserving a path for JavaScript-heavy sites.
The durable pattern is straightforward: enumerate variants, define locale inputs, isolate state, capture evidence, preserve raw values, normalize with locale context, and validate the market representation. Once locale is treated as a first-class input, regional scraping becomes a repeatable data pipeline instead of a collection of one-off country workarounds.
Ready to make localized collection more auditable? Create a PagePith account.
Sources
- Managing Multi-Regional and Multilingual SitesGoogle Search Central
- Localized Versions of Your PagesGoogle Search Central
- RFC 9110: HTTP SemanticsIETF
- Browser APIPlaywright
- BrowserContext APIPlaywright
- Unicode Locale Data Markup LanguageUnicode Consortium
- LDML Part 3: NumbersUnicode Consortium
- Robots.txt Introduction and GuideGoogle Search Central