Lang Forge includes a built-in migration tool at Lang Forge > Tools > Migration in the admin panel.
What Gets Migrated
| Source | Data | Source storage | Lang Forge target |
|---|---|---|---|
| WPML | Post language assignments | wp_icl_translations | _lf_language postmeta |
| WPML | Translation groups | wp_icl_translations.trid | wp_lf_translations rows |
| WPML | Default / active languages | icl_sitepress_settings, wp_icl_languages | lf_default_language, lf_active_languages |
| WPML | String translations | wp_icl_strings, wp_icl_string_translations | wp_lf_strings |
| Polylang | Post language assignments | language taxonomy relationships | _lf_language postmeta |
| Polylang | Translation groups | post_translations taxonomy description | wp_lf_translations rows |
| Polylang | Language metadata | language taxonomy description / term meta | lf_default_language, lf_active_languages |
| TranslatePress | Language settings | trp_settings | lf_default_language, lf_active_languages, lf_hide_default_language |
| TranslatePress | Rendered post translations | wp_trp_dictionary_{default}_{target} + wp_trp_original_meta | New translated post copies + wp_lf_translations rows |
| TranslatePress | Gettext strings | wp_trp_gettext_{target} | wp_lf_strings |
What Is NOT Migrated
- WPML / Polylang post contentAlready exists in
wp_postsand does not need to be moved. - Translation jobs/statusesWPML workflow data is not carried over.
- Translation Management / ATE metadataWPML translator assignments, baskets, review states, Advanced Translation Editor history, and job workflow metadata are not converted into Lang Forge workflow state.
- Advanced plugin settingsNon-language workflow settings from WPML, Polylang, or TranslatePress are not migrated.
- Visual-editor historyTranslatePress editor history/status metadata is not converted into Lang Forge workflow statuses.
Running the Migration
Via Admin UI:- Install and activate Lang Forge
- Navigate to Lang Forge > Tools > Migration
- Lang Forge auto-detects WPML, Polylang, or TranslatePress data in the database
- Click “Run Migration”
- Review the summary
- Deactivate the previous multilingual plugin
- Test the frontend
wp plugin install langforge --activate, run the browser migration from Lang Forge > Tools > Migration, then verify with wp langforge status and wp langforge flush. Deactivate the previous multilingual plugin only after the migration summary looks correct.
Activation seeding: on first activation, Lang Forge seeds lf_default_language and lf_active_languages from a real WPML/SitePress installation when it is active (wpml_default_language and wpml_active_languages), or from stored WPML tables/options when only migration data remains. If Polylang is present, activation reads the language taxonomy and polylang.default_lang instead of falling back to the generic ru/en pair.
Polylang re-runs: the Polylang importer treats the actual post_translations mapping as authoritative. It clears existing Lang Forge rows for the Polylang-assigned posts being migrated, then writes one row per real Polylang language relationship. Missing languages in an incomplete Polylang group stay missing; the importer does not fabricate default-language translations.
Post-Migration Checklist
- Verify translation groups: Run
wp langforge status - Check URL rewriting: Run
wp langforge flush - Test the language switcher on frontend
- Verify front page: Check
is_front_page()for translated pages - Re-enter string translations in Lang Forge > String Translation
- Test WooCommerce pages if applicable, including cart, checkout, order emails, and any paid extensions
- Check page builders – verify Elementor pages render correctly; verify Divi/Beaver/WPBakery/third-party widgets manually before launch
- Verify hreflang tags in page source
- Test search in each language
- Check the XML sitemap for hreflang annotations
php
function lf_migration_diagnostic() {
$languages = lf_get_languages();
$results = [];
foreach ($languages as $code => $lang) {
$count = new WP_Query([
'post_type' => 'any',
'meta_key' => '_lf_language',
'meta_value' => $code,
'posts_per_page' => -1,
'fields' => 'ids',
]);
$results[$code] = [
'name' => $lang['name'],
'posts' => $count->found_posts,
];
}
// Check for posts without language
$no_lang = new WP_Query([
'post_type' => 'any',
'meta_query' => [
['key' => '_lf_language', 'compare' => 'NOT EXISTS'],
],
'posts_per_page' => -1,
'fields' => 'ids',
]);
$results['_unassigned'] = [
'name' => 'No language assigned',
'posts' => $no_lang->found_posts,
];
return $results;
}—