Skip to content

Catch text truncation and overflow in CI ​

German and Finnish run 30 to 40% longer than English. A button that fits "Save changes" cuts off "Änderungen speichern", and a tab label that fits "Billing" spills over its neighbour in Finnish. These bugs usually surface after the translations ship, when nobody on the team reads the language that broke.

fitlocale finds them before any translation exists, in the Playwright tests you already have, and fails the pull request that introduces them.

How it works ​

localeCheck() rewrites every visible text on the page into a longer, accented version, about 40% longer:

Save changes   →   ⟦Šávé çháñgéš ~~~~⟧

Then it measures every element with text:

  • overflow/clipped: text that no longer fits and is cut off by overflow: hidden;
  • overflow/ellipsis: text newly truncated with an ellipsis or a line clamp;
  • overflow/escapes-box: text spilling outside its box;
  • overflow/overlap: buttons, links or inputs that now overlap.

It then puts the page back exactly as it was, so your test can carry on.

Problems that already exist in your source language are left out. A title truncated on purpose with an ellipsis in English is not reported when it is truncated in the longer text too. So a report only lists what translation will break.

Write the test ​

Add the reporter to playwright.config.ts, as in Getting started, then call localeCheck() on the pages you care about:

ts
test('home page: text that no longer fits', async ({ page, localeCheck }) => {
  await page.goto('/');
  await expect(page.locator('html')).toHaveAttribute('data-ready', '1'); // your app has rendered
  await localeCheck(); // checks at 1280px and 390px wide, with text about 40% longer
});

On the demo app, this finds a save button that cuts off its label, a tab label that spills out, and two toolbar buttons that overlap. The ellipsis on the long page title is left out, because it is there in English too.

Narrow screens ​

Text that fits on a laptop often breaks on a phone. By default every check runs at 1280 px and 390 px wide, then restores the window size. Choose your own widths with the viewports option:

ts
test.use({ fitlocale: { viewports: [{ width: 1440, height: 900 }, { width: 360, height: 780 }] } });

For languages that run longer than German, raise expansion, for example to 0.6.

In your real locales ​

Once translations exist, check the languages you ship as well. Point localeMessages at your message files and switch the page with useLocale():

ts
test.use({
  fitlocale: {
    sourceMessages: 'demo/locales/en.json',
    localeMessages: 'demo/locales/{locale}.json', // de → demo/locales/de.json
    setLocale: byLocalStorage('lang'),
  },
});

test('home page in German: text the translation no longer fits', async ({ page, useLocale, localeCheck }) => {
  await page.goto('/');
  await useLocale('de');
  await expect(page.locator('html')).toHaveAttribute('data-ready', '1');
  await localeCheck(); // reports only what overflows in German and not in English
});

fitlocale puts the English text back wherever it finds a translation in your messages, measures again, and reports only what overflows in German and not in English, so a truncation that is there by design stays out of the report. Text it cannot find in your messages, such as user names, is left out rather than guessed.

On the demo app, this finds the German save button whose label no longer fits.

An app that already has issues ​

The first run reports everything it finds, but fails no test: there is no baseline file yet. Accept the current issues, then commit the file:

bash
npx fitlocale baseline fitlocale-report/results.json
git add fitlocale-baseline.json

From then on, only new issues fail. Accepted ones stay in the report, marked "accepted". Run the command again after you fix some, to drop them from the file. It accepts whatever that run found, new issues included, so run it on a run you have reviewed.

In GitHub Actions ​

Run your tests, then add the fitlocale step with if: always(), so it reports when the tests fail too:

yml
# .github/workflows/e2e.yml: Playwright tests with fitlocale on every pull request.
name: End-to-end tests
on:
  pull_request:
  push:
    branches: [main]

jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npx playwright test
      # A table of new issues in the job summary, and the HTML report as an artifact.
      - uses: melliforge/fitlocale@v0
        if: always()

Each run then shows a table of new issues in its summary, with the full HTML report as a downloadable artifact. In a matrix or with shards, give each job its own artifact-name.

What fails the build ​

Overflow issues are errors, and ellipsis truncation is a warning. By default only errors fail a test. Set failOn to 'warning' to fail on truncation too, or to 'never' to report only.

Next ​

Released under the MIT License.