Skip to main content

i18n-memsource

Automates the Memsource/Phrase i18n translation workflow for networking-console-plugin. Use when the user asks to upload translations, download translations, check translation status, memsource upload, memsource download, i18n upload, i18n download, send for translation, or get translations.

Source facts

Repository
openshift/networking-console-plugin
Last source activity
October 13, 2026 at 19:51
Detected SKILL.md language
English
Stars
8
Forks
40

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
i18n-memsource
description
Automates the Memsource/Phrase i18n translation workflow for networking-console-plugin. Use when the user asks to upload translations, download translations, check translation status, memsource upload, memsource download, i18n upload, i18n download, send for translation, or get translations.
# networking-console-plugin i18n Memsource Workflow Manages upload/download of translations to Phrase (Memsource) for this repo. Uses the **existing** local `i18n-scripts/` + `i18next-parser` setup. Does **not** use `ocp-plugin-i18n-scripts` or `i18next-cli`. For a peer-oriented walkthrough, see [USAGE.md](./USAGE.md). ## State Read and update `.cursor/skills/i18n-memsource/state.json` after each upload. ## Plugin config | Field | Value | | ----------------------- | ------------------------------------------------------------------------------------------ | | Namespace / locale file | `plugin__networking-console-plugin` | | Memsource template ID | `zBOwr4BxYwEq7xlJ37c1F3` | | Project title | `[OCP $VERSION] UI Localization networking-console-plugin - Sprint $SPRINT/Branch $BRANCH` | | Languages | `ja`, `zh-cn`, `ko`, `fr`, `es` | | Locale dirs on disk | `en`, `es`, `fr`, `ja`, `ko`, **`zh`** (not `zh-cn`) | | PO filename pattern | `po-files/<lang>/plugin__networking-console-plugin.po` (no `public__` prefix) | ## Prerequisites ### Memsource CLI `memsource` is often installed but **not** on the default `PATH`. Resolve it first: ```bash MEMSOURCE_BIN=$(python3 -c "import shutil; print(shutil.which('memsource') or '')") if [ -z "$MEMSOURCE_BIN" ]; then MEMSOURCE_BIN=$(find "$HOME/Library/Python" -name memsource -type f 2>/dev/null | head -1) fi # Also common: $HOME/git/memsource-cli-client/.memsource/bin/memsource export PATH="$(dirname "$MEMSOURCE_BIN"):$PATH" command -v memsource >/dev/null || { echo "ERROR: memsource not found on PATH"; exit 1; } ``` ### Authentication (credentials stay with the user) **Do not** read `~/.memsourcerc`, Memsource passwords, or long-lived tokens into the agent context. Phrase is a paid external service — treat credentials like any other secret. Never paste login tables or tokens into chat. Preferred flow: 1. Ask the user to authenticate in **their own terminal** (not an agent-controlled shell) and confirm `memsource auth whoami` works. 2. The agent may run extract/export/validation without credentials. 3. For upload/download/status, the user runs authenticated `memsource-*` / `memsource job list` commands in **their** terminal after the agent prepares artifacts. Never export `MEMSOURCE_TOKEN` into an agent-controlled shell (even short-lived — the agent can read process env). **User-terminal auth (required for all Memsource API calls):** ```bash export PATH="$HOME/Library/Python/3.9/bin:$PATH" # adjust if needed source ~/.memsourcerc export MEMSOURCE_TOKEN=$(memsource auth login \ --user-name "$MEMSOURCE_USERNAME" \ --password "$MEMSOURCE_PASSWORD" \ -f json \ | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])") # Do not echo MEMSOURCE_TOKEN. Login alone is not enough — without this export, # later commands fail with 401 "auth: not logged". memsource auth whoami ``` Also ensure `jq` is installed (`brew install jq`) — upload scripts need it. --- ## Critical nuances ### 1. zh-cn vs zh filesystem mismatch `i18n-scripts/languages.sh` uses `zh-cn`, but locales live under `locales/zh/`. Without a symlink, `i18n-to-po` cannot merge existing Chinese translations and uploads empty msgstr for Chinese. **Before any `export-pos` during upload prep**, reject a non-symlink `locales/zh-cn`, then register cleanup (symlink-only removal) and create the symlink. Keep symlink + trap in the **same shell session** through validation. `memsource-upload.sh` also creates and cleans the symlink around its own `export-pos` re-run, so authenticated upload in a separate shell is safe: ```bash # Reject a non-symlink path before registering cleanup (avoid deleting real files) if [ -e locales/zh-cn ] && [ ! -L locales/zh-cn ]; then echo "ERROR: locales/zh-cn exists and is not a symlink; resolve manually" exit 1 fi trap 'if [ -L locales/zh-cn ]; then rm -f locales/zh-cn; fi; rm -rf po-files locales/tmp' EXIT ln -sfn zh locales/zh-cn ``` Download already maps `zh-cn` → `zh`. ### 2. PO generation preserves existing translations `i18n-to-po.js`: 1. Start from English keys in `locales/en/` 2. Clear values (empty placeholders) 3. Merge existing values from `locales/<lang>/` 4. Convert to PO via `i18next-conv` Never skip `export-pos` or hand-build English-only POs. ### 3. English placeholders must be empty in uploaded POs `i18next-parser` + `useKeysAsDefaultValue: true` can leave English text in secondary locale JSON. Old `i18n-to-po` copies that into `msgstr`, which Phrase may treat as already translated. **Desired PO state before upload:** | Locale JSON value | msgstr in PO | | --------------------------------------- | ----------------------------- | | Real non-English translation | Keep it (carry forward) | | Empty `""` | Empty (needs translation) | | English placeholder (== English source) | **Empty** (needs translation) | `export-pos.sh` runs `i18n-scripts/clear-english-msgstr.js` after generating POs to clear `msgstr` when it equals `msgid`. Because `memsource-upload.sh` re-runs `export-pos`, that clear also applies to the files that get uploaded. This clear step is **redundant** if the repo migrates to [`ocp-plugin-i18n-scripts`](https://github.com/avivtur/ocp-plugin-i18n-scripts), which filters English placeholders during PO generation. ### 4. Download clean-git check is wrong `memsource-download.sh` checks `public/locales` / `packages/**/locales`, but this repo uses root `locales/`. Before download, verify: ```bash git status --short --untracked-files -- locales/ ``` --- ## Action 1: Upload Translations Trigger: "upload translations", "memsource upload", "i18n upload", "send for translation" ### Checklist ```text Upload Progress: - [ ] Step 1: Load state - [ ] Step 2: Get VERSION from user, auto-increment SPRINT - [ ] Step 3: Confirm user-provided Memsource auth (no credential access) - [ ] Step 4: Extract translation keys - [ ] Step 5–8: Symlink + export + validate; user-terminal upload (script owns symlink) - [ ] Step 9: Cleanup and update state ``` ### Step 1: Load state Read `.cursor/skills/i18n-memsource/state.json`. ### Step 2: Get VERSION and SPRINT - Always ask the user for VERSION. Do not assume. - Auto-increment SPRINT from state (previous + 1). - Show: "Uploading VERSION X, Sprint Y (branch: Z)" ```bash git branch --show-current ``` ### Step 3: Confirm authentication Confirm the user will run authenticated Memsource commands in **their** terminal. Do not source `~/.memsourcerc`, and do not use or request `MEMSOURCE_TOKEN` in an agent-controlled shell. ### Step 4: Extract translation keys ```bash npm run i18n # → ./i18n-scripts/build-i18n.sh && node ./i18n-scripts/set-english-defaults.js ``` Then show: ```bash git status --short -- locales/ git diff --stat -- locales/ ``` Require approval if the locale diff looks wrong. ### Steps 5–8: Symlink, export, validate, then upload `memsource-upload.sh` **owns** the `locales/zh-cn` → `zh` symlink (creates it, re-runs `export-pos`, cleans up via `EXIT` trap). Agent prep and authenticated upload may be separate shells. Still validate POs before upload so English leaks are caught early. ```bash set -euo pipefail if [ -e locales/zh-cn ] && [ ! -L locales/zh-cn ]; then echo "ERROR: locales/zh-cn exists and is not a symlink; resolve manually" exit 1 fi trap 'if [ -L locales/zh-cn ]; then rm -f locales/zh-cn; fi; rm -rf po-files locales/tmp' EXIT ln -sfn zh locales/zh-cn rm -rf po-files npm run export-pos # export-pos ends with: node ./i18n-scripts/clear-english-msgstr.js # Validate (fail closed on missing POs / english leaks) — must exit before upload for lang in ja zh-cn ko fr es; do echo "=== $lang ===" python3 -c " import re, glob, sys files = glob.glob(f'po-files/{sys.argv[1]}/*.po') if not files: raise SystemExit(f'missing PO files for {sys.argv[1]}') content = ''.join(open(f).read() for f in files) entries = re.findall(r'msgid \"((?:\\\\.|[^\"])*)\"\s*msgstr \"((?:\\\\.|[^\"])*)\"', content) entries = [(a,b) for a,b in entries if a] translated = sum(1 for a,b in entries if b and b != a) needs = sum(1 for a,b in entries if not b) leaks = sum(1 for a,b in entries if b and b == a) print(f' total={len(entries)} translated={translated} needs_translation={needs} english_leaks_remaining={leaks}') if leaks: raise SystemExit('english_leaks_remaining must be 0') " "$lang" done # After validation: present summary and get explicit approval, then # USER terminal (authenticated) only — upload re-exports with its own symlink: npm run memsource-upload -- -v "$VERSION" -s "$SPRINT" # capture PROJECT_ID (.uid) from memsource project create output ``` Present plugin, version, sprint, branch, validation results, and project title before the user runs upload. - **translated** = non-empty msgstr and msgstr ≠ msgid - **needs translation** = empty msgstr - **english_leaks remaining** = msgstr == msgid (must be 0) ### Step 9: Cleanup and update state Explicit cleanup (trap also covers failure paths; only remove a symlink): ```bash if [ -L locales/zh-cn ]; then rm -f locales/zh-cn fi rm -rf po-files locales/tmp ``` Update `state.json` with `version`, `sprint`, `lastProjectId`, `memsourceProjectUrl`, and history. Draft notification: ```text Subject: [OCP VERSION] Translation Upload - networking-console-plugin Sprint SPRINT Hi Localization Team, New translation strings have been uploaded for networking-console-plugin (OCP VERSION, Sprint SPRINT). Memsource project: https://cloud.memsource.com/web/project2/show/PROJECT_ID Languages: ja, zh-cn, ko, fr, es Total keys: N Please review and translate at your convenience. Let us know when translations are ready for download. Thanks ``` --- ## Action 2: Download Translations Trigger: "download translations", "memsource download", "i18n download", "get translations" ### Checklist ```text Download Progress: - [ ] Step 1: Load state / confirm PROJECT_ID - [ ] Step 2: Confirm user-provided Memsource auth - [ ] Step 3: Check translation status - [ ] Step 4: Ensure locales/ is clean - [ ] Step 5: Download translations - [ ] Step 6: Show diff summary - [ ] Step 7: Create PR (optional) ``` ### Step 1: Confirm PROJECT_ID Show `lastProjectId` from state; ask to confirm or override. ### Step 2: Confirm authentication Same as upload Step 3 — user-owned credentials only; no `~/.memsourcerc` in agent context. ### Step 3: Status Omit `-c`, or pass **separate** `-c` flags. Comma-separated `-c uid,status,...` is treated as one invalid column name. Column is `target_lang` (not `targetLang`). ```bash # USER terminal (MEMSOURCE_TOKEN already exported) for lang in ja zh-cn ko fr es; do echo "=== $lang ===" memsource job list \ --project-id "$PROJECT_ID" \ --target-lang "$lang" \ -f json # optional: -c uid -c status -c target_lang done ``` Warn if not all completed; ask before proceeding. ### Step 4: Clean locales ```bash git status --short --untracked-files -- locales/ # Must be clean — commit or stash first ``` ### Step 5: Download Load `SPRINT` from `state.json` (or ask the user). Create/switch to the PR branch **before** download (the script auto-commits): ```bash
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub