Skip to main content

MHM Rentiva v4.27.1: i18n Locale-Leak Hotfix — Vehicle Settings Labels No Longer Frozen at First-Install Language

· 4 min read
MaxHandMade
Maintainer

A few hours after the v4.27.0 WordPress.org submission readiness release, a critical bug was caught: On an English WordPress site, the Vehicle Settings page was displaying Turkish labels. This is a global plugin; English is the primary language. The bug was submission-blocker severity. v4.27.1 fixes it.

Written before 6.0.0

This post is the record of a release that shipped before 6.0.0, which renamed the plugin's post types, taxonomies, options, meta keys and hooks. The identifiers below are the names those versions actually used, and they are kept that way on purpose. For the current names — and a conversion table — see the 6.0.7 release post.

🐛 Bug Scenario

Typical reproduction steps:

  1. Plugin is installed on a Turkish WordPress site; the Vehicle Settings page is visited once.
  2. The site is switched to English (or a Turkish DB backup is restored onto an English site).
  3. The admin panel still shows Turkish labels: Günlük Fiyat, Model Yılı, Plaka, Klima, Yedek Lastik...

Yet every __( 'Daily Price', 'mhm-rentiva' ) call in the source code correctly returns the English value for the current WordPress locale. So why was the admin UI still showing Turkish?

🔍 Root Cause

VehicleMeta::ensure_default_options() performed the following on the first admin page load:

$default_details = self::get_default_details(); // Array with TRANSLATED __() calls
// ...
if (get_option('mhm_vehicle_details', array()) === array()) {
update_option('mhm_vehicle_details', $default_details); // 🔥 Wrote Turkish values to DB
}

Inside get_default_details():

return array(
'price_per_day' => __('Daily Price', 'mhm-rentiva'), // Turkish site → "Günlük Fiyat"
'year' => __('Model Year', 'mhm-rentiva'), // Turkish site → "Model Yılı"
// ...
);

__() returns the translation for the locale active at the moment of the call. With a Turkish site, this returned "Günlük Fiyat" and wrote it to wp_options.

Later, in the get_all_available_details() render flow:

$details = self::get_default_details();              // live __() → English (site is EN now)
$stored = get_option('mhm_vehicle_details'); // FROZEN Turkish
foreach ($stored as $key => $label) {
$details[$key] = $label; // Override: Turkish wins
}

The frozen translation always overwrote the live translation.

🛠️ Fix

1. Stop auto-persisting

The three update_option('mhm_vehicle_*', $translated_defaults) calls inside VehicleMeta::ensure_default_options() were removed entirely. It continues to seed mhm_selected_* (key lists only — no translated strings).

2. Clean up existing installs

New method VehicleMeta::migrate_remove_auto_populated_labels() — a one-time migration guarded by the mhm_rentiva_v4271_labels_migrated flag:

foreach (['mhm_vehicle_details', 'mhm_vehicle_features', 'mhm_vehicle_equipment'] as $key) {
$stored = get_option($key, null);
if (is_array($stored) && !empty($stored)) {
delete_option($key);
}
}

Migration is triggered inside the existing version-drift block on the plugins_loaded hook — runs across admin, cron, and WP-CLI flows.

3. User custom fields are preserved

mhm_custom_details, mhm_custom_features, mhm_custom_equipment — fields added by the user via "Add Custom" live in separate options. The migration does not touch them.

⚖️ Trade-off

If you renamed a core field via "Edit Names" (e.g. "Daily Price" → "Rental Rate"), you will need to re-apply that rename once after upgrading. This is a rare scenario — but the bug affected every install. The trade-off is fair.

🧪 Tests

tests/Migration/LocaleLeakLabelsMigrationTest.php4 new regression tests, 13 assertions:

  1. Leaked label arrays are cleaned (Turkish, English, and mixed content)
  2. Migration is idempotent — second call is a no-op, does not overwrite a legitimate post-migration rename
  3. Fresh install — no legacy option present; flag is still set (skips future calls)
  4. mhm_custom_* — user custom fields are not touched

✅ Verification

CheckResult
PHPCS release scope0 error, 0 warning
PHPUnit732/732 (+4 new), 6 skipped, 0 regressions
Plugin Check (PCP)0 error, 3 trivial warnings (.distignore-excluded)

📦 Installation

mhm-rentiva.4.27.1.zipPlugins → Add New → Upload Plugin, or use the Rentiva Pro auto-updater.

Requirements: PHP 8.1+, WordPress 6.7+.

🔜 Next

  • v4.27.2: Search Results UX improvements + Shortcode Review Backlog (combined patch)
  • v4.28.0: Popular Routes shortcode/block/widget (new feature)
  • v4.29.0: Vendor Report / Appeal System (new subsystem)

🎓 Lesson

This bug was caught a few hours after the v4.27.0 WordPress.org submission readiness release. The zero PHPCS / zero PCP validation suite had passed, but neither PHPCS nor PCP can see a runtime bug (existing DB state overwriting __() output). Real admin UI testing is the only safeguard. We needed a smoke test run before WordPress.org submission — v4.27.1 establishes this pattern: manual verification is mandatory for any UI that routes through the translation layer.


GitHub Release: v4.27.1