Migrating from TranslatePress

TranslatePress and PerfLocale use fundamentally different storage models. TranslatePress keeps string-level translations in its own tables (wp_trp_original_strings + wp_trp_dictionary_*) and reconstructs pages at render time by swapping matched strings. PerfLocale uses the standard WordPress model - one post per language, each with its own post_content. Migration therefore reconstructs full post content by walking TranslatePress’s string dictionary and substituting translated strings into the original post. Then, when that’s done, it writes a brand-new translated post per language into wp_posts.

If you haven’t already, start with the migration overview for the shared pre-flight steps (backup, install PerfLocale, add languages).

What’s imported

  • Post translations - for every original post that has at least one translated string in TP’s dictionary, the importer reconstructs the translated content by substituting matched strings, then creates a new language-specific post in wp_posts and links it into a PerfLocale translation group. Only translations with TranslatePress status ≥ 2 (“manually translated”) are included - auto-machine-translated strings at status 1 are skipped by default.
  • String translations - every qualifying entry in wp_trp_dictionary_* (one table per language) is inserted into wp_perflocale_strings with the appropriate translation row.
  • Translated slugs - TranslatePress’s wp_trp_slug_translations rows are imported into PerfLocale’s slug-translation store so /fr/produits/, /de/produkte/, etc. keep working.

What’s not imported

  • Machine-translated strings at status 1. TranslatePress marks automatic DeepL/Google output as status 1; the importer only pulls status 2 (“manually reviewed”) and above. If you want the auto-translations too, lower the threshold via the perflocale/migration/translatepress/min_status filter before running, or re-run PerfLocale’s own machine translation after import.
  • The TranslatePress gettext dictionary. TranslatePress stores plugin/theme gettext strings under a separate gettext_ domain in its dictionaries. These are imported, but you should still run wp perflocale strings scan post-import to cover anything TranslatePress never rendered.
  • Meta translations (SEO title/description via TP’s SEO Pack addon) for unsupported SEO plugins. PerfLocale handles Yoast, RankMath, SEOPress, AIOSEO, Slim SEO, and The SEO Framework natively - confirm your SEO plugin is in that list before relying on meta translations.

Pre-flight checks

Know your database size

The TranslatePress importer does more work per post than the WPML or Polylang importers, because it reconstructs content rather than just relinking existing posts. For a site with thousands of translated posts, the admin-UI path may hit PHP’s max_execution_time. The importer calls set_time_limit(300) to extend the budget to 5 minutes, but some hosts disable that function.

If your host caps PHP execution or your site has more than a few hundred translated posts, use the WP-CLI path instead - it runs under your shell’s time budget and can handle any site size. The default 300s time limit is also filterable:

// Raise the time cap to 15 minutes before running the importer.
add_filter( 'perflocale/migration/time_limit', fn() => 900 );

Batch size

The importer processes posts in chunks of 50 (TranslatePressImporter::BATCH_SIZE). Between batches, the cache is flushed to keep memory usage flat even for very large sites. No user configuration is required; this is internal.

Match your language codes

TranslatePress stores locales in its options blob (trp_settings), e.g. fr_FR, de_DE. The importer maps those to PerfLocale languages via:

  1. Exact locale match first (PerfLocale language’s locale = TP’s code).
  2. Slug-prefix fallback (PerfLocale’s slug matches the prefix of TP’s locale, e.g. PerfLocale fr matches TP fr_FR).

Run the import

From the admin

  1. Go to PerfLocale → Settings → Export & Import.
  2. Scroll to “Import from TranslatePress”. If you don’t see it, the wp_trp_original_strings table isn’t present.
  3. Click Import from TranslatePress. Confirm the prompt.
  4. The page reloads with import counts. Expect this to take longer than WPML/Polylang imports because of the content-reconstruction work per post.

From WP-CLI (recommended for larger sites)

# Sanity-check: confirm TranslatePress data is detected.
wp perflocale migrate translatepress --dry-run

# Run the real import (prompts for confirmation).
wp perflocale migrate translatepress

# Skip the prompt when scripting.
wp perflocale migrate translatepress --yes

Verification

  1. New translated posts exist. Go to Posts → All Posts, filter by language, and confirm each language shows the expected number of posts.
  2. Reconstructed content looks right. Open a translated post in the editor and visit the frontend URL on a browser session where TranslatePress is still driving the render, then compare the two. Layout should be identical; wording should match.
  3. Strings imported. Open PerfLocale → Strings and confirm translations for a handful of phrases you remember translating in TP.
  4. Slugs work. Visit a translated URL like /fr/produits/ and confirm it resolves to the French version of the product archive.
  5. hreflang is correct. View page source on a translated URL - <link rel="alternate" hreflang="…"> tags should list every language version with the correct URL.
  6. Cache flushed. PerfLocale flushes its internal caches at the end of a TranslatePress import. If you use an external object cache or a page cache (WP Rocket, LiteSpeed, etc.), flush it manually.

Re-running the import

Re-running is safe. For each original post, the importer checks whether a PerfLocale group already exists for it and reuses the group when found, adding missing siblings. Already-linked translations at the same language are skipped. If new strings were added to TranslatePress after your first run, re-running picks them up.

Rollback

TranslatePress migration creates new posts in wp_posts (one per translated language per original post), so the cleanest undo is to restore from backup. If that’s not possible:

  • Open Posts → All Posts, filter by non-default languages, and bulk-delete the newly-created translation posts. This leaves TranslatePress’s dictionary untouched, so reactivating TP restores its behaviour.
  • Reactivate TranslatePress alongside PerfLocale is not recommended long-term - both will attempt to render language variants and you’ll get unpredictable behaviour. Keep only one active at a time.

Troubleshooting

Admin-UI import timed out

Expected on larger sites. Switch to WP-CLI, which doesn’t share PHP-FPM’s request timeout.

Some posts imported with partial translations

This typically means TranslatePress didn’t have a reviewed (status ≥ 2) translation for some of the original post’s strings. Only manually-reviewed strings are imported by default. To include auto-translated strings:

add_filter( 'perflocale/migration/translatepress/min_status', fn() => 1 );

Then re-run the importer - the missing strings will fill in during the reconstruction pass.

Old TranslatePress URLs 404 after the import

Flush rewrite rules: wp rewrite flush. TranslatePress’s URL conversion is runtime-only, so after you deactivate it, PerfLocale needs its rewrite rules re-registered.

Next steps

  • Run wp perflocale strings scan to cover theme/plugin gettext strings TranslatePress never rendered (it only catches strings that appeared on a page it was rendering).
  • Configure language fallbacks.
  • See the PerfLocale vs TranslatePress feature comparison for the structural differences (per-language posts vs dynamic substitution).