WP Manifestindependent plugin directory
manifest / i18n / codeon-multilingual

CodeOn Multilingual

Lightweight multilingual plugin for WordPress and WooCommerce. WPML-compatible, fraction of the weight.

by Samsiani / CodeOn · github.com/samsiani/codeon-multilingual · website

0stars
28release downloads
0forks

Install

The author publishes release zips, so WP-CLI can install straight from GitHub:

wp plugin install https://github.com/samsiani/codeon-multilingual/releases/download/v0.9.3/codeon-multilingual.zip

Readme

CodeOn Multilingual

A lightweight multilingual plugin for WordPress and WooCommerce. WPML-compatible feature surface at a fraction of the weight.

Why this exists

WPML and Polylang work, but they carry a lot of 2009-era baggage: 17+ DB tables, polymorphic indexes that MySQL plans poorly, React-heavy admin bundles, translation-workflow surface most sites never use. We deliver the same outcomes at ~10% of the disk weight and ~10–20% of the per-request query/memory cost.

Dimension CodeOn Multilingual Polylang WPML
PHP code (LOC) ~10.9 k ~30–50 k ~100 k+ across the suite
Release ZIP ~700 KB (incl. 60 SVG flags + catalog) ~3 MB ~50–150 MB
DB tables added 5 2 + reused taxonomy 17+
Frontend JS shipped by default ~1.4 KB (dropdown toggle) ~30 KB ~200 KB+
Admin JS bundle ~6 KB vanilla ~120 KB jQuery+UI 400 KB+ React
Extra queries per WP_Query 1 LEFT JOIN 2–3 (taxonomy joins) 3–7
gettext hot path overhead 1 isset() per call against a request-static map filter per call filter + DB hit
Memory per request <1.5 MB 3–5 MB 8–15 MB

Status

v0.9.2 — live-tested on artcase.ge. The locked v0.1.0 MVP scope is shipped, plus migration tooling, inline string editor, scan-based discovery, WP 6.5+ native .l10n.php translation path, the WPML compatibility shim (13 API surfaces — themes/plugins written against WPML's public API run unmodified), a full WP-CLI command surface for ops/CI/benchmark workflows, a first-run setup wizard with a bundled 66-language catalog, bundled SVG flags (60 countries — Windows-safe vs emoji-only rendering), per-language compiled-map cache, a hardened WooCommerce translation surface (product field locking on translations, automatic shop-page mapping per language, cart/order/email language handling, payment/shipping/coupon/status/notice/download label translation, translated product terms/variation attribute slugs, cart items follow current language with fallback to original, WC attribute label translation), a dedicated menu translation flow, per-language columns on posts/taxonomy admin lists, WPML rollback snapshots, a production Health screen, and blocking PHPCS/PHPStan/unit/package/WordPress+WooCommerce integration gates.

See ROADMAP.md for what's built, what's missing, and what's next.

Features at a glance

Routing

  • Subdirectory URL strategy (/ka/, /en/), virtual prefix, no flush_rewrite_rules() needed
  • Single LEFT JOIN injection on every WP_Query and WP_Term_Query
  • Per-language locale switching (<html lang>, .mo bundle selection, hreflang)

Content

  • Post / Page / CPT / Attachment translation via deep post_meta clone
  • Taxonomy term translation with hierarchical parent mapping
  • Slug uniqueness scoped per (post_type, post_parent, language)
  • Auto-tag new posts on save_post
  • Cron-driven backfill for legacy content

WooCommerce

  • Product translation with full variation auto-clone
  • 22 props synced across the translation group: price, stock, manage_stock, backorders, sold_individually, low_stock_amount, sale dates, dimensions, weight, tax_class, tax_status, downloadable, virtual, SKU, GTIN/UPC/EAN/ISBN, shipping class
  • Field-lock UI on translations (WPML-style): pricing, SKU, GTIN, stock controls, dimensions, shipping class, tax, virtual/downloadable, and the entire Attributes panel render disabled + read-only on translated products; banner reads "You're editing a translation of — edit the original to change these"
  • Shop-page mapping per language: on /en/cart/, wc_get_page_id('cart') returns the English cart page so is_cart() matches and the cart template fires
  • Cart items follow current language: line items, mini-cart, checkout review, order-received page, and emails swap to the translation; items without a translation stay in their original language
  • Attribute labels translatable (Color / Size / ფერი → translate from Multilingual → Strings; filter woocommerce_attribute_label swaps per language)
  • Attribute terms translatable (each pa_* taxonomy has the same per-language column UI as categories)
  • WC's "duplicate SKU" validator suppresses errors when the conflict is just another sibling in the same translation group
  • Translated WP pages without a sibling in the current language fall back to the source page rather than 404
  • Group-keyed lock prevents recursion during sibling sync

Strings

  • Hash-keyed compiled map in wp_cache, keyed per language so a request that touches multiple locales (e.g. WP boot's early __() calls + later router lang switch) never bleeds one language's translations into another
  • Translations into the default language apply correctly (the source code is in English; the site default is Georgian; the Georgian translation of an English source string IS the right thing to render)
  • Opt-in WP 6.5+ native .l10n.php writer (atomic + opcache-aware)
  • File-system scanner with regex for __, _e, _x, _ex, esc_*
  • Inline popup editor (vanilla JS, click-outside-saves)
  • Per-string source-language detection + badge
  • Plugin integration API: StringTranslator::register_source() + lookup_translation() for sources that bypass gettext (e.g. WooCommerce attribute labels, gateway titles, email subjects). One hash lookup against the request-static compiled map on the hot path.

Frontend

  • Shortcode [cml_language_switcher] + classic widget + auto-floating switcher + nav-menu item + Gutenberg block — five drop-in surfaces wrapping the same LanguageSwitcher::render()
  • Nav-menu item: side meta-box on Appearance → Menus inserts a single placeholder that auto-expands at render time, with per-item display (names / flags / both) and layout (inline / dropdown) settings
  • Gutenberg block (codeon-multilingual/switcher): server-rendered, ServerSideRender preview in the editor, style + show-flag/native/code attributes, wide/full alignment + spacing controls
  • Custom button/menu dropdown with bundled SVG flags (60 countries, MIT-licensed from lipis/flag-icons; Windows desktop without flag-emoji glyphs renders properly now)
  • Dropdown menu auto-sizes to the widest item, opens upward when anchored at the bottom of the viewport, and locks to the floating-switcher box width with a measure-and-pin JS pass that's stable across repeat clicks
  • Hreflang link tags + x-default
  • <html lang> + dir rewriting per current view
  • WP-core sitemap participation (all-language enumeration)

Admin

  • First-run setup wizard at admin.php?page=cml-setup — 4 steps (welcome → default language → secondary languages → done), 66-language catalog, search-as-you-type, SVG flags, auto-fills locale/native/RTL when a code is recognised
  • Defensive redirect: admin-post.php?page=cml-setup&step=N 302s to the canonical admin.php?... so stale bookmarks don't white-screen
  • Languages admin list shows SVG flag per row + "Run setup wizard" page-title action
  • "Add Language" form auto-fills locale, English name, native name, flag, RTL from the catalog as the admin types a code
  • Per-post-type and per-taxonomy language quick-link row above each list table
  • Per-language columns — one narrow column per active language on every translatable posts list AND taxonomy list. Header shows flag + uppercase code (🇬🇪 KA / 🇷🇺 RU / 🇺🇸 EN); each cell renders the flag for the row's source language and a green ✓ / grey + for every other (linked to the sibling edit screen or "create translation" respectively)
  • Menu translation flow: Multilingual → Menus lists every nav menu with per-language "Sync" actions. Untranslated items copy from source so translated menus are never half-empty
  • Translation slug derived from translated title: drafts keep post_name empty until publish, so a Georgian ვაშლი → translated as Apple → published with /apple/, not /ვაშლი/
  • Admin bar language indicator reflects the edited post's language (not the admin user's) on edit screens; sub-menu jumps directly to sibling edit screens; "Add translation" always creates from the group source
  • "Permalink:" preview on the translation edit screen now reflects the post's own language URL
  • Translation meta box distinguishes the source ("Edit X (original)") from translations ("Edit X translation")
  • Cookie-persisted language selection
  • WPML data migration tool (5-statement SQL importer)
  • Polylang migration tool for languages, post mappings, term mappings, and dynamic strings

REST

  • ?lang= parameter on every show_in_rest endpoint
  • POST /cml/v1/strings/<id>/translations for inline editor

WPML compatibility

  • icl_object_id, icl_get_languages, icl_get_default_language, icl_get_current_language functions
  • wpml_object_id, wpml_current_language, wpml_default_language, wpml_active_languages, wpml_post_language_details, wpml_element_has_translations, wpml_translate_single_string filters
  • wpml_register_single_string, wpml_switch_language actions
  • Astra, GeneratePress, WoodMart, Elementor, YITH, Woo extensions work without code changes

WP-CLI

  • wp cml language list / create / activate / deactivate / set-default / delete
  • wp cml translate post <id> --to=<lang> / wp cml translate term <id> --tax=<tax> --to=<lang>
  • wp cml strings scan [--theme=<slug>] [--plugin=<slug>] [--all]
  • wp cml strings export --lang=<code> [--format=po|json] [--domain=<d>] [--output=<file>]
  • wp cml strings import <file> [--format=auto|po|json] [--lang=<code>]
  • wp cml migrate wpml [--dry-run] --snapshot=<file>
  • wp cml migrate polylang [--dry-run] --snapshot=<file>
  • wp cml migrate sources
  • wp cml migrate export --output=<file>
  • wp cml migrate rollback <file> [--dry-run|--confirm-rollback]
  • wp cml backfill run [--all] / status / reset
  • wp cml health summary / report [--failed-only]
  • wp cml health repair [--dry-run|--apply] [--scope=all|orphaned-post-rows|orphaned-term-rows|orphaned-string-rows|missing-post-rows|missing-term-rows|unknown-source-languages]
  • wp cml benchmark report / seed --yes / cleanup --dry-run|--yes — strings plus real WP_Query product/variation collection workloads in the selected language, with guarded fixture cleanup

v0.9.x hardening

  • WPML/Polylang migration preflight is stricter about default-language and source-language conflicts.
  • Admin WPML/Polylang imports create a source-tagged rollback snapshot before writes; WP-CLI imports remain snapshot-gated.
  • WooCommerce product relationship IDs and variation option display labels remap to current-language siblings where possible.
  • WooCommerce Store API language state is scoped to Store API dispatches, including cart item product-object swaps and notice payload translation.
  • Yoast SEO and Rank Math breadcrumb/schema/canonical/sitemap surfaces localize URLs, post IDs, and term IDs through CodeOn translation groups.
  • Builder/template library CPTs are included in the default translatable post type set when present.

Requirements

  • PHP 8.1+
  • WordPress 6.5+
  • WooCommerce 8.0+ (optional, only for product sync)

Install

Production (auto-updates)

Download the latest ZIP from Releases and install via Plugins → Add New → Upload. Plugin Update Checker keeps it current as new tags are pushed.

Development

git clone https://github.com/Samsiani/codeon-multilingual.git
cd codeon-multilingual
composer install
ln -s "$(pwd)" /path/to/wp-content/plugins/codeon-multilingual

Then activate in Plugins.

See DEVELOPMENT.md for the full dev workflow.

Quick start

  1. Activate the plugin — the setup wizard opens automatically on first run, walking you through default + secondary language selection from a bundled catalog of 66 curated languages.
  2. (After the wizard) Edit any post — use the Translations meta box to create translations.
  3. Visit /{lang}/ on the front-end to see the translated site.
  4. Multilingual → Settings — tune the floating switcher and the optional native .l10n.php translation path.

If you skipped the wizard, "Run setup wizard" stays available as a link on the Languages page.

Documentation

Doc Purpose
README.md This file — overview and quick start
ARCHITECTURE.md Deep dive: modules, design decisions, hot paths, schema, request lifecycle
ROADMAP.md Current state, gap analysis, prioritised next steps
CHANGELOG.md Per-release notes
DEVELOPMENT.md Dev environment, tests, release workflow

License

GPL-2.0-or-later. See plugin header.

Read the full README on GitHub →

Releases

TagPublishedAssetDownloads
v0.9.3 Aug 31, 2026 codeon-multilingual.zip 1
v0.9.2 May 19, 2026 codeon-multilingual.zip 1
v0.9.1 May 19, 2026 codeon-multilingual.zip 2
v0.9.0 May 18, 2026 codeon-multilingual.zip 0
v0.8.0 May 18, 2026 codeon-multilingual.zip 0
v0.7.37 May 14, 2026 codeon-multilingual.zip 0
v0.7.36 May 14, 2026 codeon-multilingual.zip 0
v0.7.35 May 14, 2026 codeon-multilingual.zip 0
v0.7.34 May 14, 2026 codeon-multilingual.zip 0
v0.7.33 May 14, 2026 codeon-multilingual.zip 4
v0.7.32 May 14, 2026 codeon-multilingual.zip 0
v0.7.31 May 14, 2026 codeon-multilingual.zip 0
v0.7.30 May 14, 2026 codeon-multilingual.zip 0
v0.7.29 May 14, 2026 codeon-multilingual.zip 0
v0.7.28 May 14, 2026 codeon-multilingual.zip 0
v0.7.27 May 14, 2026 codeon-multilingual.zip 0
v0.7.26 May 14, 2026 codeon-multilingual.zip 0
v0.7.25 May 14, 2026 codeon-multilingual.zip 0
v0.7.24 May 14, 2026 codeon-multilingual.zip 0
v0.7.23 May 14, 2026 codeon-multilingual.zip 0
v0.7.22 May 14, 2026 codeon-multilingual.zip 0
v0.7.21 May 14, 2026 codeon-multilingual.zip 0
v0.7.20 May 14, 2026 codeon-multilingual.zip 0
v0.7.19 May 14, 2026 codeon-multilingual.zip 0
v0.7.18 May 14, 2026 codeon-multilingual.zip 0