Remove locale:missing; standardise on locale:download:missing#8204
Remove locale:missing; standardise on locale:download:missing#8204
Conversation
…d:missing) Uses POEditor filters=untranslated to download only untranslated terms per locale directly from the API, without needing a full locale:download first. - New: locale/scripts/poeditor-missing-downloader.js - New npm script: locale:download:missing - Updated: locale/README.md (documents both methods) - Updated: .agents/skills/churchcrm/locale-ai-translation.md skill docs Co-authored-by: DawoudIO <554959+DawoudIO@users.noreply.github.com>
- Delete locale/scripts/locale-build-missing.js - Remove locale:missing npm script from package.json - Update locale-translate.js error hint to locale:download:missing - Update poeditor-missing-downloader.js JSDoc (remove ref to deleted file) - Update locale/README.md, locale-ai-translation.md skill, locale-release.md Co-authored-by: DawoudIO <554959+DawoudIO@users.noreply.github.com>
- Replace npm run locale:missing with npm run locale:download:missing - Update commit step path from locale/missing-terms/ to locale/terms/missing/ - Update PR body references to new path Co-authored-by: DawoudIO <554959+DawoudIO@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR removes the redundant local diff–based missing-terms generator (locale:missing) and standardizes the workflow on downloading untranslated terms directly from POEditor via locale:download:missing.
Changes:
- Replace
locale:missingwithlocale:download:missingin npm scripts and related docs. - Add/update the POEditor missing-terms downloader workflow and commit paths to use
locale/terms/missing/. - Remove the old
locale-build-missing.jsscript and update references/messages accordingly.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
package.json |
Removes locale:missing and adds locale:download:missing script. |
locale/scripts/poeditor-missing-downloader.js |
Implements missing-terms download from POEditor and writes batched JSON under locale/terms/missing/. |
locale/scripts/locale-translate.js |
Updates the user hint to reference locale:download:missing. |
locale/scripts/locale-build-missing.js |
Deletes the old local diff–based missing-terms generator. |
locale/README.md |
Collapses missing-terms workflow to the single canonical POEditor-download approach. |
.github/workflows/locale-poeditor-download.yml |
Switches the workflow step to locale:download:missing and commits from locale/terms/missing/. |
.claude/commands/locale-release.md |
Updates release workflow docs to use locale:download:missing. |
.agents/skills/churchcrm/locale-ai-translation.md |
Updates AI translation skill quick start and scripts table to use locale:download:missing. |
| /** | ||
| * Fetch the untranslated terms for a locale from POEditor as a JSON object. | ||
| * Returns null when the locale has no untranslated terms (empty export). | ||
| */ | ||
| async function fetchUntranslatedTerms(poEditorLocale) { | ||
| const postData = new URLSearchParams({ | ||
| api_token: apiToken, | ||
| id: PROJECT_ID, | ||
| language: poEditorLocale, | ||
| type: 'key_value_json', | ||
| filters: 'untranslated', // POEditor filter: only export terms without a translation | ||
| }).toString(); | ||
|
|
||
| const response = await makeRequest(POEDITOR_API, postData); | ||
| const result = JSON.parse(response.data); | ||
|
|
||
| if (result.response.status !== 'success') { | ||
| throw new Error(result.response.message || 'Unknown POEditor error'); | ||
| } | ||
|
|
||
| const downloadUrl = result.result.url; | ||
|
|
||
| // Download the JSON file from the signed S3 URL | ||
| return new Promise((resolve, reject) => { | ||
| https | ||
| .get(downloadUrl, (res) => { | ||
| if (res.statusCode < 200 || res.statusCode >= 300) { | ||
| let body = ''; | ||
| res.on('data', (chunk) => { | ||
| body += chunk; | ||
| }); | ||
| res.on('end', () => | ||
| reject( | ||
| new Error( | ||
| `Download failed HTTP ${res.statusCode}: ${body.substring(0, 200)}`, | ||
| ), | ||
| ), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| let raw = ''; | ||
| res.on('data', (chunk) => { | ||
| raw += chunk; | ||
| }); | ||
| res.on('end', () => { | ||
| try { | ||
| const parsed = JSON.parse(raw); | ||
| resolve(parsed); | ||
| } catch (e) { | ||
| reject(new Error(`Failed to parse POEditor response: ${e.message}`)); | ||
| } | ||
| }); |
There was a problem hiding this comment.
fetchUntranslatedTerms() claims it returns null for an empty export, but the implementation always JSON.parse(raw) and will throw if the download body is empty/whitespace. Consider treating an empty download as “no missing terms” (e.g., return {}) and align the JSDoc + callers accordingly, so fully-translated locales don’t get counted as failures.
| const entries = Object.entries(missingTerms); | ||
| const files = []; | ||
| let batchNumber = 1; | ||
|
|
||
| for (let i = 0; i < entries.length; i += TERMS_PER_FILE) { | ||
| const batch = Object.fromEntries(entries.slice(i, i + TERMS_PER_FILE)); | ||
| const filename = path.join(localeOutDir, `${poEditorCode}-${batchNumber}.json`); | ||
| fs.writeFileSync(filename, JSON.stringify(batch, null, 2) + '\n'); | ||
| files.push({ number: batchNumber, filename, count: Object.keys(batch).length }); |
There was a problem hiding this comment.
Batch file contents are built from Object.entries(missingTerms) without any deterministic ordering. If POEditor changes export ordering, batching can churn and create noisy diffs/commits. Sort the entries (e.g., by key) before slicing into batches to keep output stable across runs.
| **Missing Terms Reports:** | ||
| For each incomplete locale, a `poeditor-{locale}.json` file contains all missing/untranslated terms from the master English list. These can be imported into POEditor to help translators complete the translations. | ||
| For each incomplete locale, batch files under `locale/terms/missing/{locale}/` contain all untranslated terms downloaded directly from POEditor. These can be used with `/locale-translate` or uploaded directly to POEditor. | ||
|
|
There was a problem hiding this comment.
The missing-terms output folder is keyed by POEditor language code (see locale/terms/missing/{poEditorCode}/ in the downloader and other docs). This PR body text uses {locale}, which can be different (e.g., pt_BR vs pt-br). Update the placeholder to {poEditorCode} to avoid confusion.
| ``` | ||
|
|
||
| This compares `locale/messages.json` against all installed `src/locale/i18n/*.json` files and writes fresh empty batch files to `locale/terms/missing/{locale}/`. | ||
| This queries POEditor with `filters=untranslated` for each locale and writes fresh empty batch files to `locale/terms/missing/{locale}/`. |
There was a problem hiding this comment.
The missing-terms directory name is the POEditor code (the --locale <poEditorCode> value), not necessarily the full app locale (e.g., fi_FI). Consider changing locale/terms/missing/{locale}/ to locale/terms/missing/{poEditorCode}/ for accuracy and consistency with the scripts.
| This queries POEditor with `filters=untranslated` for each locale and writes fresh empty batch files to `locale/terms/missing/{locale}/`. | |
| This queries POEditor with `filters=untranslated` for each locale and writes fresh empty batch files to `locale/terms/missing/{poEditorCode}/`. |
| Every new UI string added to ChurchCRM creates a translation gap in 45+ languages. The AI translation system bridges that gap at release time by: | ||
|
|
||
| 1. Reading empty batch files from `locale/terms/missing/{locale}/*.json` (generated by `npm run locale:missing`) | ||
| 1. Reading empty batch files from `locale/terms/missing/{locale}/*.json` (generated by `npm run locale:download:missing`) | ||
| 2. Translating missing values with Claude Code, applying denomination-aware church vocabulary | ||
| 3. Writing translated values back into the same batch files |
There was a problem hiding this comment.
This path placeholder uses {locale}, but the batch directories are keyed by POEditor language code (what entry.poEditor maps to and what /locale-translate --locale <code> expects). Recommend updating to locale/terms/missing/{poEditorCode}/*.json to match the actual folder layout.
The local-diff approach (
locale-build-missing.js/locale:missing) was redundant — POEditor's ownfilters=untranslatedexport API already computes missing terms server-side with no prior full download required. This removes the old script and wires everything tolocale:download:missing.Changes
locale/scripts/locale-build-missing.jspackage.json— removedlocale:missingscriptlocale/scripts/locale-translate.js— updated fallback hint tolocale:download:missinglocale/scripts/poeditor-missing-downloader.js— cleaned up JSDoc (dropped cross-references to deleted file)locale/README.md— collapsed two-option workflow to single canonical approach; removedlocale:missingfrom scripts table and quick-reference block.github/workflows/locale-poeditor-download.yml— replacednpm run locale:missingstep withnpm run locale:download:missing; updated commit step paths fromlocale/missing-terms/poeditor-*.json→locale/terms/missing/.agents/skills/churchcrm/locale-ai-translation.mdand.claude/commands/locale-release.md— Quick Start and scripts table updated tolocale:download:missing💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.