Export & Import

Translations need to travel: between staging and production, to an external translation agency, to a colleague’s computer for review, to backup storage. PerfLocale supports three formats covering every common use case - pick the one that fits the receiver.

Formats

  • XLIFF 2.0 - the industry-standard translation exchange format. Open in any CAT tool (SDL Trados, memoQ, Phrase, Crowdin, etc.) to hand off work to professional translators. Post content, titles, excerpts, + meta fields marked translatable are exported as <unit> / <segment> elements per the XLIFF 2.0 schema (urn:oasis:names:tc:xliff:document:2.0).
  • .po / .mo - classic gettext format. Best for exporting string translations (from PerfLocale → Strings) to edit in Poedit or Loco, or to ship as a language pack alongside a theme/plugin.
  • JSON - PerfLocale’s own format. Round-trips every plugin-specific field (translation link groups, workflow state, glossary, etc.) with no loss. Use for site-to-site migration or full backups.

Exporting via the admin UI

At Settings → Export & Import:

  1. Choose what to export (translations / strings / glossary / language definitions / all).
  2. Optionally filter by post type + language pair.
  3. Pick a format (XLIFF / PO-MO / JSON).
  4. Click Download. Small exports stream directly to your browser; large ones queue as a data_export background job and the admin redirects to PerfLocale → Jobs. When the job completes, a single-use Download button appears on the job row — click it and the export file is streamed to your browser and deleted from the server in the same request. The async/sync split is governed by the data_export threshold under Settings → Performance → Background Thresholds (default: 5,000 translation rows).

Importing via the admin UI

Same page, Import section. Upload a file in any of the supported formats. PerfLocale auto-detects the format from the content, validates it, shows a preview (X rows, Y new, Z updates), and asks for confirmation before writing. Imports are idempotent - re-importing the same file is a no-op on data that hasn’t changed.

Large imports queue automatically via PerfLocale's background-jobs system (Action Scheduler when loaded; WP-Cron otherwise) and the admin redirects to PerfLocale → Jobs for live progress. Small operations still finish inline. The uploaded file is persisted under wp-content/uploads/perflocale-temp/ for the worker to read and is cleaned up on completion.

WP-CLI (faster for large exports)

For sites with tens of thousands of translations, the CLI is still a strong choice (streams directly to disk, runs under your shell's time budget, easy to schedule). Each subcommand writes a single file; format is dictated by the subcommand, not a flag:

# Full-site JSON envelope (everything PerfLocale owns - languages, links,
# strings, glossary, slugs, settings, role caps, workflow state).
# The envelope embeds a `format_version` field so older importers refuse
# mismatched payloads.
wp perflocale export /tmp/full-backup.json
wp perflocale import /tmp/full-backup.json                      # additive merge
wp perflocale import /tmp/full-backup.json --mode=replace       # truncate-then-load

# Gettext PO files for string translations, one language at a time
wp perflocale po-export /tmp/de.po --lang=de
wp perflocale po-export /tmp/de-mytheme.po --lang=de --domain=mytheme
wp perflocale po-import /tmp/de.po --lang=de
wp perflocale po-import /tmp/de.po --lang=de --replace

# Glossary as RFC 4180 CSV (round-trips notes, case-sensitive flag, language pairs)
wp perflocale glossary-export-csv /tmp/glossary.csv
wp perflocale glossary-import-csv /tmp/glossary.csv
wp perflocale glossary-import-csv /tmp/glossary.csv --replace

# Multisite: one envelope keyed by blog ID, all sites at once
wp perflocale network-export /tmp/network.json
wp perflocale network-export /tmp/network.json --include-inactive
wp perflocale network-import /tmp/network.json --mode=replace
wp perflocale network-import /tmp/network.json --site=42        # restore one slice on a single-site target

# XLIFF for translation-agency handoff is exported via the admin UI;
# import the returned XLIFF file with the same `wp perflocale import` command
# (format is auto-detected from file contents).
wp perflocale import /tmp/de-posts.xliff

See WP-CLI Commands → Export & Import for the complete flag reference.

Merge vs. replace semantics

  • Merge (default) - additive. Existing rows kept; foreign keys re-mapped via id-maps so cross-site references survive intact. Idempotent on re-run.
  • Replace - truncates the target tables (scoped to the current site on multisite) before loading. Use only when you genuinely want to wipe the destination's PerfLocale state first.

Translation-agency handoff pattern

Typical flow when hiring an external agency:

  1. Export the source post(s) as XLIFF with target language = German (source = your default).
  2. Agency opens the XLIFF in their CAT tool, fills in translations, returns the file.
  3. Import the returned XLIFF via UI or CLI. Status of each translation becomes Pending for your review.
  4. Approve / tweak in the editor, change status to Published.

What isn’t included in exports

The exporter ships everything you need to reconstitute a site’s multilingual configuration and translation data. A small set of things is deliberately left out, because including them would either leak secrets, queue meaningless work on the destination, or duplicate state that’s regenerated automatically:

  • API keys / tokens / secrets / passwords — every settings key matching *_api_key, *_token, *_secret, or *_password is stripped from the settings blob before write. The destination operator re-enters them (or supplies them via wp-config.php constants / env vars). This is the right behavior for security: a JSON file shouldn’t round-trip your DeepL key through email.
  • Webhook subscriber URLs — site-specific infrastructure with HMAC secrets; the destination should re-register its own integrations.
  • Translation Memory rows — available as an optional section (tick the box in the export UI / pass the section name to CLI). Skipped by default because it’s a runtime cache that rebuilds as you translate.
  • Background-jobs queue state — the jobs table holds operational state with source-site-specific user / post / blog IDs. Importing it would queue meaningless work on the destination. The destination starts with an empty queue and dispatches its own.
  • Per-user admin UI preferences — user-meta keys like perflocale_strings_hidden_langs are user-id-specific and not portable across sites with different user tables.
  • Generated translation files — the .l10n.php output files in uploads/perflocale/translations/ are regenerated from strings + string_translations on the destination. Shipping the build artifacts would multiply export size for no benefit.
  • Internal caches — the addon-failure tracker, the active-engine memo, the eager-link-map, and similar diagnostic / runtime options are auto-rebuilt from the data that is exported.

Cross-site ID remapping

The importer doesn’t blindly insert primary-key values from the export. It resolves language_id references via the language slug, so a German row exported from a site where German has id = 3 lands correctly under whatever id German happens to have on the destination. Same for string_id via original_hash, group IDs via group type + member identity, etc. This is what makes export → import work cleanly between sites that were configured independently.

Format version + compatibility

Every export carries a format_version integer (currently 1). The importer accepts the current version and any older version it knows how to migrate. It refuses newer versions with a clear error rather than risk silent data loss on a partial-format-understanding import. Upgrade the destination plugin first if you see that error.

← Back to Docs