Skip to Content

Ecommerce localization beyond translation

Articles


Vercel Retail

A storefront launch plan usually carries one line for localization, and that line is usually about language. Translated strings appear on a working /de route, the translation review passes, and the market is called open.

14 Sep 2026

15 min read

Copy

Ecommerce localization is six problems. Language is the smallest of them. The other five each depend on configuration in the commerce backend, which is how a translated route still prices a German order in dollars.

This guide covers what each layer requires, which decisions are hard to reverse, and what breaks once locale multiplies by currency.

Key takeaways:

  • Localization covers interface strings, currency display, tax presentation, price sourcing, catalog availability, and payment methods, and only the first two are settled in the frontend.
  • Redirecting shoppers by Internet Protocol (IP) address costs index coverage, because Googlebot crawls primarily from United States addresses and sends no Accept-Language header.
  • Subpath URLs consolidate domain authority under one deployment, and the price of that is hreflang correctness, which 67% of domains using hreflang get wrong.
  • Price books buy predictable margins and cost per-currency maintenance, while conversion at request time removes the maintenance and hands back exchange-rate risk on refunds.
  • Locale belongs in the URL path, because a locale carried in a Vary header or written as a cookie on a product response takes that page out of the cache entirely.

What is ecommerce localization?

Ecommerce localization is the practice of adapting a storefront's content, pricing, catalog, and checkout behavior to what shoppers in one market expect to see and, in several jurisdictions, are entitled to see. It spans the frontend and the commerce backend, and those two halves rarely ship in the same release.

The split is visible in tooling. Vercel and Shopify's rebuilt Hydrogen storefront template ships localization as two separate capabilities, and the template's own skills name the boundary. enable-i18n adds locale-prefixed routing, message catalogs, and a locale switcher. enable-shopify-markets adds region-aware pricing, inventory, and payments. A storefront can run the first without the second and look finished from the outside.


The six dimensions of a localized storefront

Language is the only dimension that needs no commerce backend configuration, so it ships first and alone. The other five each require a decision somewhere a frontend team does not usually own.

Here is what each dimension asks for, and where the work lands:

Dimension

Shipping zones plus local rails such as Swish, Twint, and iDEAL/Wero

Interface strings

Currency display

Where it's configured
Frontend
Message catalogs, pluralization rules, right-to-left layout
Frontend

Tax presentation

ISO 4217 codes and locale-aware formatting through Intl.NumberFormat

Per-market catalogs, product selections, or channel assignment
Commerce backend
Commerce backend
Per-currency price books, or conversion applied at request time

Catalog availability

Fulfillment and payment

Price sourcing
Commerce backend
What it requires
Commerce backend

Why the scope of localization decides whether an order can be placed

Four of the six rows are configured in the commerce backend, which is where a launch stalls after the strings are done.

A useful test is whether opening a new market requires configuration or engineering. A checkout that hardcodes two-decimal currencies and a single tax mode fails that test the day the second market opens, and no translation review will catch it.

Four failures follow from scoping localization as a translation project:


  • Currency is displayed but not charged: The storefront formats prices in euros while the cart settles in the store's base currency. The shopper sees one number and their bank statement shows another.
  • Tax appears only at checkout: In markets that require tax-inclusive display, a price that grows at the final step moves the problem from conversion to compliance.
  • The catalog ignores market rules: Products that cannot be shipped or sold in a region stay listed, and the products meant to be promoted there never surface.
  • Payment stops at cards: Markets where bank redirects and mobile wallets carry most volume see checkout abandonment that reads as a pricing problem in the analytics.

Core components of a localized ecommerce storefront

URL architecture: ccTLD, subdomain, or subpath

Six components do the work. The first two decide what search engines can index, and the other four decide what the shopper can buy.

You can reach our customer support team by emailing info@yourcompany.example.com, calling +1 555-555-5556, or using the live chat on our website. Our dedicated team is available 24/7 to assist with any inquiries or issues.

We’re committed to providing prompt and effective solutions to ensure your satisfaction.

We offer a 30-day return policy for all products. Items must be in their original condition, unused, and include the receipt or proof of purchase. Refunds are processed within 5-7 business days of receiving the returned item.

Dimension

One Domain Name System (DNS) record per locale

Registration and setup per country

Geo-targeting signal

ccTLD ( example.de)
Strong
Weak, compensated by hreflang
Separated

Domain authority

Migration per country code top-level domain (ccTLD)

