Skip to content

Localization and QA testing by country

See your site the way visitors in each country see it: language, currency, consent banners, redirects and region-specific features, checked on every release.

Proxy URL, exit IP in United Kingdom

http://USERNAME-cc-GB-session-a1b2c3d4-ttl-30:PASSWORD@scale-eu.netdash.io:2000

Residential exit IPs in United Kingdom

Your clientGateway -eu
Your clientGateway -eu
Sticky session: requests from your client go through the -eu gateway to one exit IP for 30 min, while that IP stays available.

Illustrative routes, not live traffic. Exits are available in most countries; the countries shown are examples.

  • Country-level exits by ISO code
  • Sticky sessions per test
  • Mobile exits on the same syntax

Localization testing proxies in your test suite

Language negotiation, currency, consent banners, geo-redirects and region-specific features often depend on where a visitor connects from. From the office or a CI runner you see one country’s version of the site, so this behavior ships untested and breaks for users abroad.

Localization testing proxies route each test through an exit IP in the country it covers. netdash takes the exit country as -cc- in the proxy username, so your test matrix is a list of ISO codes, and a sticky session keeps each test on one exit IP from the first page to the last assertion.

What depends on the visitor’s country

What depends on the visitor’s country
AttributeSignals your app may useHow to test it
LanguageIP country, Accept-Language, account settingAssert the lang attribute and key strings per exit country and browser locale
Currency and tax displayIP country, site selectorAssert the currency on a price element and the tax wording where it differs
Consent banner variantIP countryAssert which banner variant appears on a first visit
Geo-redirect to a country siteIP countryAssert the final URL, and that a visitor who picks another country stays there
Region-specific features or content rulesIP country, account regionAssert that the feature is present or absent per country

Build a country matrix

  1. Step 1: Write the matrix

    Country → expected language, currency, consent variant, redirect target and features.

  2. Step 2: Create a context per row

    Exit country via -cc-, a sticky session for the test, and the locale and timezone the test case calls for.

  3. Step 3: Assert expectations

    The lang attribute, price currency, banner presence and final URL.

  4. Step 4: Capture screenshots

    Key pages per country, for visual review.

  5. Step 5: Run on every release

    In CI, with each exit country paired with its gateway as in the configuration below.

    Country targeting
  6. Step 6: Triage differences

    Separate content bugs from signal bugs with the one-signal-at-a-time cases.

Test one signal at a time

Apps mix three signals: the IP country, the browser locale and the timezone. If every test changes all three together, a failure doesn’t tell you which signal your app used. Vary them independently instead.

For example, run a German exit with an en-US browser locale, then a US exit with de-DE. If the German exit still sends the visitor to the German site after they chose English, you’ve found a redirect that ignores the user’s language choice.

  • IP country: the -cc- part of the proxy username
  • Browser locale: locale on the browser context, which also sets Accept-Language
  • Timezone: timezoneId on the browser context
  • Site or account setting: your app’s own selector, set in the test

Matrix configuration

The examples use the Scale plan: every residential plan uses the same syntax, and you switch plans by changing the host and port. The proxy string format explains each part of the username.

United States

scale-us.netdash.io:2000 · USERNAME-cc-US-session-{SID}-ttl-30

Sticky, 30 minutes, through the Americas gateway.

Germany

scale-eu.netdash.io:2000 · USERNAME-cc-DE-session-{SID}-ttl-30

Sticky, 30 minutes, through the Europe gateway.

United Kingdom

scale-eu.netdash.io:2000 · USERNAME-cc-GB-session-{SID}-ttl-30

Sticky, 30 minutes. Use GB, not UK.

United Kingdom, mobile web

mobile-eu.netdash.io:4000 · USERNAME-cc-GB-session-{SID}-ttl-30

Sticky, 30 minutes, with a mobile viewport in the test.

Session ID (`SID`)

Country code in lowercase plus a short run hash, e.g. gb3fa91c

Letters and digits only. A new run gets new IDs, so each run starts on fresh sessions and each test keeps one exit IP while it stays available.

More countries

Add a row with the ISO code

To confirm availability for a specific country, contact support.

Generate one row of the test matrix

Choose a country and session lifetime, then copy the server, username and password into your test configuration.

Your route

