Skip to content
Back to skills

Orbit I18n Js Parity

ASecurity

PHP↔JS label parity audit — verifies every label key consumed by JS via `wp_localize_script` / `wp_set_script_translations` is actually emitted from PHP. Catches the silent-English-fallback bug where JS reads `myObject.someLabel` but PHP never put `someLabel` into the localized object. Use when the user says "JS label", "wp_localize_script", "Elementor editor label", "Gutenberg block label", "missing translation in JS", or before any release that touches block editor / Elementor / admin JS.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 9, 2026
testinggophpbashcode-review

Security analysis

A100/100

Scanned September 9, 2026

npx -y skills add adityaarsharma/orbit --skill orbit-i18n-js-parity --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Orbit I18n Js Parity?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Orbit I18n Js Parity
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/adityaarsharma-orbit-i18n-js-parity/badge)](https://www.skillsdirectory.com/skills/adityaarsharma-orbit-i18n-js-parity)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: orbit-i18n-js-parity
description: PHP↔JS label parity audit — verifies every label key consumed by JS via `wp_localize_script` / `wp_set_script_translations` is actually emitted from PHP. Catches the silent-English-fallback bug where JS reads `myObject.someLabel` but PHP never put `someLabel` into the localized object. Use when the user says "JS label", "wp_localize_script", "Elementor editor label", "Gutenberg block label", "missing translation in JS", or before any release that touches block editor / Elementor / admin JS.
---

# 🪐 orbit-i18n-js-parity — JS↔PHP label parity

The skill that catches the bug `orbit-i18n` cannot see: **the PHP side wraps every string in `__()` correctly, but the JS side reads a key the PHP never localizes.** Result: JS falls back to English (or `undefined`), and no warning, no error, no test fails. The string just silently never translates.

**Why this skill exists:** example-plugin's Elementor editor showed an English "Generate FAQ" button in every language because `class-rnrd-elementor.php` localized `rnrdElEditor.generateSummary` but not `rnrdElEditor.generateFaq` — the JS read the missing key, defaulted to a hardcoded English fallback, and shipped to production. Plugin-agnostic — applies to any plugin with PHP-localized JS labels.

---

## What this skill checks

### 1. Collect every `wp_localize_script` emit

Scan every PHP file for:
```bash
grep -rn 'wp_localize_script' --include='*.php' . | grep -v 'tests/'
```

For each call, extract:
- The script handle (arg 1)
- The JS object name (arg 2)
- The array of keys (arg 3)

Build a table:
```
HANDLE                 JS OBJECT          KEYS EMITTED
rnrd-elementor-editor  rnrdElEditor       generateSummary, regenerateSummary, generatingSummary
```

### 2. Collect every `wp_set_script_translations` emit

```bash
grep -rn 'wp_set_script_translations' --include='*.php' .
```

For each: handle + textdomain + path. Required for `wp.i18n.__()` calls in JS to resolve.

### 3. Collect every JS read of the localized object

For each JS object name discovered in §1, grep the JS sources:
```bash
grep -rn 'rnrdElEditor\.\w\+\|window\.rnrdElEditor\.\w\+' --include='*.js' --include='*.jsx' --include='*.ts' --include='*.tsx' assets/ src/ build/ 2>/dev/null
```

Build a table:
```
JS OBJECT          KEYS READ
rnrdElEditor       generateSummary, regenerateSummary, generatingSummary,
                   generateFaq, regenerateFaq, generatingFaq        ← extra keys
```

### 4. Diff EMITTED vs READ

```
ORPHAN KEYS (JS reads, PHP doesn't emit):
  rnrdElEditor.generateFaq            ← FAIL — JS shows fallback / undefined
  rnrdElEditor.regenerateFaq          ← FAIL
  rnrdElEditor.generatingFaq          ← FAIL

UNUSED KEYS (PHP emits, JS never reads):
  rnrdElEditor.legacyButton           ← cleanup candidate (low priority)
```

Every orphan key is a Critical i18n bug.

### 5. JS `wp.i18n.__()` calls without script translations

For every JS file using `__('Some string', 'plugin-textdomain')` or `import { __ } from '@wordpress/i18n'`:
- Trace the JS file back to its enqueued script handle (via `wp_enqueue_script` + script path)
- Confirm a matching `wp_set_script_translations( $handle, 'plugin-textdomain', $languages_path )` call exists

If the handle isn't covered → `wp.i18n.__()` silently returns the English source string. Critical.

### 6. Object name collision check

Multiple `wp_localize_script` calls writing to the same JS object name on different handles → only the last one wins on the page where both scripts load. Catches the "I added the keys, why are they still missing?" bug.

```bash
# Sort by JS object name, count occurrences > 1
grep -rn 'wp_localize_script' --include='*.php' . \
  | awk -F"'" '{print $4}' | sort | uniq -c | sort -rn
```

Collisions need either a single localization point or distinct object names per handle.

### 7. Translator comment coverage in JS

Every `__()` / `_x()` in JS must be preceded by a translator comment if the string has placeholders or context ambiguity:
```js
// translators: %d is the number of items found
const label = sprintf( __( '%d items found', 'myplugin' ), count );
```

Same rule as `orbit-i18n` step 4, but on the JS side. POT extraction (`wp i18n make-pot`) scans JS — translator comments must be on the line directly above.

---

## Report format

```markdown
# i18n JS Parity Audit — [Plugin]

## Summary
- wp_localize_script calls:          <N>
- wp_set_script_translations calls:  <N>
- JS object names in use:            <N>
- Orphan JS reads (Critical):        <N>   ← list each
- Unused PHP emits (Info):           <N>
- JS handles missing translations:   <N>   ← list each
- Object-name collisions:            <N>
- JS strings without translator cmt: <N>

## Critical findings

### Orphan key: rnrdElEditor.generateFaq
**JS file:** assets/elementor-editor.js:142
**Read:** `rnrdElEditor.generateFaq`
**PHP localize site:** includes/class-rnrd-elementor.php:88
**Emitted keys:** generateSummary, regenerateSummary, generatingSummary
**Fix:** add `'generateFaq' => esc_html__( 'Generate FAQ', 'plugin-textdomain' )` to the array.

[Continue for every orphan]
```

---

## Plugin-agnostic detection algorithm

```
INPUT: plugin root directory
STEP 1: PHP_EMITS  = parse all wp_localize_script(handle, obj, keys[])
STEP 2: JS_READS   = parse all <obj>.<key> across JS files
STEP 3: ORPHANS    = JS_READS \ PHP_EMITS  (set difference)
STEP 4: UNUSED     = PHP_EMITS \ JS_READS  (set difference, low severity)
STEP 5: HANDLES_NO_TRANS = JS files using wp.i18n.__ \ wp_set_script_translations(handle)
STEP 6: OBJECT_COLLISIONS = JS objects emitted by >1 handle, both enqueued on same page
OUTPUT: severity-ranked finding list
```

---

## Cross-references

- **`orbit-i18n`** — gettext side (every `__()` call). Run both. Different surfaces.
- **`orbit-i18n-runtime`** — JSON encoding flags (different bug class).
- **WP.org Plugin Check** — has a similar but coarser scan; this skill catches more.
- **§10.10 in orbit-code-reviewer** — Tab/REST route slug mismatch (analogous bug class for navigation).

---

## Severity

- Orphan JS read (key absent in PHP):   **Critical** (silent English/undefined fallback)
- JS file using `__()` with no `wp_set_script_translations`: **Critical**
- Object-name collision on same page:   **High**
- JS string without translator comment (placeholder/context): **Medium**
- Unused PHP-emitted key:                **Info** (cleanup, not blocking)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.