Subdomain ( de.example.com)
Consolidated
Isolated per domain
Subpath (example.com/de/)

Infrastructure

Reversibility

Medium
Single deployment
Redirect map per subdomain
Path rewrite

Subdirectories are a practical default for many catalogs because they keep localized pages on one host and reduce infrastructure overhead. Use hreflang, localized content, currency, addresses, and other signals to identify the intended audience.


Hreflang annotations across every locale variant

Hreflang tells Google that several URLs are the same page for different audiences. Google accepts three equivalent methods, HTML link elements, an HTTP Link response header, and an XML sitemap. Using more than one carries no ranking benefit and multiplies what has to stay in sync.

The requirements are unforgiving in a specific way. Every variant must reference every other variant, including itself; annotations that aren't reciprocal are ignored silently, and URLs must be fully qualified.

Use a supported ISO 639-1 language code, optionally followed by an ISO 15924 script code and an ISO 3166-1 Alpha-2 region code. For example, use en-GB, not en-UK. Add x-default for selectors or unmatched visitors.


Currency display and settlement currency

Three currency roles may be involved: the currency shown to the customer, the integration or transaction currency, and the settlement currency. Two or more roles can use the same currency. Refund exchange-rate exposure depends on the provider and conversion product, so verify refund behavior before selecting a pricing model.

The distinction shows up in accounting before it shows up on the page. Under Stripe Adaptive Pricing, the conversion cost sits inside the rate the shopper sees as a 2% to 4% fee, waived if they pay in the integration currency.

Price sourcing: price books or live conversion

A price book stores an explicit price per currency. Some backends implement this as standalone price records scoped to a currency, and those platforms don't copy price records between currencies, so every currency needs its own book. Conversion at request time is the other model, applying a rate to a base price as the page renders.

Price books buy control and predictable margins, and cost hand-maintenance of every currency. Conversion removes that maintenance and hands back exchange-rate risk, most visibly on a refund issued weeks later that no longer reconciles to what the customer paid.


Catalog and market availability

Every commerce backend has a market primitive, and they don't agree on shape. Shopify links a catalog to each Market and scopes Storefront API queries with an @inContext(country:) directive. commercetools assigns Product Selections to Stores. BigCommerce requires explicit channel assignment, where an unassigned product cannot be sold on that storefront at all. VTEX bundles catalog, pricing, promotions, and logistics into a trade policy, which is its term for a sales channel.

A frontend that fetches without the right market context shows items that can't be bought in that region, or drops the ones that were meant to be promoted there.


Tax display and local payment rails

Tax presentation is a display rule before it is a calculation rule. EU consumer law requires the total price including value-added tax (VAT) before the consumer is bound, under the Consumer Rights Directive as amended by the Modernisation Directive. Australia and Singapore apply comparable goods and services tax (GST) inclusive display rules. The US goes the other way, adding tax at checkout at a rate that depends on the buyer's state. These constraints are jurisdictional and fact-specific, and naming them is not tax or legal advice.

Payment rails follow the same market logic, and they move. Bank redirects and wallets carry most consumer volume in several European markets, and the Dutch rail is a live example of what that costs to track. The transition from iDEAL to Wero began with iDEAL | Wero co-branding in 2026. The current plan is for merchants to transition by the end of 2027, followed by a gradual phaseout of the iDEAL brand. Treat branding and migration dates as subject to change and confirm them with the payment service provider.

5 best practices for localizing an ecommerce storefront

The components describe what has to exist. The five decisions below determine whether a storefront built from them survives production traffic.


Suggest the locale instead of redirecting on IP address

The default pattern in most storefront codebases reads the country from the IP address and redirects. It costs three things, and none of them show up in testing.

Index coverage goes first. Googlebot crawls primarily from United States addresses and sends no Accept-Language header, so a redirect keyed on a guessed locale can leave every other variant uncrawled. Google advises against redirecting on assumed language or location.

Compliance headroom goes next. In the EU, Regulation 2018/302 prohibits rerouting a customer to a different country version of an interface without explicit prior consent, and requires the originally requested version to stay reachable. The Commission opened an evaluation of that regulation in 2025, so check the obligations before a rollout rather than inheriting a 2018 reading of them.

Accuracy is the last of the three. Country-level IP geolocation accuracy exceeds 99%, but city-level accuracy runs between 20% and 75%, and a shopper on a virtual private network (VPN) lands in the wrong store regardless.

