Documentation

Pseudo-localization

Render your pages with a fake translation, accented, longer and reversed for right-to-left languages. You see text the catalogue missed, labels cut off and layout that doesn't mirror, before anyone translates.

A pseudo translation replaces every string with a recognizable fake. In a left-to-right language, each letter is accented and the string grows inside brackets: Save changes becomes ⟦Šáṽé çĥáñĝéš··········⟧. In a right-to-left language, each string is reversed with a direction override, so the page reads right to left even before Arabic or Hebrew exists.

On a page rendered this way, three problems become visible:

  • Plain text did not come from the catalogue: it is written into the code or built at run time.
  • Cut-off text did not leave room for longer languages.
  • Layout that stays put in a right-to-left page uses left and right instead of start and end.
German · pseudo translation/de-DE/
Your basket⟦Ýóúŕ ƀášķéţ·········⟧

Free shipping on every order

Save changes⟦Šáṽé çĥáñĝéš··········⟧

Prices include tax.⟦Þŕíçéš íñçļúðé ţáẋ.················⟧

Arabic · pseudo translation/ar-SA/
Your basket‮Your basket‬

Free shipping on every order

Save changes‮Save changes‬

Prices include tax.‮Prices include tax.‬

What the probe found
  1. Text that did not come from the catalogue/{locale}/ · p#promo · “Free shipping on every order” · runtime_text (de-DE, ar-SA)
  2. Text cut off when it grows/{locale}/ · button#save · “⟦Šáṽé çĥáñĝéš··········⟧” · clipped_text (de-DE)
  3. Layout that does not mirror right to left/{locale}/ · p#note · “padding-left: 12px” · unmirrored_layout (ar-SA)

What the pseudo-localized pages show: the same page in German, with every string accented and longer, and in Arabic, reversed right to left. The probe marks text written into the code, a button that cuts its text off and a rule that stays on the left. Illustrative data.

In the Globalization check

When the configuration file says how to start the app, the Globalization check renders its pages on every change it checks. It works on a copy of the change in the check's own sandbox, so the fake translation never reaches your repository or other checks:

  1. It writes the pseudo translation into one left-to-right and one right-to-left language of the project.
  2. It installs the dependencies and starts the app.
  3. It opens every page in both languages in a browser set to that language, and reads what is on screen.
  4. It stops the app and deletes the copy.

What it finds becomes findings of the check:

CodeWhat it finds
runtime_textVisible text without the pseudo marks
clipped_textPseudo text cut off by its box, with overflow hidden or an ellipsis
unmirrored_layoutIn a right-to-left page: a page that is not right to left, text aligned or floated left or right, or an element placed, margined or padded by left or right

The same text found in both languages is one finding. The findings appear in the task's Issues found as the page, the element, the text and the languages, such as /{locale}/ · p#promo · “Free shipping on every order” · runtime_text (de-DE, ar-SA).

They are reported and block nothing until you choose. Under Quality Control → Globalization, Render pages turns the step off, and Page problems that send a change back for repair chooses which codes block.

An app that does not install or start is reported as not rendered, with its output, never as a clean render. It holds the change only when you block page problems.

Set it up

Add world.render to .coroid/i18n.json:

{
  "world": {
    "render": {
      "start": "npm run dev",
      "pages": ["/{locale}/", "/{locale}/pricing", "/{locale}/checkout"]
    }
  }
}
FieldMeaning
startStarts the app. It must listen on the port in $PORT; HOST is 127.0.0.1
pagesUp to ten paths, from the app's root. {locale} becomes the language's tag, so /{locale}/pricing opens /de-DE/pricing; use /?lang={locale} if the language is a query parameter
directoryThe app's folder in the repository, when it is not the root
installThe command that installs dependencies. Without it, Coroid runs npm when a package.json declares any
localesUp to three target languages to render. Without it, the first left-to-right and the first right-to-left target
readyTimeoutMsHow long the app may take to answer, 120,000 by default

The browser also sends each language as its preferred language, so an app that picks its language from the browser works without {locale}.

The field needs CLI 1.16.0 or later in the configuration's cli pin; an older engine refuses the configuration.

What is left out

The check reads only what a person sees. It skips hidden elements, code (code, pre, kbd, samp), form fields' own values, and elements marked translate="no", aria-hidden="true", data-i18n-ignore or with the class notranslate. Words in the configuration's world.allow list, such as product names, are never reported as plain text.

Text that comes from your own data, such as product names or user content, is not translated by the catalogue either. Mark its container translate="no" or data-i18n-ignore, which also tells browser translation tools to leave it alone.

Try it yourself

The pseudo translation is part of the command-line tool. Run it in a throwaway checkout, because it replaces the language's translations:

coroid-i18n pseudo --locale de-DE                # accented and longer
coroid-i18n pseudo --locale ar-SA                # reversed, right to left
coroid-i18n pseudo --locale de-DE --style rtl    # choose the style yourself

Then start the app and open its pages in that language. Placeholders, plural forms and markup stay intact, so the app works as usual. Markdown and MDX pages are left as they are.