Through the AshburnFrankfurt gateway to an exit IP in Via Ashburn (-us)United StatesCanadaMexicoBrazilArgentinaChileVia Frankfurt (-eu)United KingdomGermanyFranceSouth AfricaIndiaJapan, rotatingsticky for 15 minsticky for 30 minsticky for 12 hsticky for 7 days, on the SolveScaleOpus plan.

Proxy URL

http://USERNAME-cc-GB-session-a1b2c3d4-ttl-30:PASSWORD@scale-eu.netdash.io:2000

Replace USERNAME and PASSWORD with the credentials from your dashboard.

Scale, Europe gateway, exit IP in United Kingdom, sticky for 30 min.

curl --max-time 30 -x "http://USERNAME-cc-GB-session-a1b2c3d4-ttl-30:PASSWORD@scale-eu.netdash.io:2000" \
  "https://api.ipify.org?format=json"
import requests

proxy = "http://USERNAME-cc-GB-session-a1b2c3d4-ttl-30:PASSWORD@scale-eu.netdash.io:2000"
proxies = {"http": proxy, "https": proxy}  # set both, or HTTPS requests bypass the proxy

response = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=30)
response.raise_for_status()
print(response.text)
import { ProxyAgent, fetch } from "undici";

const dispatcher = new ProxyAgent({
  uri: "http://scale-eu.netdash.io:2000",
  token: "Basic " + Buffer.from("USERNAME-cc-GB-session-a1b2c3d4-ttl-30:PASSWORD").toString("base64"),
});

const res = await fetch("https://api.ipify.org?format=json", { dispatcher, signal: AbortSignal.timeout(30_000) });
console.log(await res.text());
import { chromium } from "playwright";

const browser = await chromium.launch({
  proxy: {
    server: "http://scale-eu.netdash.io:2000",
    username: "USERNAME-cc-GB-session-a1b2c3d4-ttl-30",
    password: "PASSWORD",
  },
});
const page = await browser.newPage();
await page.goto("https://api.ipify.org?format=json");
console.log(await page.textContent("body"));
await browser.close();
{"ip":"203.0.113.10"}

Your output shows a different address in the country you chose.

Illustrative routes, not live traffic. Exits are available in most countries; the countries shown are examples. For exits in Oceania, ask support which gateway to use.All locations

One Playwright test per country

Each matrix row gets its own browser context, with its own proxy, locale and timezone, so rows don’t share cookies or exit IPs. Playwright takes the proxy server, username and password as separate fields.

The expectations are examples: replace them with your app’s rules. Each session ID combines the country with a hash of the run ID, so every run starts on fresh sessions.

  • Playwright Test applies its default test and assertion timeouts
  • Add the mobile-web row with mobile-eu.netdash.io:4000 and a mobile viewport
  • Syntax-checked. Setup details for Playwright, Selenium and Puppeteer
import { createHash } from "node:crypto";
import { test, expect } from "@playwright/test";

const USER = process.env.NETDASH_USERNAME!;
const PASS = process.env.NETDASH_PASSWORD!;
const RUN_ID = process.env.CI_RUN_ID ?? String(Date.now()); // any value unique per run
const runHash = createHash("sha256").update(RUN_ID).digest("hex").slice(0, 6);
const sid = (cc: string) => `${cc.toLowerCase()}${runHash}`; // e.g. gb3fa91c

// Example expectations: replace them with your app's rules.
const matrix = [
  { cc: "US", host: "scale-us.netdash.io:2000", locale: "en-US", timezoneId: "America/New_York",
    lang: "en", currency: "$", consent: "consent-us", path: "/" },
  { cc: "DE", host: "scale-eu.netdash.io:2000", locale: "de-DE", timezoneId: "Europe/Berlin",
    lang: "de", currency: "€", consent: "consent-eu", path: "/de/" },
  { cc: "GB", host: "scale-eu.netdash.io:2000", locale: "en-GB", timezoneId: "Europe/London",
    lang: "en", currency: "£", consent: "consent-gb", path: "/en-gb/" },
];

