# General Translation Knowledge Base: What is pseudo-localisation?
URL: https://generaltranslation.com/en-GB/kb/pseudo-localization.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Pseudo-localisation is a test that swaps your source text for an altered version, accented, padded about 40% longer, and wrapped in brackets, so hardcoded strings, truncation, broken fonts, and concatenated sentences show up before any translation exists. Android ships the en-XA and ar-XB pseudo-locales for it.
Category: Internationalisation
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

Pseudo-localisation (also written pseudolocalisation) tests whether your code is ready for translation, not whether a translation is good. Because the altered text stays readable, anyone on the team can click through the app in a pseudo-locale and spot what breaks. It runs in seconds, costs nothing, and works from the first day of development.

## What&#39;s the difference between pseudotranslation and a pseudo-locale?

[Microsoft&#39;s globalisation docs](https://learn.microsoft.com/en-us/globalization/methodology/pseudolocalization) split it into two parts:

* **Pseudotranslation** transforms the strings themselves: &quot;Save changes&quot; becomes something like `[Šáṽé çĥáñĝéš~~~~~]`.
* **Pseudo-locales** are fake locale codes, such as Windows&#39; `qps-ploc` or Android&#39;s `en-XA`, that you can select like a real language. Some also carry deliberately unusual date, time, and number formats to test formatting code.

Microsoft recommends codes that are well-formed but will never describe a real locale, such as the Windows `qps-` codes, which sit in a range BCP 47 reserves for private use. It learned this the hard way: its first pseudo-locale was `tk-TM` (Turkmen, Turkmenistan), which later shipped as a real Windows locale and collided with build and test systems.

## What bugs does pseudo-localisation catch?

* **Hardcoded strings.** Any text that didn&#39;t go through your i18n layer stays plain English while everything around it is accented, so it stands out on every screen. (For finding these in code, see [How do I find hardcoded strings that aren&#39;t translated?](/kb/find-hardcoded-strings))
* **Truncation and overflow from text expansion.** Padded strings show which buttons, tabs, and table headers clip, wrap badly, or push controls off screen.
* **Encoding and font problems.** Accented and non-Latin characters reveal broken encodings, fonts without the glyphs, and line heights that cut off diacritics.
* **Concatenation.** A sentence assembled from fragments shows up as several bracketed pieces, `[Ýöû ĥáṽé~~~~] 3 [íţéɱš~~]`, instead of one, which tells you translators would get the fragments without the sentence.
* **Missing right-to-left support.** An RTL pseudo-locale shows whether the layout mirrors, which icons need flipping, and where left-to-right values break. (See [How do I support RTL (right-to-left) languages in CSS?](/kb/right-to-left-css))

## How does pseudo-localisation work?

Most tools combine some of these:

* **Accents and other scripts.** Replace `a` with characters like `á`, `ä`, or `å`, and digits with circled digits, as Microsoft&#39;s guide suggests.
* **Expansion.** Lengthen each string. For English sources, Microsoft suggests about 40% as a heuristic, and notes that some strings run 200% or even 400% longer once translated, with one- or two-word strings growing the most.
* **Delimiters.** Wrap each string in markers such as brackets or carets. A missing closing marker means the text was truncated.
* **Identifiers.** Optionally add a short ID to each string so a tester can find its source.
* **Right-to-left.** Reverse the text direction so the UI behaves as it would in Arabic or Hebrew, as Android&#39;s `ar-XB` and Windows&#39; mirrored `qps-plocm` do.

Translations can also get shorter. A width change belongs to a particular string rendered in a particular font, size, and weight; it isn&#39;t a fixed percentage for a whole language. Padded pseudotranslations test expansion. Use real translations to check shorter labels, spacing, and alignment too.

## Which platforms have built-in pseudo-locales?

| Platform            | Pseudo-locales                                                                          | What they do                                                                                                                            | How to turn them on                                                                                                                                  |
| ------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Android             | `en-XA`, `ar-XB`                                                                        | `en-XA` adds accents, expands text, and brackets each message; `ar-XB` switches messages to right-to-left and reverses their characters | `pseudoLocalesEnabled` on the debug build type; enable Developer options and reboot the device, then pick the pseudo-locale in its language settings |
| Xcode (iOS, macOS)  | Double-Length, Right-to-Left, Accented, Bounded String, Tall, and other pseudolanguages | Double string length, flip direction, add accents, wrap strings to expose truncation, add vertical height                               | **Product &gt; Scheme &gt; Edit Scheme &gt; Run &gt; Options**, then choose from the **App Language** menu                                           |
| Windows             | `qps-ploc`, `qps-plocm`, `qps-ploca` (plus `qps-Latn-x-sh` from Windows 10)             | Base, mirrored (right-to-left), and East Asian pseudo-locales with test data for dates, fonts, and string lengths                       | Pass the pseudo-locale name to the NLS APIs, such as `GetLocaleInfoEx`; Microsoft&#39;s Multilingual App Toolkit pseudo-translates resources         |
| Lingui (JavaScript) | Any locale you name, such as `pseudo-LOCALE`                                            | Generates accented messages at compile time, with an optional right-to-left variant                                                     | The `pseudoLocale` key in the Lingui config                                                                                                          |

On Android, the [pseudolocales guide](https://developer.android.com/guide/topics/resources/pseudolocales) enables them in the module&#39;s Gradle file:

```kotlin title="build.gradle.kts"
android {
    buildTypes.getByName("debug") {
        isPseudoLocalesEnabled = true
    }
}
```

The [Gradle reference](https://developer.android.com/reference/tools/gradle-api/8.3/null/com/android/build/api/dsl/BuildType) notes that a `resourceConfigurations` filter can strip the pseudo-locale resources from the APK. In Xcode, the same scheme sheet also has **Show non-localized strings**, which renders strings Xcode won&#39;t export in all caps. (See Apple&#39;s [Preparing your interface for localization](https://developer.apple.com/documentation/xcode/preparing-your-interface-for-localization).) Windows lists its codes on the [Pseudo-Locales](https://learn.microsoft.com/en-us/windows/win32/intl/pseudo-locales) page and notes that they aren&#39;t enumerated as system locales by default ([Using pseudo-locales](https://learn.microsoft.com/en-us/windows/win32/intl/using-pseudo-locales-for-localization-testing)), and Lingui documents its set-up in the [pseudolocalisation guide](https://lingui.dev/guides/pseudolocalization).

## Pseudo-localise strings in JavaScript

On the web there&#39;s no built-in pseudo-locale, but a transform is a few lines. This one accents ASCII letters, pads by about 40%, and brackets the result, while leaving `{placeholders}` and HTML tags untouched so interpolation and markup still work:

```js title="pseudo.mjs"
// Accented look-alikes for ASCII letters
const ACCENTS = {
  a: 'á', b: 'ƀ', c: 'ç', d: 'ð', e: 'é', f: 'ƒ', g: 'ĝ', h: 'ĥ', i: 'í',
  j: 'ĵ', k: 'ķ', l: 'ļ', m: 'ɱ', n: 'ñ', o: 'ö', p: 'þ', q: 'ǫ', r: 'ŕ',
  s: 'š', t: 'ţ', u: 'û', v: 'ṽ', w: 'ŵ', x: 'ẋ', y: 'ý', z: 'ž',
  A: 'Á', B: 'Ɓ', C: 'Ç', D: 'Ð', E: 'É', F: 'Ƒ', G: 'Ĝ', H: 'Ĥ', I: 'Í',
  J: 'Ĵ', K: 'Ķ', L: 'Ļ', M: 'Ṁ', N: 'Ñ', O: 'Ö', P: 'Þ', Q: 'Ǫ', R: 'Ŕ',
  S: 'Š', T: 'Ţ', U: 'Û', V: 'Ṽ', W: 'Ŵ', X: 'Ẋ', Y: 'Ý', Z: 'Ž',
};

// Placeholders like {name} and tags like <b> must survive untouched
const PROTECTED = /(\{[^}]*\}|<[^>]+>)/;

export function pseudoLocalize(text, { expansion = 0.4 } = {}) {
  const accented = text
    .split(PROTECTED)
    .map((part, i) =>
      i % 2 === 1 ? part : part.replace(/[A-Za-z]/g, (ch) => ACCENTS[ch])
    )
    .join('');
  // Pad by about 40% of the source length to simulate longer translations
  const padding = '~'.repeat(Math.ceil(text.length * expansion));
  // Brackets show where a string starts and ends, so truncation is visible
  return `[${accented}${padding}]`;
}
```

```js title="demo.mjs"
import { pseudoLocalize } from './pseudo.mjs';

for (const source of ['Save changes', 'Hello, {name}!', 'Read the <a>terms</a>']) {
  console.log(pseudoLocalize(source));
}
```

Running `node demo.mjs` with Node.js 24:

```text title="Output"
[Šáṽé çĥáñĝéš~~~~~]
[Ĥéļļö, {name}!~~~~~~]
[Ŕéáð ţĥé <a>ţéŕɱš</a>~~~~~~~~~]
```

To use it, apply it to every message your i18n layer returns while a debug flag or a fake locale is active. Then a concatenated sentence gives itself away:

```js title="concatenation.mjs"
import { pseudoLocalize } from './pseudo.mjs';

// ❌ Three pieces: translators never see the whole sentence
console.log(pseudoLocalize('You have') + ' 3 ' + pseudoLocalize('items'));
// ✅ One message with a placeholder
console.log(pseudoLocalize('You have {count} items'));
```

```text title="Output"
[Ýöû ĥáṽé~~~~] 3 [íţéɱš~~]
[Ýöû ĥáṽé {count} íţéɱš~~~~~~~~~]
```

The placeholder regex handles simple `{name}` variables. ICU plural and select messages nest braces, so pseudo-localise the formatted output instead of the raw message. The same applies to the [`pseudo-localization`](https://github.com/tryggvigy/pseudo-localization) npm package: its `pseudoLocalizeString('Hello, {name}!')` returns `Ħḗḗŀŀǿǿ, {ƞȧȧḿḗḗ}!`, accenting the placeholder name too, so run it on translated output as its README shows. That package also has a `bidi` strategy for right-to-left testing and a DOM mode that pseudo-localises a whole page. For an RTL check of your own, set `dir="rtl"` on the `<html>` element while the pseudo-locale is active.

To find hardcoded strings, keep the transform at the i18n boundary. A whole-page DOM transform changes hardcoded text too, so it can test layout but hides which strings skipped your translation calls. Transforming formatted output also changes interpolated names and other data; keep that output in the test UI.

## What pseudo-localisation doesn&#39;t test

Pseudo-localisation says nothing about translation quality, word order, or tone. It also can&#39;t exercise plural rules: English has 2 plural forms and Arabic has 6, and an accented English string still has English&#39;s 2. Microsoft&#39;s guide says it &quot;isn&#39;t a complete replacement&quot; for validating the translated application, so test real languages before launch, too. The [multilingual app launch checklist](/kb/multilingual-app-launch-checklist) covers what to check.

## Pair pseudo-localisation with real translations

General Translation lets you test real translations during development: with a project API key that has runtime translation permission, `gt-next` requests translations from GT&#39;s service on demand. Switch to German to check actual wording and layout, and to a language with different plural rules to exercise those branches. Text outside [`<T>`](/docs/react/reference/components/t) or a translation function stays in the source language. (See the [Next.js Quickstart](/docs/react/nextjs-quickstart)). Use pseudo-localisation for an early layout pass that testers can read, then generate release translations with the [CLI](/docs/cli/reference/commands/translate) and review them in GT&#39;s [translation editor](/docs/platform/dashboard/guides/reviewing-translations) and in the running app. Padding English cannot establish whether a translated sentence is correct or fits the same space.

## Key takeaways

* Pseudo-localisation replaces source text with accented, padded, bracketed, or mirrored versions to find i18n bugs before translation starts.
* It catches hardcoded strings, truncation, font and encoding problems, concatenated sentences, and missing right-to-left support.
* Android (`en-XA`, `ar-XB`), Xcode (pseudolanguages in the scheme options), and Windows (`qps-ploc`, `qps-plocm`, `qps-ploca`) have it built in; on the web, a short transform function or the `pseudo-localization` package does the job.
* It doesn&#39;t test translation quality or plural rules; for those, test with real translations, which `gt-next` can generate on demand during development.

## Related questions

- [How do I find hardcoded strings that aren't translated?](https://generaltranslation.com/en-GB/kb/find-hardcoded-strings.mdx): Run a linter that flags user-facing string literals in CI, such as eslint-plugin-i18next's no-literal-string rule for JSX, Android Lint's HardcodedText check, or erb_lint's HardCodedString linter for Rails. Then click through the app in a pseudo-locale to catch text that static checks can't trace.
- [How do I support RTL (right-to-left) languages in CSS?](https://generaltranslation.com/en-GB/kb/right-to-left-css.mdx): Put dir="rtl" and lang on the html element, because lang alone doesn't change direction, then replace physical CSS such as margin-left with logical properties such as margin-inline-start. Flex rows, grid columns, and default text alignment flip on their own; translateX(), left, and right don't.
- [Localisation testing checklist for multilingual apps](https://generaltranslation.com/en-GB/kb/multilingual-app-launch-checklist.mdx): Test nine things in every supported locale on a production build: whole journeys including error states and emails, language switching, plurals, currency and time zones, text expansion, right-to-left layout, accessibility, translation delivery, and crawlable language URLs. Give each check an explicit pass condition.
- [Internationalisation vs. localisation (i18n vs. l10n)](https://generaltranslation.com/en-GB/kb/internationalization-vs-localization.mdx): Internationalisation (i18n) is the code and design work that lets one product support many locales, such as formatting dates with Intl and supporting right-to-left text. Localisation (l10n) adapts it for one locale, which covers translation plus currency, date formats, units, tone, and legal details.

## Previous and next questions

- Previous question: [What is ICU message format?](https://generaltranslation.com/en-GB/kb/icu-message-format.mdx)
- Next question: [How do I add i18n to a Next.js App Router app?](https://generaltranslation.com/en-GB/kb/add-i18n-to-nextjs.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/en-GB/kb/sitemap.md) for every article.
