WP-CLI Commands
PerfLocale provides WP-CLI commands for managing languages, translations, strings, caches, and database integrity from the command line.
Namespace: wp perflocale
Languages
List Languages
wp perflocale languages listDisplays all configured languages in a table.
Options:
--format=<format>- Output format:table(default),json,csv,yaml.
Example:
$ wp perflocale languages list
+----+------+--------+-----------+---------+--------+-----------+
| ID | Slug | Locale | Name | Default | Active | Direction |
+----+------+--------+-----------+---------+--------+-----------+
| 1 | en | en_US | English | No | Yes | ltr |
| 2 | fr | fr_FR | French | Yes | Yes | ltr |
| 3 | de | de_DE | German | No | Yes | ltr |
| 4 | ar | ar | Arabic | No | Yes | rtl |
+----+------+--------+-----------+---------+--------+-----------+
$ wp perflocale languages list --format=json
[{"ID":"1","Slug":"en","Locale":"en_US",...}]Add Language
wp perflocale languages add <locale> [--default]Adds a new language.
Arguments:
<locale>(required) - WordPress locale code (e.g.fr_FR,es_ES,ar).
Options:
--default- Set as the default language.
RTL languages (Arabic, Hebrew, Farsi, Urdu, etc.) are auto-detected.
Examples:
# Add French
$ wp perflocale languages add fr_FR
Success: Language fr_FR added with ID 7 (direction: ltr).
# Add Arabic (RTL auto-detected)
$ wp perflocale languages add ar
Success: Language ar added with ID 8 (direction: rtl).
# Add as default language
$ wp perflocale languages add en_US --defaultDelete Language
wp perflocale languages delete <slug>Deletes a language. Prompts for confirmation. Cannot delete the default language.
Arguments:
<slug>(required) - Language slug (e.g.fr,de).
Example:
$ wp perflocale languages delete fr
Warning: 42 translation links use this language. They will be orphaned.
Are you sure you want to proceed? [y/n] y
Success: Language 'fr' deleted.Safety features:
- Warns if translations exist for this language.
- Requires explicit confirmation (use
--yesto skip in scripts). - Blocks deletion of the default language.
Translate
Single Post
wp perflocale translate <post_id> --to=<lang> [--provider=<provider>]Translates a single post using machine translation.
Arguments:
<post_id>(required) - The post ID to translate.
Options:
--to=<lang>(required) - Target language slug.--provider=<provider>- Provider:deepl,google,microsoft,libretranslate. Uses configured default if omitted.
Example:
$ wp perflocale translate 42 --to=de
Translating post 42 to de...
Success: Translated! New post ID: 99Bulk Translation
wp perflocale translate --all --to=<lang> [--post-type=<type>] [--skip-existing]
wp perflocale translate --post-ids=<ids> --to=<lang> [--skip-existing]Translates multiple posts with a progress bar.
Options:
--all- Translate all published posts of the given type.--post-type=<type>- Post type for--all. Default:post.--post-ids=<ids>- Comma-separated list of post IDs.--to=<lang>(required) - Target language slug.--skip-existing- Skip posts that already have a translation in the target language.--provider=<provider>- Translation provider.
Examples:
# Translate all posts to German, skipping already translated
$ wp perflocale translate --all --to=de --post-type=post --skip-existing
Translating to de 100% [============================] 50 / 50
Success: Done. Translated: 38, Skipped: 12, Failed: 0.
# Translate specific posts to French
$ wp perflocale translate --post-ids=42,43,44 --to=fr
Translating to fr 100% [============================] 3 / 3
Success: Done. Translated: 3, Skipped: 0, Failed: 0.
# Translate all WooCommerce products
$ wp perflocale translate --all --to=fr --post-type=product --skip-existingNotes:
- Machine translation must be enabled in Settings → Machine Translation.
- Failed translations are reported individually but don't stop the batch.
- Progress bar shows real-time progress.
Translations
List Translations
wp perflocale translations list <post_id> [--format=<format>]Shows all translations linked to a post.
Example:
$ wp perflocale translations list 42
+----------+---------+--------------------+---------+
| Language | Post ID | Title | Status |
+----------+---------+--------------------+---------+
| EN * | 42 | Hello World | publish |
| FR | 160 | Bonjour le monde | publish |
| DE | 165 | Hallo Welt | publish |
+----------+---------+--------------------+---------+The * marks the current post's language.
Create Translation
wp perflocale translations create <post_id> --to=<lang>Creates a new translation post (copies content from source).
Example:
$ wp perflocale translations create 42 --to=fr
Success: Translation created. New post ID: 200Delete Translation
wp perflocale translations delete <post_id> --lang=<slug>Permanently deletes a translation post. Prompts for confirmation.
Example:
$ wp perflocale translations delete 42 --lang=fr
Are you sure you want to proceed? Delete fr translation (post 200)? [y/n] y
Success: Translation deleted (post 200).Set Language
wp perflocale translations set-language <post_id> --lang=<slug>Changes the language of an existing post. Fails if the language is already used by a sibling in the same translation group.
Example:
$ wp perflocale translations set-language 42 --lang=en
Success: Post 42 language set to en.
$ wp perflocale translations set-language 42 --lang=fr
Error: Failed to set language. The language may already be used by a sibling translation.Strings
Scan for Strings
wp perflocale strings scan [--domain=<domain>] [--path=<path>]Scans PHP files for translatable strings (__(), _e(), _x(), _n(), _nx(), _n_noop()).
Options:
--domain=<domain>- Only scan strings for a specific text domain.--path=<path>- Directory to scan. Default: active theme directory.
Examples:
# Scan active theme
$ wp perflocale strings scan
Scanning /var/www/html/wp-content/themes/flavor/flavor...
Success: Found 342 strings, inserted 15 new.
# Scan a specific plugin
$ wp perflocale strings scan --path=/var/www/html/wp-content/plugins/woocommerce --domain=woocommerce
Scanning /var/www/html/wp-content/plugins/woocommerce...
Success: Found 4821 strings, inserted 4821 new.Performance:
- Batched inserts (500 per batch) - handles large codebases.
- Skips files over 2 MB to prevent memory exhaustion.
- Excludes
vendor/,node_modules/,.git/,tests/,build/,dist/.
Slugs
Backfill Slug Translations
wp perflocale slugs backfillPopulates the slug translation table for all existing term translation groups. This ensures that translated taxonomy URLs work correctly.
When to run:
- After migrating from another multilingual plugin.
- After creating term translations manually.
- If taxonomy archive URLs show 404 errors.
Example:
$ wp perflocale slugs backfill
Success: Backfilled 48 slug translation entries.Verify Slug Translations
wp perflocale slugs verifyChecks if all terms in translation groups have corresponding slug translation entries.
Example:
$ wp perflocale slugs verify
Success: All term translations have slug entries.
$ wp perflocale slugs verify
Warning: 12 terms are missing slug translations. Run 'wp perflocale slugs backfill' to fix.Cache
Flush Caches
wp perflocale cache flushFlushes all three cache layers:
- L1: In-memory static cache.
- L2: WordPress object cache (Redis/Memcached if available).
- L3: Transients (wp_options table).
Example:
$ wp perflocale cache flush
Success: All PerfLocale caches flushed.Migrate
Migrate from another multilingual plugin
wp perflocale migrate <source> [--dry-run] [--yes]Imports translation data from another multilingual plugin into PerfLocale’s own tables. The source plugin’s data is not modified or deleted - the importer is read-only on the source side and writes new rows on the PerfLocale side.
Arguments:
<source>(required) - one ofwpml,polylang,translatepress.
Options:
--dry-run- confirm the source is detected and importable without writing anything.--yes- skip the interactive “Are you sure?” prompt (use for scripting / CI).
Examples:
# Confirm WPML data is detected.
$ wp perflocale migrate wpml --dry-run
Dry run - checking wpml data...
Success: Wpml data detected and ready to import. Run without --dry-run to proceed.
# Run the real import, prompt for confirmation.
$ wp perflocale migrate wpml
Migrate translation data from wpml? This cannot be undone. [y/n] y
Migrating from wpml...
Success: Migration complete. Imported: 234 posts, 89 terms, 1412 strings.
# Scripted (no prompt).
$ wp perflocale migrate polylang --yes
Migrating from polylang...
Success: Migration complete. Imported: 128 posts, 34 terms.Per-source guides: WPML • Polylang • TranslatePress. For the shared pre-flight steps and safety model, see the migration overview.
Memory tuning on very large sites: all three importers chunk their source-table SELECTs. The default is fine up to tens of thousands of posts (32 MB peak in our 5,000 group × 3 language benchmark). On tighter memory_limit hosts you can shrink the chunk size with perflocale/migration/wpml/batch_size, perflocale/migration/polylang/batch_size, or perflocale/migration/translatepress/batch_size filters.
Database Migrations
Run pending schema migrations
wp perflocale db-migrateApplies any pending PerfLocale database schema migrations. Idempotent - safe to run multiple times. Useful in CI/CD pipelines or headless environments where the WordPress admin dashboard is never visited (since the migrator normally runs on admin_init).
Example:
$ wp perflocale db-migrate
Success: PerfLocale database migrations completed successfully.Health Check
Run Database Integrity Checks
wp perflocale health-check [--fix]Checks for database inconsistencies and optionally repairs them.
Options:
--fix- Automatically fix found issues.
Checks performed:
- Orphaned post links - Translation links pointing to deleted posts.
- Orphaned term links - Translation links pointing to deleted terms.
- Invalid language references - Links referencing deleted languages.
- Widow translation groups - Non-string groups with zero remaining links. String-type groups are preserved (they are valid with no links until a translation is saved).
- Groupless orphan links - Translation links whose parent group row no longer exists.
Since 1.0.16, --fix delegates to the shared cleanup_orphans() method on the translation-group repository, so the CLI and the internal sweep produce identical results. Running without --fix is strictly read-only.
Examples:
# Report only
$ wp perflocale health-check
Running PerfLocale health checks...
Orphaned post links: 0
Orphaned term links: 3
Invalid language references: 0
Widow translation groups: 5
Groupless orphan links: 2
Warning: Found 10 issues. Run with --fix to repair.
# Auto-fix
$ wp perflocale health-check --fix
Running PerfLocale health checks...
Orphaned post links: 0
Orphaned term links: 3
Fixed: removed 3 orphaned term links.
Invalid language references: 0
Widow translation groups: 5
Fixed: removed 5 widow groups.
Groupless orphan links: 2
Fixed: removed 2 groupless orphan links.
Success: Fixed 10 issues.Status
Translation Completeness Overview
wp perflocale status [--format=<format>]Displays translation completeness per language.
Options:
--format=<format>- Output format:table(default),json,csv.
Example:
$ wp perflocale status
+---------------------+-----------+-------+-------+-------+
| Language | Published | Draft | Empty | Total |
+---------------------+-----------+-------+-------+-------+
| English (en) | 12 | 2 | 0 | 14 |
| French (fr) | 14 | 0 | 0 | 14 |
| German (de) | 8 | 1 | 5 | 14 |
| Arabic (ar) | 3 | 0 | 11 | 14 |
+---------------------+-----------+-------+-------+-------+
$ wp perflocale status --format=json
[{"Language":"English (en)","Published":12,...}]Export & Import
Export a single site
wp perflocale export <file>Writes a single JSON envelope containing every PerfLocale-managed surface for the current site - languages, translation links, string translations, glossary terms, slugs, settings, role caps, workflow state. Format auto-detected from the extension; the envelope embeds a format_version field so older importers will refuse mismatched payloads.
$ wp perflocale export /tmp/perflocale-backup.json
Success: Wrote 1.4 MB envelope (format_version 2) to /tmp/perflocale-backup.jsonImport into a single site
wp perflocale import <file> [--mode=<mode>]Options:
--mode=merge(default) - additive; existing rows are kept, new rows are inserted. Conflicts resolved by re-mapping foreign keys via id-maps so cross-site references stay intact.--mode=replace- truncates the target tables first (scoped to the current site on multisite) and then writes the envelope verbatim.
# Restore from backup, additive
wp perflocale import /tmp/perflocale-backup.json
# Replace everything PerfLocale owns on this site with the envelope
wp perflocale import /tmp/perflocale-backup.json --mode=replace --yesExport string translations as PO
wp perflocale po-export <file> --lang=<slug> [--domain=<domain>]Writes a gettext PO file for one language. Optional --domain filters to a single text domain (default: all domains).
$ wp perflocale po-export /tmp/fr.po --lang=fr
Success: Exported 1,243 strings to /tmp/fr.po
$ wp perflocale po-export /tmp/fr-myplugin.po --lang=fr --domain=my-pluginImport string translations from PO
wp perflocale po-import <file> --lang=<slug> [--replace]Options:
--replace- replaces the existing translation for each msgid in this language. Without it, only new msgids are inserted; existing translations are left untouched.
Robust to \r\n line endings and gracefully ignores fuzzy / obsolete entries.
wp perflocale po-import /tmp/fr.po --lang=fr
wp perflocale po-import /tmp/fr.po --lang=fr --replaceExport glossary as CSV
wp perflocale glossary-export-csv <file>Writes the full glossary to RFC 4180 CSV (source term, language pair, translation, notes, case-sensitive flag).
wp perflocale glossary-export-csv /tmp/glossary.csvImport glossary from CSV
wp perflocale glossary-import-csv <file> [--replace]Options:
--replace- clears the existing glossary first, then imports. Without it, new rows are appended and duplicate (source, language pair) entries are skipped.
wp perflocale glossary-import-csv /tmp/glossary.csv
wp perflocale glossary-import-csv /tmp/glossary.csv --replaceExport a multisite network
wp perflocale network-export <file> [--include-inactive]Multisite only. Walks every site in the network and writes a single envelope keyed by blog ID, marked network: true. Inactive sites are skipped unless --include-inactive is passed.
wp perflocale network-export /tmp/network.json
wp perflocale network-export /tmp/network.json --include-inactiveImport a multisite network
wp perflocale network-import <file> [--mode=<mode>] [--site=<id>]Options:
--mode=merge(default) or--mode=replace- same semantics as single-siteimport, applied per site.--site=<id>- on a single-site install, pick which slice of a network envelope to load. Required when loading a network envelope outside multisite.
# Across the whole network (multisite)
wp perflocale network-import /tmp/network.json --mode=replace
# Restore one slice on a non-multisite target
wp perflocale network-import /tmp/network.json --site=42Addons
Inspection-focused subcommands for the Addon System. Every command is read-only except migrate and reset-version; there is no uninstall subcommand by design.
List Addons
wp perflocale addon list [--format=<format>] [--all]Options:
--format=table|json|csv|yaml(default:table).--all- include orphan manifests (addons whose class is gone but whose manifest is still in the database).
wp perflocale addon list
wp perflocale addon list --format=json --allInspect a Single Addon
wp perflocale addon info <id> [--format=<format>]Shows the addon's full manifest plus a live PurgePlan preview (counts only — no DB mutation). Use this to verify that a custom addon's tables, options, and capabilities are namespaced correctly before relying on the uninstall contract.
wp perflocale addon info my-addon
wp perflocale addon info my-addon --format=jsonList Orphans
wp perflocale addon orphansLists addon IDs that still have a manifest row but whose registering class is no longer loaded — typically because the addon's plugin was deactivated or deleted before PerfLocale itself was uninstalled.
Run Addon Migrations
wp perflocale addon migrate [<id>] [--force]Options:
<id>- optional; migrate just this addon (default: all).--force- reset the stored schema version to0and re-run every migration step. Dangerous.
wp perflocale addon migrate
wp perflocale addon migrate my-addon
wp perflocale addon migrate my-addon --forceMigration / Uninstall Errors
wp perflocale addon errors [<id>] [--clear]Tail the addon migration / uninstall error log. Pass an addon ID to filter, or --clear to empty the log (optionally for one addon).
wp perflocale addon errors
wp perflocale addon errors my-addon
wp perflocale addon errors --clear
wp perflocale addon errors my-addon --clearReset Stored Schema Version
wp perflocale addon reset-version <id> <version> [--yes]Manual recovery only — sets the stored schema version for one addon to a specific value. Setting it lower than the addon's current target causes the next migration pass to re-run every step in between. Confirms before writing unless --yes is passed.
wp perflocale addon reset-version my-addon 0
wp perflocale addon reset-version my-addon 2 --yesBackground Jobs
Inspect and control the background-processing queue from the command line. See the Background Jobs doc for the full feature reference.
Namespace: wp perflocale jobs
List Jobs
wp perflocale jobs list [--status=<status>] [--format=<format>]Lists all jobs in the active-jobs index.
Options:
--status=<status>— Filter by status:queued,running,complete,failed,canceled.--format=<format>— Output format:table(default),json,csv,yaml.
Example:
$ wp perflocale jobs list
+--------------------------------------+--------------+----------+----------+--------------------+
| id | type | status | progress | updated_at |
+--------------------------------------+--------------+----------+----------+--------------------+
| 5fc94964-29f6-4697-8c4e-64a8ffb5c72d | string_scan | running | 47% | 12 seconds ago |
| 9d291e3c-3a41-47ab-b3e6-3ba00e88e55a | data_import | queued | 0% | 8 seconds ago |
+--------------------------------------+--------------+----------+----------+--------------------+
$ wp perflocale jobs list --status=failed --format=json
[{"id":"...","type":"glossary_import","status":"failed",...}]Get Job Details
wp perflocale jobs get <id> [--format=<format>]Prints the full state of one job: type, hook, engine, status, progress, args, log ring buffer, error message, result payload, attempts, created_at / completed_at / updated_at, created_by user id.
Arguments:
<id>(required) — Job UUID (visible injobs list).
Options:
--format=<format>— Defaultyaml(one key:value per line);jsonfor machine-readable.
Cancel a Job
wp perflocale jobs cancel <id>Cancels a queued or running job. Sets status to canceled and unschedules the worker event. A worker that's currently mid-execute() aborts cooperatively on its next progress callback — the delay until that happens depends on the job's batch size (most jobs tick every batch, so detection lag is typically milliseconds to a few seconds). Very short jobs that complete before any progress tick may still finish naturally.
Refuses jobs that are already complete / failed / canceled with a warning.
Retry a Job
wp perflocale jobs retry <id>Re-enqueues a failed or canceled job. Resets status to queued, clears the error and result fields, schedules a new worker event with the original args + hook.
Refuses non-terminal jobs (queued / running / complete) with a warning.
Delete a Job
wp perflocale jobs delete <id>Permanently removes a job from the active-jobs index and deletes its per-job state row. Only allowed on jobs that have already finished (complete, failed, canceled) — running/queued jobs must be canceled first so the runner has a chance to unschedule cleanly. Matches the same restriction as the REST DELETE /jobs/{id} endpoint.
Pause / Unpause the Queue
wp perflocale jobs pause
wp perflocale jobs unpauseToggles the background_paused setting. While paused, new dispatches are accepted but workers immediately re-queue them every 5 minutes (configurable) instead of running. Operator brake when something is mis-dispatching.
Run Garbage Collection
wp perflocale jobs gcRuns the daily GC sweep right now without waiting for the cron tick. Reports how many jobs were pruned. Performs:
- Stuck-job sweep — queued/running jobs with no
updated_atbump in 6+ hours are marked failed. - Terminal-state prune — complete/failed/canceled jobs older than 24h are deleted.
- Stale lock sweep —
perflocale_job_lock_*andperflocale_type_lock_*rows with past expiry are deleted.
Resume Surviving Jobs
wp perflocale jobs resumeManually trigger the post-reactivation resume sweep that normally runs automatically on activation. Walks the active-jobs index, finds queued/running rows that have no scheduled worker event, and re-enqueues each one. Useful if the auto-scheduled perflocale_resume_jobs cron event was lost (e.g. DISABLE_WP_CRON + no external trigger between activate and the operator noticing). Reports the count re-enqueued.
CLI runs synchronously — admin honors thresholds
The Background Thresholds setting in Settings → Performance only gates admin-triggered operations: when an operator clicks Scan, Import, Migrate, etc., or when an external client hits the REST endpoint, the dispatcher compares the work size against the threshold and routes large operations through the queue.
CLI commands deliberately bypass that gate and run the work inline in the foreground, regardless of size. This matches Unix conventions for command-line tools: progress streams to stdout, errors stream to stderr, exit code reflects success, and chained scripts (wp ... && next-step) behave as expected. Deploy scripts, CI/CD pipelines, and ssh-only servers all rely on this synchronous behavior.
Operations affected: strings scan, translate, export, import, migrate <plugin>, glossary-import-csv. They will not queue jobs even on very large workloads — you'll see the work happen live and the command will return when it's done.
If you need to dispatch a large operation through the queue from a CLI context (e.g. a server cron that should kick off a job and exit immediately so it doesn't tie up the cron worker), use the dispatcher directly:
wp eval '\PerfLocale\Background\Dispatcher::dispatch(
new \PerfLocale\Background\Jobs\BulkTranslateJob(),
[ "source_ids" => [ 1, 2, 3 ], "target_lang_ids" => [ 2 ] ]
);'That returns a job UUID immediately and exits; the queued job runs on the next worker tick (WP-Cron or Action Scheduler, whichever is active).
Scripting Tips
Suppress Confirmation Prompts
Use --yes to skip all confirmation prompts (useful in scripts and CI):
wp perflocale languages delete fr --yes
wp perflocale translations delete 42 --lang=fr --yesCombine with WordPress CLI
# Translate all published pages to German
wp perflocale translate --all --to=de --post-type=page --skip-existing
# Check health and fix issues
wp perflocale health-check --fix
# Full maintenance routine
wp perflocale cache flush && wp perflocale slugs backfill && wp perflocale health-check --fixJSON Output for Parsing
# Get languages as JSON for scripting
wp perflocale languages list --format=json | jq '.[].Slug'
# Get translation status as JSON
wp perflocale status --format=json | jq '.[] | select(.Published < .Total)'