← CrowdinCONTENT HISTORY
WHAT CHANGED
Update to Crowdin
Snapshot Oct 28, 2026 · 13:09 UTC · version 1.8.0
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save Crowdin and check future changes in Following.
Up to 10 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Generates a starting glossary for a Crowdin project from the project's own source strings and uploads it with the Crowdin CLI. Use when someone wants to create a glossary, extract key terms or terminology from an app's strings, mark product and feature names as do-not-translate, or seed a Crowdin project with consistent vocabulary - including bare phrasings like \"make a glossary for my project\". Terms are drafted for review and never uploaded unseen, and translations are never invented. Not for managing an existing glossary's entries one by one, and not for the full internationalize-and-connect journey (that is i18n-setup, which delegates here).",
"included_files": [],
"name": "glossary-generation",
"skill_md_contents": "---\nname: glossary-generation\ndescription: Generates a starting glossary for a Crowdin project from the project's own source strings and uploads it with the Crowdin CLI. Use when someone wants to create a glossary, extract key terms or terminology from an app's strings, mark product and feature names as do-not-translate, or seed a Crowdin project with consistent vocabulary - including bare phrasings like \"make a glossary for my project\". Terms are drafted for review and never uploaded unseen, and translations are never invented. Not for managing an existing glossary's entries one by one, and not for the full internationalize-and-connect journey (that is i18n-setup, which delegates here).\n---\n\n# Glossary generation\n\nTranslators meet a product's vocabulary one string at a time, with no way to tell which words are the product's own. A glossary is where a term gets decided once — translated one way everywhere, or not translated at all — and it is worth most before the first translation is made rather than after fifty of them disagree.\n\nThe glossary this skill produces is a **starting point, not a finished asset**. Ten to thirty terms a localization manager can accept, edit, or delete beats two hundred that read as noise; the second kind gets deleted wholesale, which also throws away the few good entries in it. Treat forty terms as a hard ceiling, and prefer leaving a doubtful term out — a missing entry costs one inconsistent translation, a wrong one teaches every translator the wrong answer.\n\nFor the `glossary` command's full flag set, read the `crowdin-cli` skill's [commands reference](../crowdin-cli/references/commands.md#glossary); below are only the flags this skill passes.\n\n## Inputs\n\nThree things, resolved before any term is picked:\n\n- **The source strings.** Read the **source catalog** — the file `crowdin.yml` uploads, at its resolved `source` pattern — not the source tree. Those are exactly the strings translators will see, already stripped of code. When there is no `crowdin.yml`, ask the user where the source-language strings live rather than scanning code for them.\n- **A one-line product description.** This is what makes \"brew\" a domain term in a coffee-logging app and an ordinary verb anywhere else; without it, term selection degenerates into picking capitalized words. Ask for it if nothing in the conversation or repository supplies one.\n- **A draft location outside version control** — a scratch directory, or a gitignored path the caller names — so a rejected draft never lands in the repository.\n\n## What earns an entry\n\nA term qualifies when a translator, seeing it in one string with no other context, could reasonably get it wrong. In practice that is four kinds:\n\n- **Product and feature names.** Brand names, plan names, named surfaces — usually the entries that say *do not translate this*. This is the single most valuable category and the one most often missing.\n- **Domain vocabulary the product leans on.** Words with a specific meaning in this product's world, particularly ones that recur across strings.\n- **Words that are ambiguous out of context.** English's noun/verb pairs are the classic case: on its own, a `Log` label could be a record or an action, and a translator has to pick a word class before they can pick a word.\n- **UI objects that must stay consistent with each other.** When a product distinguishes two similar things, translating them to the same word collapses a distinction the interface depends on.\n\nRecurrence is the strongest signal: a term in two or more source strings is a candidate on frequency alone. A term appearing once needs one of the other reasons — a brand name earns its entry the first time it is used.\n\n**What does not earn an entry:** ordinary UI verbs every translator has already translated a thousand times (`Save`, `Cancel`, `Delete`) unless this product uses one in a non-obvious way; whole sentences or message strings, which are strings to translate rather than terms to define; and anything you would be guessing at, including a term whose meaning you could not determine from the strings and the product description together.\n\nEach entry carries a **description**, and it answers a practical question — what this is in this product, and whether it should be translated at all — rather than defining the English word. \"The paid tier; a plan name, keep in English\" is useful. \"A plan is an intention to do something\" is not.\n\nNever invent translations. The glossary goes up with source-language terms only; translations are the translators' work, and a machine-guessed one in a glossary is a wrong answer wearing the authority of a decision.\n\n## Draft the CSV\n\nWrite the terms to the draft location:\n\n```csv\nterm_en,description_en\nBrewLog,The product name. Keep in English everywhere.\nbrew,A single coffee preparation the user records. Not a beverage in general.\ncup,One serving logged against a brew. Countable; appears in plural forms.\nHome barista,Free plan name. Keep in English.\n```\n\nThe header uses the **source locale's Crowdin language id** (`term_en`, `description_en` for an `en` source), which is the language id, not whatever code scheme the project spells its locale directories in — those two are allowed to differ, and the glossary API only ever speaks Crowdin ids.\n\nThen show the user the terms and stop. This is a real gate: the file is small, plainly readable, and about to become an asset their translators treat as authoritative. Ask for an explicit go-ahead, and take edits before uploading rather than after.\n\n## Upload\n\nBoth commands are authenticated: the token comes from the environment (`CROWDIN_PERSONAL_TOKEN`, or whatever `crowdin.yml`'s `api_token_env` names), and its value is never asked for, echoed, or written. A gitignored `.env` at the project root is enough to supply it — the CLI reads `.env` from its working directory natively.\n\n**First, check for an existing glossary** — uploading without an id creates a *second* glossary rather than merging:\n\n```bash\ncrowdin glossary list --assigned\n```\n\n- **A glossary already exists** — do not create another; import into it: `crowdin glossary upload <file> --id <id>`. Read its term count before saying anything about the merge. **`terms: 0` is an empty glossary Crowdin created with the project**, which happens on every new project and means there is nothing to preserve — import and move on. A **non-zero count is an asset somebody curated**: say plainly that the import merges into it, and let the user decline, which a project with a mature glossary usually should.\n\n `--assigned` lists only the glossaries assigned to the project in `crowdin.yml`, which on most projects is exactly one; without the flag the list is account-wide and the project's own glossary has to be picked out by name.\n- **No glossary exists** — create one: upload without `--id`, passing `--language` the Crowdin language id of the source locale:\n\n```bash\ncrowdin glossary upload glossary.csv \\\n --language en \\\n --scheme term_en=0,description_en=1 \\\n --first-line-contains-header\n```\n\nThe upload prints the new glossary's id — report it back, and record it wherever the caller keeps decisions: it is what a later run passes to `--id` instead of creating a duplicate.\n\nThe import is asynchronous. `glossary upload` prints `Imported in #<id>` while `glossary list` can still report `terms: 0`; the count catches up moments later. Confirm the term count by re-reading it, so a race is not mistaken for a failed import.\n\n## Failure and re-runs\n\n- **The upload fails** — the terms survive in the draft CSV, so nothing has to be regenerated. Read the error before changing the file: an authentication or permission failure is not a malformed CSV.\n- **The run repeats on the same project** — `glossary list` is what makes it idempotent. Without it, every re-run leaves another glossary behind, and a project with three glossaries called after the same file is worse off than one with none.\n- **The user declines the upload** — that is a complete outcome, not a failure. Leave the CSV in place, tell them where it is and what command would upload it, and stop.\n\n## Invoked by another workflow?\n\nA calling workflow (the `i18n-setup` journey is one) may pass the inputs directly instead of being asked for them: the source catalog path, the product description, the draft path, and the source locale's Crowdin language id. Honor them as given. The contract back is small: the draft CSV at the path it named, the review gate still real (the caller's user, not the caller, says go), and one of two outcomes reported — the uploaded glossary's id, or \"declined, draft kept at `<path>`\". Both are complete outcomes.\n\n## Related skills\n\n- **`crowdin-cli`** — the authoritative surface for `crowdin glossary` and every other command this skill runs.\n- **`i18n-setup`** — the full internationalize-and-connect journey; its phase 6 delegates its `generate_glossary` and `upload_glossary` steps here.\n"
}SHA-256 of public snapshot: 6f22b3eb7bdf51f804a7010d6265f0000d5ba274d58659c0e8964bf1aaa2d5fa