Migrating from WPML
PerfLocale ships a bundled importer for WPML that reads the wp_icl_translations and wp_icl_strings tables and reconstructs your translation groups as native PerfLocale rows. Your WPML data is read-only during the import - nothing is modified or deleted from WPML’s tables, so you can re-run, abandon, or verify the migration at your own pace before switching over.
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 - every entry in
wp_icl_translationswithelement_type LIKE 'post_%'. This covers posts, pages, attachments, and all custom post types that WPML was tracking. Translation groups are reconstructed from the WPMLtrid(translation group ID). - Term translations - every entry with
element_type LIKE 'tax_%': categories, tags, custom taxonomies. - String translations - rows from
wp_icl_stringsjoined withwp_icl_string_translationswhere the translation status is10(WPML’s “translation complete” status). Strings with status0(not translated) or20(needs update) are skipped.
What’s not imported
- WPML Media Translation uploads (per-language media files). PerfLocale handles multilingual media differently - via the same post-translation-group mechanism applied to attachments - so re-translating media is a post-migration task in the PerfLocale admin.
- WPML Menu Sync relationships that rely on WPML-internal IDs. Menus themselves are translated as terms (via the
nav_menu_itempost type) when WPML tracks them that way, but the sync-on-save logic is a WPML-specific feature. - WPML Translation Management workflow state (who was assigned what, status history). PerfLocale has its own workflow addon; assignments don’t carry across.
- Strings at status 20 (“needs update”). Treat them as not-yet-translated in PerfLocale and revisit in the string-translation admin.
Pre-flight checks
In addition to the shared steps in the overview, there are two WPML-specific things to get right:
Match your language codes
The importer maps WPML’s language_code (from wp_icl_translations) to a PerfLocale language ID using a two-step lookup:
- Exact slug match first. If WPML uses
fr, add a PerfLocale language with slugfr. - If no slug match, locale prefix match: PerfLocale’s
locale(e.g.fr_FR) starts-with WPML’s code (e.g.fr). Useful when WPML uses a short code and PerfLocale tracks a full locale.
If your WPML site used a non-standard code (e.g. en-US with a hyphen instead of an underscore), add a PerfLocale language with the matching slug exactly.
Keep WPML active until you verify
Don’t deactivate WPML before running the importer. The importer reads WPML’s tables directly, but leaving WPML active also means you can open any post in its original editor to cross-check against PerfLocale’s translated post as you spot-check.
Run the import
Upgrade note for 1.0.16: earlier builds had a fatal in the string-migration path (the importer attempted to wrap translated strings in a post-type translation group, which was rejected by the string-group guard). If you attempted a WPML migration on 1.0.15 or earlier and saw a fatal during the string phase, upgrade to 1.0.16+ and re-run - the importer is idempotent, so previously-imported posts and terms stay intact and the missing string translations fill in.
From the admin
- Go to PerfLocale → Settings → Export & Import.
- Scroll to the “Import from WPML” section. If you don’t see it, the
wp_icl_translationstable isn’t detected - WPML may never have been installed on this site, or the DB prefix is non-standard. - Click Import from WPML. Confirm the prompt.
- Wait. On sites with a few hundred multilingual posts the import typically completes in under 30 seconds. The page reloads with a banner showing counts and any per-language warnings.
From WP-CLI
# Sanity-check: confirm WPML data is detected.
wp perflocale migrate wpml --dry-run
# Run the real import (prompts for confirmation).
wp perflocale migrate wpml
# Skip the prompt when scripting.
wp perflocale migrate wpml --yes
For sites with thousands of multilingual posts or a strict PHP max_execution_time cap, prefer the CLI path - it runs under your shell’s time budget rather than PHP-FPM’s request timeout.
Verification
Work through these in order before deactivating WPML:
- Spot-check a post. Open any multilingual post in Posts → All Posts. The PerfLocale sidebar should list every language that existed in WPML, each linking to the correct translated post.
- Check terms. Go to Posts → Categories (or any translated taxonomy) and confirm translated terms are linked in the PerfLocale translation column.
- Open a string. Navigate to PerfLocale → Strings and filter for a phrase you remember translating in WPML. Confirm the translation is present for the expected language.
- Visit a translated URL. Browse to
/fr/your-post-slug/(adjust for your prefix convention). Confirm the page renders the French content, and that the language switcher highlights French. - View source. Confirm
hreflangtags in the page head point at the right sibling URLs.
Re-running the import
The WPML importer is idempotent: re-running it after a partial or failed run completes the gaps rather than creating duplicates. Internally, every trid is checked against existing PerfLocale translation groups - if a group already contains any of the posts from that trid, the importer reuses it and links the remaining siblings. If a sibling is already linked to the same group with the same language, it’s skipped silently (no cache churn).
This means if your first run warned about “No PerfLocale language match for WPML code <x>”, you can add the missing language and re-run - the previously-imported content stays intact and the new language’s content fills in.
Rollback
There’s no “undo migration” button. Two paths if you need to back out:
- Restore from backup - the cleanest undo, since it rewinds any new
wp_perflocale_*rows that were written. - Manual cleanup while WPML is still active - if you only imported a handful of posts, you can open each newly-created translation post and delete it from Posts → All Posts. The original WPML-managed posts are untouched.
In either case, leave WPML activated until you’re certain you won’t need it - WPML’s tables are not modified by the importer, so reactivating it restores the pre-migration experience immediately.
Troubleshooting
“No matching languages found between WPML and PerfLocale”
Every language in wp_icl_translations.language_code has to match either a PerfLocale language slug or the prefix of a locale. Check the output for specific “No PerfLocale language match for WPML code <x>” warnings, then add the missing languages in PerfLocale → Languages and re-run.
The import timed out
Switch to WP-CLI (see above). The admin-UI path runs inside a regular PHP request and is subject to max_execution_time / PHP-FPM’s request timeout; WP-CLI runs under your shell’s time budget and can handle sites of any size.
I re-ran and now I have duplicate posts
You shouldn’t - the importer is idempotent. If you’re seeing duplicates, they were most likely created by WPML itself (WPML sometimes creates “duplicate” placeholder posts when a translation is queued). Check whether the duplicates existed pre-import via Posts → All Posts filtered by language. If they did, they’re a WPML artefact, not a migration bug.
Next steps
- Configure language fallbacks - untranslated posts can redirect to a related language instead of 404’ing.
- Scan for theme/plugin gettext strings via
wp perflocale strings scan- WPML’s string table only covers what WPML’s own scanner picked up. - See the PerfLocale vs WPML feature comparison for what’s available in PerfLocale beyond what WPML offers.