for (const row of matrix) {
  test(`storefront in ${row.cc}`, async ({ browser }) => {
    const context = await browser.newContext({
      proxy: {
        server: `http://${row.host}`,
        username: `${USER}-cc-${row.cc}-session-${sid(row.cc)}-ttl-30`,
        password: PASS,
      },
      locale: row.locale,
      timezoneId: row.timezoneId,
    });
    try {
      const page = await context.newPage();
      await page.goto("https://staging.example.com/");

      await expect(page).toHaveURL(new RegExp(`${row.path}$`));
      await expect(page.locator("html")).toHaveAttribute("lang", new RegExp(`^${row.lang}`));
      await expect(page.getByTestId("price").first()).toContainText(row.currency);
      await expect(page.getByTestId(row.consent)).toBeVisible();
      await page.screenshot({ path: `screenshots/home-${row.cc}.png`, fullPage: true });
    } finally {
      await context.close();
    }
  });
}
Playwright

Testing staging behind a firewall

Residential exit IPs change, so a firewall can’t admit them by address. You have two options.

Test a publicly reachable staging host that is protected by your own authentication, such as a login or a signed preview link. Or use static ISP or datacenter IPs and add them to your firewall’s allow rules: they stay assigned to you for as long as the subscription renews and arrive as a host:port:user:pass list. ISP IPs are in Ashburn, Frankfurt, London and Sydney; datacenter IPs are in the US, Germany, the UK and the Netherlands.

Static IPs through sales

Self-serve checkout for ISP and datacenter proxies is paused. Contact sales for ISP or datacenter IPs with the locations and the number of IPs your staging setup needs.

Which product for which test

  • Residential proxies

    Self-serve

    One exit country per test, sticky for the length of the test. The examples use the Scale plan.

    From $1.20/GB, graduated per order

  • Mobile proxies

    Self-serve

    Mobile-web checks from mobile exit IPs, with a mobile viewport in the test.

    From $3.50/GB, graduated per order

  • ISP proxies

    Through sales

    Fixed IPs in Ashburn, Frankfurt, London or Sydney that your staging firewall can admit.

    $1.60 per IP per 30 days

Test what you’re responsible for

Test sites and apps you own, or that you’re authorized to test. Keep test traffic to what the suite needs. Targeting is country-level, so state- or city-specific rules can’t be tested this way. See how we expect netdash to be used.

Localization testing questions

No. Targeting is country-level, so rules that depend on a state, region or city can’t be tested this way.

Set them per test case. The exit country changes the IP address your app sees; the browser’s locale and timezone are separate signals. Varying them independently shows which one your app uses.

Residential exit IPs change, so use a publicly reachable staging host protected by your own authentication, or static ISP or datacenter IPs that your firewall admits. Static IPs are available through sales; self-serve checkout is paused.

Use any ISO 3166-1 alpha-2 code in the username. To confirm availability for a specific country, contact support. Gateways and static-IP locations are on the locations page.

Yes. Use the Mobile plan (mobile-eu.netdash.io:4000 or mobile-us.netdash.io:4000) and a mobile viewport in your test. Credentials and username syntax are the same as on the residential plans.

Add a country to your next release check

Pick one country your release checks don’t cover yet, add it as a matrix row, and run the suite against staging.

Privacy choices

We use cookies and similar technologies that this site needs to work and to remember your choice. We also use Google Analytics to understand which pages and features people use: in the EEA, the UK and Switzerland only with your consent. Live chat loads only if you open it. You can change your choice at any time under “Privacy choices” in the footer. Privacy Policy

Privacy choices

Choose what netdash.io may use. You can change this at any time under “Privacy choices” in the footer.

Necessary (always on)

Keeps the site working and secure, and remembers your privacy choices (nd_consent, 12 months).

In the EEA, the UK and Switzerland it stays off until you switch it on; elsewhere it is on unless you switch it off here. Google Analytics 4 measures page views, clicks on links and buttons, use of the price calculator, connection builder and code examples, scroll depth, file downloads, and page-performance metrics. It sets the cookies _ga and _ga_96MPNM6GHH, which last up to 12 months. Google processes this data, which may involve transfer to the United States. We do not send your name, email address or account details to Google Analytics. With this switched on we also remember a referral code or campaign you arrived with (nd_attr, 30 days), so a partner can be credited if you sign up.

Support chat (loads only when you open it)

Our live chat is provided by LiveChat. It loads only when you open the chat window. LiveChat then sets its own cookies and processes the messages you send and technical data about your browser.