Reading Accept-Language, matching it against supported locales, and offering the match keeps the guess reversible. The confirmation goes in a cookie, and you read it ahead of the header on return visits. A redirect that is still wanted belongs on the homepage alone, serving crawlers what any visitor from that address sees, which keeps it out of cloaking territory.


Store money in minor units and format at render time

Display code breaks on currencies before it breaks on languages, because the number of decimal places is not constant.

Three groups matter, and they belong in the type system rather than the template:

Minor units

0

Currencies

JPY, KRW

1,000 of the currency stored as

1000

2

3

USD, EUR

KWD, BHD, JOD

100000

1000000

Storing ¥1,000 as 100000 is an off-by-100 error that reaches production because it looks right in a two-decimal test suite. Payment providers publish the currency minor units their APIs expect, and formatting belongs to Intl.NumberFormat at render time, never to string concatenation. Rounding is a separate surface owned by the commerce backend. Shopify applies its own rounding rules to converted prices, turning $14.27 into $14.00, and doesn't expose those rules to storefront code.


Make hreflang reciprocal and validate it with a crawler

Hreflang breaks without raising an error anywhere, so nothing surfaces until rankings for a locale quietly flatten. Across 374,756 domains, Ahrefs found in 2023 that 67% had at least one hreflang issue. Reciprocity breaks most, because it fails the moment one variant ships a template the others haven't picked up yet.

Validation now has to run outside Google's own tooling. Search Console's International Targeting report was deprecated in 2022, so hreflang correctness belongs in the deploy pipeline, checked by a crawler or an external validator on every build.


Let the commerce backend's market primitive decide availability

Teams routinely rebuild market logic in the frontend, filtering products by a locale string after fetching a global catalog. That holds until a market has a product the global catalog doesn't carry, or a product that must not appear in one region for licensing reasons.

Passing market context into the query instead lets the backend return the catalog that market is allowed to see. It costs a fetch-layer refactor and a market identifier threaded through every data call, and it buys one source of truth for availability. The case that repays it is the one hardest to test for, a product that should be invisible in one region and prominent in another.


Keep locale in the URL path and out of the Vary header

Cache keys are where locale strategy shows up as an infrastructure bill. Vercel’s CDN builds a cache key from the request method, URL, host domain, deployment URL, and scheme. Any header named in Vary multiplies that key by every distinct value seen for it.

Vary: X-Vercel-IP-Country produces roughly 200 entries per path. Accept-Language is worse, because browsers send distinct strings for the same language. Vary: Cookie makes a path effectively uncacheable. A response carrying set-cookie is not cached at all, whatever Cache-Control says, so writing a locale cookie on the first product-page response turns caching off for that page. Setting it on the redirect from the language switcher instead keeps the product response cacheable, and that one change fixes the most common source of storefront cache misses.

Encoding locale in the path avoids all of it, because the locale enters the cache key through the URL and needs no Vary header.


How Vercel supports ecommerce localization for retail engineering teams

Most of what a localized storefront needs at the routing layer is either a request header or a rewrite. These five are what retail teams reach for first.


Read the shopper's country without a geolocation lookup service

The usual first move on a localization project is to budget for an IP database subscription and a lookup service in front of the storefront. That adds a dependency to the hot path of every request.

Every request reaching Routing Middleware and Vercel Functions already carries eight geolocation headers, including x-vercel-ip-country, x-vercel-ip-country-region, x-vercel-ip-city, x-vercel-ip-timezone, and x-vercel-ip-postal-code. The geolocation() helper in @vercel/functions returns most of those as one object. The caveat to design around is a proxy in front of the deployment, which makes the values describe the proxy instead of the shopper until an Enterprise team adds Trusted Proxy, which is an add-on rather than an included feature.


Rewrite to the locale path before the request reaches the cache

A locale redirect costs a browser round trip on the first request of every session, which is the request that sets the Largest Contentful Paint number for a new visitor.

Routing Middleware runs globally before the cache, so it can rewrite a request to its locale-prefixed path with the URL staying stable and no round trip added. In Next.js 16 the file is proxy.ts exporting a proxy function on the Node.js runtime, and npx @next/codemod@latest middleware-to-proxy . handles the rename from earlier versions.

A locale match that suggests rather than forces looks like this:

Our Services

import{ match }from'@formatjs/intl-localematcher'

importNegotiatorfrom'negotiator'

const locales =['en-us','de-de','fr-fr','ja-jp']

import{NextRequest,NextResponse}from'next/server'
return
exportfunctionproxy(request:NextRequest){
)

