---
title: "String Translation - Documentation - PerfLocale"
description: "Translate gettext strings from themes and plugins without code changes. Covers scanning, file vs database storage, and the Strings admin page."
canonical: "https://perflocale.com/docs/string-translation/"
source: "https://perflocale.com/docs/string-translation/"
format: "markdown"
---
# String Translation

String Translation lets you override gettext strings emitted by themes and plugins from inside WordPress - no editing `.po`/`.mo` files, no rebuilding, no lost changes after updates. PerfLocale scans PHP source for `__()`, `_e()`, `_x()`, `_n()`, and every other WordPress i18n function, stores what it finds, and serves your translations through the standard `gettext` filter.

## What it handles

-   `__()`, `_e()`, `esc_html__()`, `esc_attr__()`, `esc_html_e()`, `esc_attr_e()` - simple strings
-   `_x()`, `_ex()`, `esc_html_x()`, `esc_attr_x()` - context-disambiguated strings
-   `_n()`, `_nx()`, `_n_noop()`, `_nx_noop()` - plural forms, including languages with three to six CLDR plural forms (e.g. Russian, Polish, Arabic), not just a two-form `n == 1` / else split

## Scanning

The scanner runs from **PerfLocale → Strings → Scan for Strings** (top-right button). It walks the parent theme, the child theme (if any), every _active_ plugin directory and `mu-plugins/` — PerfLocale’s own directory is deliberately skipped so its strings stay out of your catalogue — reads every PHP file, and inserts newly-found strings into the `perflocale_strings` table. Existing strings are preserved; only new ones are added. Files larger than 2 MB are skipped to avoid memory spikes, and `vendor/`, `node_modules/`, `.git/`, `tests/`, `test/`, `cache/`, `build/` and `dist/` are excluded by default (filter `perflocale/strings/scanner/excluded_paths`).

A full scan on a site with many active plugins can take a minute or two. PerfLocale dispatches the scan via its [background-jobs system](https://perflocale.com/docs/background-jobs/) — you'll be redirected to **PerfLocale → Jobs** where progress and any errors are visible live. Smaller scans (targeted at a single plugin / theme directory) still run inline.

You can also run the scanner from the command line. The CLI scans one directory at a time and defaults to the active theme, so pass `--dir` to point it elsewhere:

```
# Active theme (default).
wp perflocale strings scan

# A specific plugin, restricted to one text domain.
wp perflocale strings scan --dir=wp-content/plugins/my-plugin --domain=my-plugin
```

## Domains and contexts

Every string has a **text domain** (usually a theme or plugin slug) and an optional **context**. Filter the Strings page by domain (e.g. only translate `woocommerce` strings) and combine with context when the same word has different meanings (e.g. `Post` as a noun vs. a verb).

## Translating a string

Click any row on the Strings page. The editor opens with the original on the left and one editable field per active language on the right. Plural strings appear as two rows — singular text and plural text — and for languages with more than two plural forms the plural row adds an extra field per grammatical form. Save - the translation takes effect immediately on frontend pages. Machine translation is available via the _MT_ button if you’ve configured a provider.

## Plural forms

WordPress plural calls — `_n()`, `_nx()`, and the `_n_noop()` / `_nx_noop()` variants — are served with each language’s full CLDR plural rules, not just a two-form `n == 1` / else split. Each language picks its own form set at runtime through WordPress core’s `\Plural_Forms`: Japanese has 1 form, English and German 2, Russian and Polish 3, Arabic 6.

For languages with more than two forms, the plural row on the Strings screen shows one extra input per grammatical form, so a translator fills in each CLDR category (Polish _few_ vs. _many_, for example) directly. Multi-form `.po` files import correctly — every `msgstr[…]` form is stored.

PO round-trip: forms beyond the standard two are **imported** from multi-form `.po` files, **editable per-form** in the Strings screen, and **preserved** across a PO export followed by a re-import. The flat `.po` export carries the standard two forms (singular + plural); those extra forms aren’t written into the exported file, but re-importing that file no longer wipes them. For a fully lossless backup of three-or-more-form translations, use the JSON data export rather than the `.po` file.

## Storage modes: database vs files

PerfLocale supports two storage backends for string translations, selectable at **Settings → Performance**:

-   **Files** (default) - generates `.l10n.php` translation files (the format WP 6.5+ prefers) in `wp-content/uploads/perflocale/translations/`. Same speed as a native language pack; zero DB reads for gettext lookups; regenerated automatically when you edit a string.
-   **Database** - translations live in `perflocale_string_translations` with a 3-layer cache in front. Simple; every edit is instantly live; portable across hosts via the Export feature.

### Regenerate Translation Files

If you ever see _Tools → Site Health_ reporting _“Some translations need to be re-linked”_ or _“Compiled translation files are missing”_, the fix is a single click: **Settings → Performance → Regenerate Translation Files**. That button does three things in one pass:

1.  **Self-heal.** Any saved translation that has lost its connection to the file-generation pipeline (orphaned `string_translations` row, string with no group, or a missing `translation_links` row) is reconnected automatically before generation starts. The completion notice reports how many translations were reconnected on this run.
2.  **Clean.** Stale `.l10n.php` files are removed.
3.  **Generate.** Fresh files are written, one per (domain × locale) pair.

In normal operation files regenerate automatically on every translation save via the `perflocale/strings/regenerate_files` hook, so you rarely need to click the button. The manual path exists for two cases: _(a)_ recovering from a partial import or a re-scan that renumbered strings, and _(b)_ after editing translations directly in the database (e.g. via a migration script that bypassed the normal save flow).

## Import / export

Export your string translations as standard `.po` files straight from **PerfLocale → Strings** — the _Export PO_ and _Import PO_ buttons sit beside the page heading, one language at a time — or from the command line with `wp perflocale po-export` / `po-import`. The full JSON export at **Settings → Export & Import** carries the same data under its _String Translations_ section. See [Export & Import](https://perflocale.com/docs/export-import/).

Plural forms are preserved across a PO export and re-import — the forms beyond the standard two are never lost — and multi-form PO files import every form. For a fully lossless backup of three-or-more-form languages, use the JSON data export.

## Related

-   [Machine Translation](https://perflocale.com/docs/machine-translation/) - auto-translate untranslated strings.
-   [WP-CLI Commands](https://perflocale.com/docs/wp-cli/) - `wp perflocale strings scan`, `translate`, `export`.
-   [Production Tuning](https://perflocale.com/docs/production-tuning/) - string-translation caching strategy.

[← Back to Docs](https://perflocale.com/docs/)
