From harness-claude
Manages i18n workflow: configures settings, scaffolds translation files, extracts strings, tracks coverage, generates pseudo-localization, retrofits projects for multi-language support.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Translation lifecycle management. Configure i18n settings, scaffold translation files, extract translatable strings, track coverage, generate pseudo-localization, and retrofit existing projects with internationalization support.
Verifies i18n compliance by detecting hardcoded strings, missing translations, locale-sensitive formatting, RTL issues, and concatenation anti-patterns across web, mobile, and backend codebases.
Safely extracts and mass-converts hardcoded strings to i18n t() calls in frontend codebases using multi-pass batching, parallel language dispatch, 8 audit gates, and HTML integrity checks. For >10 strings.
Share bugs, ideas, or general feedback.
Translation lifecycle management. Configure i18n settings, scaffold translation files, extract translatable strings, track coverage, generate pseudo-localization, and retrofit existing projects with internationalization support.
on_project_init)on_project_init triggers fire and the user has indicated multi-language supportCheck for existing configuration. Read harness.config.json and look for an i18n block.
i18n block exists: load it and report current settings. Ask the user if they want to modify any settings.i18n block: proceed with guided setup.Auto-detect project context. Before asking questions, gather context:
package.json, Podfile, build.gradle, pubspec.yaml to detect platform(s) (web, mobile, backend).agents/skills/shared/i18n-knowledge/frameworks/.locales/, src/locales/, public/locales/, assets/translations/, res/values*/).Present opinionated defaults. Show the user the recommended configuration based on detection results:
i18n Configuration (recommended defaults)
==========================================
Source locale: en
Target locales: [ask user]
Framework: [auto-detected or recommend based on platform]
Message format: ICU MessageFormat (industry standard, widest tooling support)
File format: JSON (universal, every TMS imports/exports it)
Key convention: dot-notation namespaced by feature (e.g., checkout.summary.totalLabel)
Directory: locales/{locale}/{namespace}.json
Strictness: standard
Pseudo-locale: en-XA (auto-generated for testing)
Gather required user input. The following must be answered by the user:
Write configuration. Update harness.config.json with the i18n block:
{
"i18n": {
"enabled": true,
"strictness": "standard",
"sourceLocale": "en",
"targetLocales": ["es", "fr", "de"], // user-specified
"framework": "auto", // or detected framework
"format": "json",
"messageFormat": "icu",
"keyConvention": "dot-notation",
"translationPaths": {
"web": "locales/{locale}/{namespace}.json",
},
"platforms": ["web"], // detected or confirmed
"coverage": {
"minimumPercent": 95,
"requirePlurals": true,
"detectUntranslated": true,
},
"pseudoLocale": "en-XA",
},
}
If harness.config.json does not exist, create it with { "version": 1, "i18n": { ... } }.
If it already exists, merge the i18n block into the existing config using the Edit tool.
Report configuration summary. Display the final configuration and confirm with the user before proceeding to scaffold.
Determine scaffold mode. Check if the project already has source code with user-facing strings:
Greenfield scaffolding. Create the translation directory structure based on config:
Create directory for each locale: locales/en/, locales/es/, locales/fr/, etc.
Create initial namespace file for each locale (e.g., locales/en/common.json, locales/es/common.json).
Source locale file gets a starter template:
{
"app": {
"name": "",
"description": ""
},
"common": {
"loading": "Loading...",
"error": "Something went wrong",
"retry": "Try again",
"cancel": "Cancel",
"confirm": "Confirm",
"save": "Save",
"delete": "Delete",
"edit": "Edit",
"back": "Back",
"next": "Next"
}
}
Target locale files get the same keys with empty string values (to be translated).
Create pseudo-locale directory (locales/en-XA/) -- will be populated in Track phase.
Retrofit scaffolding. Run the retrofit workflow:
a. Invoke harness-i18n detect + scan phases to assess current state. This produces:
b. Report audit results:
Retrofit Audit Results
======================
Hardcoded strings found: 47
Existing translation keys: 0
Existing locales configured: 0
Estimated extraction effort: Medium (47 strings across 12 files)
c. Generate initial key catalog. For each detected hardcoded string:
components.header.welcomeTitle)d. Scaffold translation files with the generated key catalog for the source locale.
e. Create empty target locale files with the same key structure and empty values.
f. Present the key catalog to the user for review. This is a checkpoint -- the user should review and approve key names before they become permanent.
[checkpoint:human-verify] -- User must review generated key catalog before proceeding.
Framework-specific setup. Based on the detected or configured framework:
i18next.config.ts (or .js) with namespace configuration, backend plugin setup, and language detection.IntlProvider is set up in the app root. If not, provide instructions for adding it.i18n.ts plugin configuration. Verify it is installed in the Vue app.l10n.yaml configuration. Set up intl package in pubspec.yaml.Report scaffold results. Display what was created:
Scaffold Results
================
Directories created: 4 (locales/en, locales/es, locales/fr, locales/en-XA)
Files created: 4 (common.json per locale + pseudo-locale placeholder)
Keys scaffolded: 10 (common starter keys)
Framework config: i18next.config.ts created
Next step: Run extract phase to populate translation files from source code
Read configuration. Load harness.config.json i18n block for:
dot-notation, snake_case, camelCase)Scan source code for extractable strings. Use harness-i18n scan results or run a fresh scan. For each detected hardcoded user-facing string, collect:
Generate translation keys. For each extractable string, generate a key following the configured convention:
{feature}.{component}.{description}
checkout.summary.totalLabel for "Total" in CheckoutSummary.tsxauth.login.submitButton for "Sign in" in LoginForm.tsx{feature}_{component}_{description}{feature}{Component}{Description}`Hello ${name}` becomes "greeting.hello": "Hello {name}"items.length === 1 ? "item" : "items" becomes "cart.itemCount": "{count, plural, one {# item} other {# items}}"Preserve existing translations. Before writing to translation files:
Flag strings needing human review. Mark strings that the agent cannot confidently extract:
[REVIEW] markers in the output.Write extraction results. Update translation files:
Report extraction results:
Extraction Results
==================
Strings scanned: 47
Keys generated: 42
Keys already existed: 5 (preserved)
Flagged for review: 8 [REVIEW]
Files updated: 12 (4 source files wrapped, 8 translation files updated)
Review Required
---------------
[REVIEW] src/components/Header.tsx:5 -- "Welcome" -- ambiguous context (greeting vs. page title)
[REVIEW] src/utils/format.ts:12 -- "USD" -- may be intentional (currency code, not translatable)
Calculate per-locale coverage. For each target locale:
(translated / total) * 100.agents/skills/shared/i18n-knowledge/locales/{locale}.yaml).git diff on the source locale file. Run git log --diff-filter=M -p -- {sourceLocalePath} to identify keys whose source values changed since the last commit that touched the target locale file. A translation is stale when the source value changed but the target was not updated in a subsequent commit. If git history is unavailable, fall back to heuristic detection: flag target values that are identical to the current source value (likely untranslated) or that contain substrings of the old source (partial staleness).Generate coverage dashboard:
Translation Coverage Dashboard
==============================
Source locale: en (142 keys)
Locale Translated Coverage Untranslated Stale Missing Plurals
es 128 90.1% 14 3 2
fr 135 95.1% 7 1 0
de 120 84.5% 22 5 4
ja 115 81.0% 27 2 1
ar 110 77.5% 32 4 6
Coverage threshold: 95% (from config)
Passing locales: fr
Failing locales: es, de, ja, ar
Report untranslated keys. For each locale below the coverage threshold, list the missing keys:
Untranslated Keys: es (14 missing)
-----------------------------------
checkout.payment.cardLabel
checkout.payment.expiryLabel
checkout.payment.cvvLabel
settings.notifications.emailToggle
...
Report stale translations. List keys where the source changed but the target was not updated:
Stale Translations: es (3 stale)
---------------------------------
Key: auth.login.heading
Source (old): "Sign In"
Source (new): "Log In"
Target (es): "Iniciar Sesion" (based on old source)
Action: Review and update Spanish translation
Generate pseudo-localization. Create a pseudo-locale file (default: en-XA) that transforms source strings to test for:
[ and ] to detect truncation and overflow.{name}, {count, plural, ...}) must NOT be modified.<strong>, <br/>, <Link> must NOT be modified.Example transformations (showing both expansion and accent replacement):
"Save" -> "[Šàvëë]"
"Hello {name}" -> "[Hëëllöö {name}]"
"Cancel" -> "[Çààñçëël]"
"{count, plural, one {# item} other {# items}}" -> "[{count, plural, one {# ìtëëm} other {# ìtëëmš}}]"
Write the pseudo-locale file to the configured pseudo-locale path (e.g., locales/en-XA/common.json).
Recommend MCP servers. Based on the project's workflow needs, recommend MCP server integrations from agents/skills/shared/i18n-knowledge/mcp-interop/:
MCP Server Recommendations
==========================
Based on your project configuration:
Tolgee MCP (recommended)
- Best for: TMS integration, in-context translation, developer workflow
- Why: You have 5 target locales and are using i18next -- Tolgee has native i18next support
- Setup: Install @tolgee/mcp, configure with your project API key
Lingo.dev MCP (optional)
- Best for: Brand voice consistency, glossary enforcement, multi-engine MT
- Why: Useful if you want automated translation with quality controls
i18next MCP (optional)
- Best for: Project analysis, coverage tracking, key management
- Why: Direct i18next integration for project-level insights
Load recommendation logic from the MCP interop profiles. Recommend based on:
i18n.mcp is already configured, validate the choice)If graph is available (.harness/graph/ exists): provide per-component/route coverage. Map translation keys to the components that use them and report coverage by feature area:
Component Coverage (graph-enhanced)
====================================
Component Keys es fr de
CheckoutFlow 24 92% 100% 83%
AuthPages 18 89% 94% 78%
Settings 31 87% 97% 81%
Dashboard 15 93% 93% 93%
If graph is unavailable: report per-file key-level coverage as shown in step 2.
Suggest next actions based on coverage results:
harness validate -- Run after configuration changes and file scaffolding to verify project health.harness-i18n -- The workflow skill depends on the core i18n skill for framework detection and string scanning. The detect + scan phases of harness-i18n are invoked during retrofit scaffolding and extraction.harness-initialize-project -- During project init, if the user indicates multi-language support, the configure + scaffold phases are invoked automatically.harness-release-readiness -- Coverage data from the track phase feeds into release readiness checks. Per-locale coverage is compared against i18n.coverage.minimumPercent.agents/skills/shared/i18n-knowledge/ -- Framework profiles guide scaffolding and extraction. Locale profiles provide CLDR plural rules for coverage tracking. MCP interop profiles drive server recommendations.i18n block in harness.config.json with opinionated defaultsharness validate passes after all changesContext: New React web app. User wants to support English, Spanish, French.
Phase 1: CONFIGURE
User runs harness skill run harness-i18n-workflow.
i18n Configuration (recommended defaults)
==========================================
Source locale: en
Target locales: [input required]
Framework: i18next (detected from package.json)
Message format: ICU MessageFormat
File format: JSON
Key convention: dot-notation
Directory: locales/{locale}/{namespace}.json
Strictness: standard
Pseudo-locale: en-XA
What target locales will this project support? (BCP 47 codes, comma-separated)
> es, fr
Any industry-specific requirements? (fintech, healthcare, ecommerce, legal, gaming, or none)
> none
Configuration written to harness.config.json.
Phase 2: SCAFFOLD
Scaffold Results
================
Directories created: 3 (locales/en, locales/es, locales/fr) + locales/en-XA
Files created: 4 (common.json per locale + pseudo-locale placeholder)
Keys scaffolded: 10 (common starter keys)
Framework config: Verified -- i18next already configured in src/i18n.ts
Next step: Develop features, then run extract phase to populate translation files
Phase 3: EXTRACT (after development)
Extraction Results
==================
Strings scanned: 23
Keys generated: 20
Keys already existed: 3 (common.loading, common.error, common.retry)
Flagged for review: 2 [REVIEW]
Files updated: 6 (3 translation files + 3 source files with t() wrapping instructions)
Phase 4: TRACK
Translation Coverage Dashboard
==============================
Source locale: en (23 keys)
Locale Translated Coverage Untranslated Stale Missing Plurals
es 0 0.0% 23 0 0
fr 0 0.0% 23 0 0
Coverage threshold: 95% (from config)
Passing locales: none
Failing locales: es, fr
Pseudo-locale generated: locales/en-XA/common.json (23 keys transformed)
MCP Server Recommendations
==========================
Tolgee MCP (recommended) -- native i18next support, in-context translation
Context: Existing Express.js backend with hardcoded error messages. User wants to add i18n.
Phase 1: CONFIGURE
Auto-detection finds: platform = backend, framework = none, no translation files.
i18n Configuration
==================
Source locale: en
Target locales: > es, de, ja
Framework: none detected -- recommending i18next for Node.js backend
Message format: ICU MessageFormat
File format: JSON
Key convention: dot-notation
Directory: locales/{locale}/{namespace}.json
Platforms: backend
Phase 2: SCAFFOLD (retrofit mode)
Retrofit Audit Results
======================
Hardcoded strings found: 31
Existing translation keys: 0
Existing locales configured: 0
Estimated extraction effort: Medium (31 strings across 8 files)
Generated Key Catalog (review required)
========================================
errors.auth.invalidCredentials -> "Invalid email or password"
errors.auth.tokenExpired -> "Session expired, please log in again"
errors.validation.requiredField -> "This field is required"
errors.notFound.resource -> "Resource not found"
emails.welcome.subject -> "Welcome to {appName}"
emails.welcome.greeting -> "Hello {name}, welcome aboard!"
...
[checkpoint] Please review the generated key names above.
Approve to continue scaffolding, or provide corrections.
| Rationalization | Reality |
|---|---|
| "The user already told me they want Spanish and French — I can skip the configuration phase and go straight to scaffolding." | The configuration phase writes the i18n block to harness.config.json. Without it, subsequent runs of harness-i18n have no enabled flag, no strictness level, and no locale list to work against. Verbal confirmation does not substitute for written config. |
| "In retrofit mode, the key naming is straightforward — I'll apply the generated key catalog directly without showing it to the user for review." | The retrofit key catalog checkpoint is a hard gate. Key names become permanent identifiers that translation teams, TMS tools, and source code will reference for years. The user must review and approve them before any files are written. |
"The pseudo-locale transformation for this string with {name} is obvious — I'll just wrap the entire string including the placeholder." | ICU MessageFormat placeholders must be preserved exactly. Transforming {name} to {ñàmë} breaks the interpolation at runtime. The pseudo-locale algorithm must detect and skip all placeholder syntax before applying accent and expansion transforms. |
| "These target locale files already exist from a previous run — I'll overwrite them with the new extraction output to keep things clean." | Existing target locale translations must never be overwritten. A key with a translated (non-empty, non-source-identical) value in a target locale represents real translation work. Overwriting it destroys that work silently. Always preserve existing translations. |
| "We found 120 strings in retrofit mode — I'll just run the full extraction without the audit phase since we clearly need everything extracted." | The retrofit audit results are what tell the user how much effort the extraction requires and let them prioritize high-traffic flows. Skipping the audit and going straight to extraction removes the user's ability to scope the work before it happens. |
These are hard stops. Violating any gate means the process has broken down.
{name}, {count, plural, ...}) and HTML/JSX tags must be preserved exactly in pseudo-locale output..heading vs .label vs .button) and flag for human review.