Lightweight multilingual plugin for WordPress.
- Language management — Multilingual > Languages page with drag-and-drop ordering (auto-saves via AJAX)
- Automatic language detection — install a language in WP General Settings, it auto-appears in WP-LOC. Delete from WP-LOC — removes language files too.
- Post/page translations — auto-create translation drafts, translation metabox in editor with per-language
+button for on-demand creation - Bidirectional post taxonomy sync — when a translated post changes its multilingual categories/tags/custom taxonomies, sibling posts receive the mapped term translations in their own language
- Taxonomy/term translations — migration-compatible term translation groups for
category,post_tag, and selected custom taxonomies - Term translation UI — translation column in term lists, translation panel on term edit screens, and per-language
+buttons for on-demand term translation creation - Automatic term translation creation — creating a term can auto-create sibling translations in all active languages
- Hierarchical taxonomy sync — translated parent terms are mapped automatically when creating/editing child terms
- Same slug in different languages — term slugs are unique per language, not globally
- Cascade delete for term translations — deleting any translated term deletes the whole translation group
- Protected default category group — default category and all its translations cannot be deleted
- Multilingual nav menus — translated menu groups, translated menu items, language-aware menu locations, auto-created menu translations, and cascade deletion for menu translation groups
- Tools page — Multilingual > Tools with tabbed utilities for WP Menus Sync, AI Translation, and Config Migration
- WP Menus Sync — AJAX preview/apply for syncing menu structure from the default language to secondary languages
- AI-assisted custom menu links — optional AI translation for
customnav menu items during menu sync, while preserving URLs and other menu item settings, with safe fallback when the AI provider refuses a short-field translation - AI Translation tool — TinyMCE-based AJAX translator for formatted HTML content, with translated content inserted back into the editor without reloading the page
- Config Migration tool — detects legacy multilingual config files (
wpml-config.xml), reads only translatable post types and taxonomies, generates lightweightwp-loc-config.xml, and can remove theme-level legacy config files - Database Optimization Wizard — appears in admin after activation, scans multilingual data left by another plugin, adopts compatible translation links, imports languages, detects translated post types/taxonomies/options, and removes obsolete service data after confirmation
- Manual language mapping during optimization — the wizard auto-matches detected languages through a built-in registry, shows match confidence, and lets admins override the target WP-LOC language before applying cleanup
- Language registry — central locale/code/slug/name/flag normalization for common WordPress and multilingual-plugin language codes, including aliases like
uk→uaand legacyiw→he - Separate URL slugs and compatibility codes — languages can use URL slugs like
uawhile compatible database/API language codes remainuk - Non-translatable post types — work correctly with language URL prefixes (shared content across languages)
- Translatable post type detection — if compatible translation rows already exist, WP-LOC can detect translated custom post types and taxonomies and merge them into runtime settings
- Frontend/admin query filtering — translatable posts are filtered by the current language for main, secondary, AJAX, REST, and Gutenberg preview
WP_Querycalls when filters are not suppressed - Frontend AJAX language context — standard
admin-ajax.phphandlers inherit the current frontend language through compatible cookies, request parameters, and referring URLs - Runtime language switching — WPML-style
wpml_switch_language/$sitepress->switch_lang()calls temporarily switch WP-LOC's language and WordPress locale, so background handlers and transactional email flows can render content in a user's preferred language - URL structure —
/ua/page-slug/,/en/page-slug/, default language without prefix - Migrated default language — the migration wizard preserves the legacy multilingual default language for no-prefix URLs
- Admin language switcher — in the admin bar with flags, cookie-based
- Frontend language switcher —
wp_loc_get_lang_switcher(),wp_loc_get_language_switcher_html(),wp_loc_the_language_switcher()with translated post, custom post type, taxonomy, author, search, date, paginated, query-filtered, and archive URLs - SEO — frontend hreflang alternate tags for translated singular, front page, posts page, and taxonomy contexts; canonical URL fallback when no SEO plugin outputs one; proper
<html lang=""> - Yoast SEO compatibility — localized
wpseo_titles/wpseo_social/wpseo_rssoptions, translated primary category resolution, copied Yoast term SEO meta for translated terms, multilingual sitemap alternate links, stripped category-base compatibility, and Yoast indexable invalidation after multilingual updates - Localized options —
blogname,blogdescription,page_on_front,page_for_posts, plus options registered throughwp_loc_multilingual_optionsor compatiblewpml_multilingual_options, per language and with localized front page / posts page routing - Custom settings page support — localized options are displayed and saved correctly on WordPress settings pages, including custom submenu pages under Settings that register option names dynamically
- AI settings — choose OpenAI / Claude / Gemini, store API keys, and enable AI translation for custom menu links during menu sync
- Translation workflow settings — control automatic creation of post, term, and menu translations from Multilingual > Settings > Content Translation
- Sync policy settings — control taxonomy sync, featured image sync, and shared post-attribute sync for translation groups
- Switcher behavior settings — control whether the frontend switcher shows flags and names, hides the current language, hides untranslated targets, or falls back to language home URLs
- Integration toggles — enable or disable ACF compatibility, Yoast compatibility, and Yoast sitemap alternate links from Multilingual > Settings > Integrations
- Third-party compatibility —
icl_object_id(),$sitepress,ICL_LANGUAGE_CODE, common multilingual filters - ACF integration — ACFML-like field/group translation config for DB, local JSON, and PHP-registered field groups, plus language-aware
options_{lang}routing for options pages - ACF field translation modes —
shared,copy_once,translatable, and editable shared-valuenonebehavior for multilingual field workflows - ACF media/relation mapping — translated attachment, post, term, and nav menu IDs are resolved per language for fields like
image,file,gallery,post_object,page_link,relationship,taxonomy, andnav_menu - ACF container field support — multilingual behavior for
group,repeater,flexible_content, andclonefields across options pages, posts/pages, and term edit screens - ACF nav_menu field support — translated menu values resolve to the correct menu in the current language context
- Timber integration — Twig functions
wp_loc_language_switcher()andwp_loc_languages() - Activation safety — on activation, WP-LOC deactivates known conflicting multilingual add-ons instead of deleting them
- GitHub updates — native WordPress plugin updates from the public GitHub repository by comparing the remote
wp-loc.phpVersion:header on the configured branch - Ukrainian slug —
uklocale →uaURL slug out of the box
- WordPress 6.0+
- PHP 8.1+
- Upload
wp-locfolder to/wp-content/plugins/ - Activate the plugin
- If the Database Optimization Wizard appears, review the scan, adjust language mapping if needed, and apply or dismiss it
- Go to WP General Settings and install any additional languages you need — they auto-appear in Multilingual > Languages
- Configure language slugs, display names and ordering in Multilingual > Languages
- Select or review translatable post types and taxonomies in Multilingual > Settings
- Configure Multilingual > Settings tabs for content workflow, switcher behavior, integrations, and AI provider settings
- Use Multilingual > Tools for WP Menus Sync, the AI Translation tool, and Config Migration
- WP-LOC uses the standard WordPress plugin update flow, so update checks, update notices, AJAX updates, and disabled-update hooks behave like native plugin updates
- The updater reads
Version:fromwp-loc.phpon the configured GitHub branch and compares it with the installedWP_LOC_VERSION - Update checks are cached in a site transient and do not ping GitHub on every admin request
- GitHub Releases are not required; the update package is the configured branch ZIP archive
- To publish an update, bump both the plugin header
Version:andWP_LOC_VERSION, then push the branch
// Get current language
$lang = wp_loc_get_current_lang(); // 'ua', 'en', 'ru'
// Get language switcher
$switcher = wp_loc_get_lang_switcher();
foreach ( $switcher as $lang ) {
echo '<a href="' . $lang['url'] . '" class="' . ($lang['active'] ? 'active' : '') . '">';
echo '<img src="' . $lang['flag'] . '" /> ' . $lang['name'];
echo '</a>';
}
// Or render ready-to-use markup:
wp_loc_the_language_switcher();
// Get translated term ID
$translated_term_id = icl_object_id( $term_id, 'category', true, 'en' );
// Get translated post ID
$translated_id = icl_object_id( $post_id, 'page', true, 'en' );
// Register a multilingual option
do_action( 'wp_loc_multilingual_options', 'my_custom_option' );
// Compatible registration used by many existing themes/plugins
do_action( 'wpml_multilingual_options', 'my_custom_option' );
// Temporarily switch language for a background/transactional flow
do_action( 'wpml_switch_language', 'ru' );
$subject = get_option( 'my_custom_option' ); // reads my_custom_option_ru when available
do_action( 'wpml_switch_language', 'uk' ); // restore previous/default context when done- Create the source menu in the default language
- WP-LOC auto-creates sibling menus in the other active languages
- Menu locations are assigned from the default-language menu and resolved automatically per language on the frontend
- Use Multilingual > Tools > WP Menus Sync to sync structure/order/options from the default-language menu to translated menus
- If Try to translate custom nav menu links with AI during menu sync is enabled in Multilingual > Settings > Content Translation, custom menu links are translated with the selected AI engine during sync; otherwise they are duplicated 1:1 with the same title, URL, and item settings
- If an AI provider returns a refusal or unusable short-text response for a custom menu link field, WP-LOC keeps the original field value instead of saving the refusal text into the translated menu item
- Automatic menu creation can be disabled from Multilingual > Settings > Content Translation if you prefer to create translated menus manually
- In Multilingual > Settings > AI, choose the translation engine (
OpenAI,Claude, orGemini) and provide the matching API key - In Multilingual > Tools > AI Translation, paste or write formatted content in the TinyMCE editor, choose a target language, and translate it via AJAX without reloading the page
- The translated HTML is inserted back into the editor while preserving formatting
- In Multilingual > Tools > Config Migration, WP-LOC scans the active theme, parent theme, and active plugins for legacy multilingual config files such as
wpml-config.xml - WP-LOC reads only
custom-typesandtaxonomiesfrom supported legacy config files; other config sections are ignored on purpose - You can generate a lightweight
wp-loc-config.xmlfrom the current WP-LOC settings - You can also generate
wp-loc-config.xmlfrom a detected legacy config source - Theme-level legacy config files can be removed from the same screen after migration; plugin-level files are shown as read-only
- The wizard opens automatically for admins after activation while its status is
pending - Closing the modal with the
Xonly hides it for the current page load; dismissing optimization stores that choice and stops the automatic modal - A plugin-row Wizard action remains available while the wizard has not been completed, so admins can return to it after dismissing
- The scan summarizes compatible translation links, detected content types, taxonomies, media, menus, localized options, language records, and removable service data
- Detected languages are normalized through
WP_LOC_Language_Registry; the wizard shows match confidence and lets admins manually map each detected source language to a WP-LOC target language - Imported languages preserve compatible language codes and detected switcher display names where available, so URL slugs and database/API codes do not have to be identical
- Applying optimization imports or updates WP-LOC languages, adopts compatible translation links, imports detected translatable post types/taxonomies into settings, cleans obsolete options/meta/tables, and marks the wizard as completed
- The apply flow requires confirmation because removed service data is not restored by WP-LOC
sharedfields stay on the base ACF options post ID (options)- Saving translated options, posts, or terms ignores read-only
sharedfields so disabled ACF containers cannot overwrite source-language values translatablefields are routed through language-aware ACF options post IDs likeoptions_en/options_rucopy_oncecontainer fields inherit from the source language until the translated options page stores its own value- Both
get_field( 'field_name', 'options' )andget_fields( 'options' )resolve translated values in the current language context nav_menuACF fields resolve to the translated menu for the current language
- Options registered through
wp_loc_multilingual_optionsand compatiblewpml_multilingual_optionsare stored/read with language suffixes, for examplemy_custom_option_en,my_custom_option_ru, or compatible codes likemy_custom_option_uk - WP-LOC reads both the internal URL slug suffix and the compatible language-code suffix, so installations that use
uain URLs can still read legacy-compatible*_ukoption values - Custom settings pages under Settings receive localized values in the admin when the admin language differs from the default language
- Frontend AJAX requests to
admin-ajax.phpuse the frontend language context, so transactional option reads inside AJAX handlers do not fall back to the default language - Code that explicitly calls
do_action( 'wpml_switch_language', $lang )or$sitepress->switch_lang( $lang )temporarily switches WP-LOC's current language and WordPress locale until it is switched back. This is useful for cron jobs, payment webhooks, and transactional emails that need to render content in a user's saved preferred language
- The same multilingual ACF logic works for translatable posts/pages, classic editor post screens, Gutenberg page screens, and multilingual term edit screens
- ACF picker fields for posts, pages, relationships, taxonomies, and nav menus only list choices from the current editor language
- ACF user profile fields marked as translatable read/write language-suffixed user meta such as
field_name_ruandfield_name_en, while the default language keeps the canonicalfield_namemeta key sharedfields resolve the source-language value but map media/post/term/menu references into the current languagesharedfields stay editable on the source-language entity even when that source language is not the site default, and become readonly only on translated entities- Saving a translated post or term cannot push readonly
sharedvalues back into the translation group; shared sync runs from the source-language entity only copy_oncefields inherit the source-language value until the translated post/term/options page stores its own independent value- Container fields such as
group,repeater,flexible_content, andclonepreserve their ACF structure while mapping nested media and relation values per language
- WP-LOC loads a dedicated Yoast compatibility layer only when Yoast SEO is active
- The Yoast compatibility layer and Yoast sitemap alternate links can be toggled separately in Multilingual > Settings > Integrations
- Global Yoast options such as
wpseo_titles,wpseo_social, andwpseo_rsscan be localized per language through the same multilingual options model used by WP-LOC - Yoast primary category meta is resolved to the translated term in the current post language
- Yoast taxonomy SEO meta is copied into translated terms so translated archives keep their own SEO title/description state
- Yoast indexables are invalidated after multilingual post, term, and global-option updates so Yoast can rebuild its cached SEO data
- Yoast XML sitemaps gain
xhtml:linkalternate-language entries for translated posts, pages, terms, and first archive links - Yoast
stripcategorybaserewrites remain compatible with multilingual category slugs - Yoast News can reuse the current post language code for publication-language output when the addon is active
{{ wp_loc_language_switcher() }}
{% for lang in wp_loc_languages() %}
<a href="{{ lang.url }}" class="{{ lang.active ? 'active' : '' }}">{{ lang.name }}</a>
{% endfor %}- Enable multilingual behavior per taxonomy in Multilingual > Settings
- Term translations are stored in the
icl_translationstable astax_{taxonomy}rows usingterm_taxonomy_id - Category, tag, and custom taxonomy archive URLs are language-aware
- Nested hierarchical term archive paths resolve segment-by-segment in the requested language, so duplicate child slugs across languages do not collapse to the default language
- For translatable posts, multilingual taxonomy assignments sync across the whole post translation group
- If a translated term does not exist for the current language, the frontend switcher falls back to the language home URL
- Wrong-language term archive URLs return
404
- Translated singular URLs resolve by language, post type, and slug, so translated posts from different post types can safely share the same slug
- Hierarchical page paths are resolved in the requested language before WordPress fallback resolution, so translated parent/child pages can safely reuse the same slugs across languages
- Custom post type translations with identical slugs across languages resolve to their translated post instead of redirecting back to the default-language post
- Canonical redirects are blocked when WordPress tries to strip or replace an existing non-default language URL prefix
- Compatibility switcher APIs such as
icl_get_languages()use the same translated URLs as WP-LOC's native switcher helpers - Frontend requests persist
wp_loc_current_language,wp_loc_current_locale,_icl_current_language, andwp-wpml_current_languagecookies so same-origin AJAX calls toadmin-ajax.phpkeep the expected language context
WP-LOC can interoperate with sites that already use the icl_translations table and legacy multilingual config files such as wpml-config.xml.
WP-LOC is an independent open-source project. It is not affiliated with, endorsed by, or sponsored by any third-party multilingual plugin vendor.
GPLv2 or later.
Vitalii Kaplia — vitaliikaplia.com