if(

){

const defaultLocale ='en-us'
locales.some(
(locale)=>
const{ pathname }= request.nextUrl

const headers ={

pathname ===`/${locale}`|| pathname.startsWith(`/${locale}/`)

}
const stored = request.cookies.get('storefront_locale')?.value
'accept-language': request.headers.get('accept-language')??'',
}

The stored cookie takes priority over the header, so a shopper who switches markets once keeps that choice without another negotiation.


Change market rules without shipping a deploy

Which markets are live, which currencies each accepts, and which catalog a region maps to are settings a merchandising team owns. Holding them in the bundle turns every one of those changes into a deploy.

Global Config stores configuration outside the deployment and serves the vast majority of optimized reads within 15ms at P99. Because updates propagate globally rather than providing transactional consistency, keep inventory and other immediately consistent data in a transactional system. Cache one shell per locale and stream the price.

Product pages are where locale and currency collide, because the description is identical for everyone in a locale, while the price, tax, and availability are not. Caching the whole page per shopper is wasteful, and caching nothing gives up the CDN.

Cache Components splits the response along that line. The layout, navigation, and description render into a shell that caches once per locale path. The per-shopper values stream inside a Suspense boundary:

import{Suspense}from'react'

import{ProductDescription}from'@/components/product-description'

params,

}:{

exportdefaultasyncfunctionProductPage({
)
params:Promise<{ locale:string; handle:string}>

}){

return(

const{ locale, handle }=await params
const product =awaitgetProduct(handle, locale)

{product.title}

import{LocalizedPrice,PriceSkeleton}from'@/components/localized-price'

}>
}

Cache Components is enabled with cacheComponents: true in Next.js 16, which replaces the experimental.ppr flag that carried Partial Prerendering through the Next.js 15 canaries. Whether revalidating one locale path also revalidates its siblings isn't documented, so test revalidation scope per path before you open a market.


Start from a template that separates internationalization from Markets

The costly version of a localization project is discovering in month three that routing and commerce were built as one layer, and that adding a market means touching both.

Starting from the rebuilt Hydrogen template keeps the two apart from the first commit, so a team can ship translated routes early without pretending the commerce side is finished. The template is framework-agnostic, so the same split holds whether the storefront runs on Next.js, Nuxt, or Svelte.

Commerce-UI built dedicated regional pages for Lift Foils, each localized and personalized, that stayed fast during traffic spikes of 200%. Helly Hansen, which operates across 38 global markets, completed a global rollout in under six months and moved its Core Web Vitals from all red to all green within five months.


Ship a localized storefront that still hits the cache

Localization goes well when you scope it as a routing and data problem from the start, and hand translation the share of it that translation owns. Currency, tax, catalog, and payment decide whether the order goes through, and the URL and caching choices made in week one set how expensive those are to add in month six.

Here is what the platform handles so those decisions stay reversible:

  • Routing Middleware: Adding a market becomes a routing rule instead of a redirect strategy, and the cache key stays clean.
  • Geolocation headers: Country detection needs no vendor contract, no lookup hop, and nothing to renew.
  • Global Config: Merchandising changes which markets are live without waiting on a build.
  • Cache Components: One market's pricing logic stops dictating another market's cache hit rate.
  • Speed Insights: A performance regression in a single locale surfaces per route, before it spreads across the catalog.

Frequently asked questions about ecommerce localization

Start a storefront on vercel.com/new, or browse the commerce starters at vercel.com/templates to see the locale routing and Markets split already wired up.

What is the difference between localization and internationalization in ecommerce?

Internationalization is the one-time structural work, covering dynamic route segments, message catalogs, and money stored in minor units. Localization is the per-market work that repeats for every market after it. If launching a third market costs about what the first one did, the internationalization layer was skipped.


Can I auto-redirect shoppers to their local storefront?

Yes, once the shopper has agreed to it, and only if the storefront they first asked for stays one click away. A redirect compelled by law is the exception that needs no agreement, and it still owes the shopper a clear and specific reason on the page.


Does Vercel geolocation need a third-party IP database?

No. Location fields arrive as request headers with nothing to install or subscribe to. A commercial IP database earns its place only for signals those headers do not carry, such as flagging a visitor arriving over a VPN or a hosting provider.


How do I generate hreflang tags in the Next.js App Router?

alternates.languages in the Metadata API emits them, exported from a layout or page with a self-referencing entry plus one per alternate. The Pages Router doesn't generate hreflang from its i18n config, so those tags go in next/head by hand.

Ready to deploy?