Skip to main content
Glossary Terms Extraction logo
Glossary Terms Extraction FreeSystem

Mines your source content for glossary candidates and adds them in small, reviewed rounds

SKILL.md

Glossary Terms Extraction

Copy link

Help the user grow a Crowdin glossary by mining their source content for high-quality candidates and adding them in small, user-reviewed rounds. The goal is good coverage of brand, feature/entity, and domain-specific terminology — not racing to a number.

The source content can be anything Crowdin translates: a SaaS product's UI strings, documentation, a knowledge base, marketing site copy, game dialogue, an e-learning course, legal/regulatory copy, subtitles, and so on. The skill's rules don't change with content type — the same brand-vs-feature-vs-technical categorization and the same rounds-of-10 review work for a knowledge base as for a POS app. Don't assume the user is localizing a SaaS product; read the actual content and let the domain speak for itself.

Inconsistent terminology is one of the most visible quality issues in localization: feature or entity names drift between languages, brand terms get mistranslated, and the same technical noun ends up with three different renderings across content. A solid glossary prevents that — but only if its entries are accurate and actually useful, which is why this workflow stays human-in-the-loop instead of bulk-importing whatever the agent guesses.

Workflow

Copy link
  1. Take stock. If the user-prompt context pins a specific glossary (entity: { type: 'glossary', id: ... }), start with get_glossary + list_terms on that ID — no broad sweep needed, you already know the target. Otherwise, call list_glossaries to see what's there: if a project is in scope (named in the user-prompt context or explicitly by the user), filter to glossaries attached to that project and pull current term counts via list_terms (or the terms field on the glossary object); if no project, list the org-wide glossaries with their counts.

    Give the user a short summary — a sentence or two, not a section header with sub-bullets. Don't surface internal concepts like "project mode" or "standalone mode" to the user; just describe what you found in plain terms.

  2. Set up the run. Three upfront things to settle before any mining: target glossary (a), reference material (b), and pacing (c). The wrong glossary or a missed source is hard to undo once dozens of terms land in the wrong place.

    Skip whatever the user already answered inline. If the user's message already pins placement ("create a new glossary called X", "add to Acme Glossary"), opts into autonomous mode ("just go", "don't ask me about each batch"), or hands over reference material upfront ("here's our docs URL: …") — don't re-ask those. Acknowledge briefly and only ask what's actually missing. The priority rules below describe how to read each signal.

    (a) Where should new terms land? Pick placement using this priority order:

    1. User explicitly stated a placement in their message — always wins. "Add to Acme Glossary" → use Acme Glossary (resolve via list_glossaries). "Create a new glossary called X" → call add_glossary (ask only for missing details like source language). This overrides entity context: if the user is viewing "Old Glossary" in Crowdin but explicitly says "create a new one and fill it" — create a new one, do NOT silently use Old Glossary. The user's words are the source of truth.
    2. User-prompt context has entity: { type: 'glossary', id: ... } and the user said nothing about placement. This means the user is currently viewing that glossary in Crowdin. Default to it — fetch its name from the entity ID via get_glossary, then acknowledge in one short line ("Adding to — the glossary you're viewing.") and move on. The user can override by saying "actually create a new one instead", and you switch.
    3. Neither of the above (no explicit statement, no entity context, or context names a project but no specific glossary). Use AskUserQuestion with both options:
      • Add to an existing glossary. If a project is in scope, list attached glossaries first; also surface unattached org glossaries if any look relevant. If there's no project, list whatever exists in the org.
      • Create a new glossary (add_glossary). Ask for the name and source language.

    In all three branches: if you end up creating a new glossary and a project is in scope, attach it via edit_project (defaultGlossaryId) after creation. This is asked upfront, not after the first round, so placement is decided with a clean head — not under the pressure of "here are 10 great terms, where do they go?"

    (b) What reference material should I mine? Ask in plain text — most users will paste:

    • A product / marketing website URL (landing page, docs site, help center).
    • Links to existing documentation, style guides, or terminology lists.
    • Files attached to this chat (PDFs, text docs, existing glossary CSV/TBX exports, README files).

    If the user provides URLs, fetch them with WebFetch and extract candidate terms from the rendered content. If they attach files, read those too. External sources are usually denser in terminology than the project's own source strings — a canonical page (product landing page, docs index, KB homepage, "About us" section) names features, entities, and brand terms cleanly, whereas individual source strings show those terms only in passing. If the project's source content IS already documentation or a knowledge base, external material may add less — but even then a public docs URL or marketing page can help catch brand names the internal content assumes.

    URL depth — fetch only what the user gave you, then ask. Default to WebFetch on just the URL(s) the user provided. Don't follow internal links automatically — a landing page or docs index can link to dozens of pages, most of which add noise rather than terminology, and silently crawling burns tokens against the user's quota. When asking Q2, surface the lever so the user knows it exists: "I'll fetch the URLs you give me one-by-one. If you want me to also follow internal links (e.g. 'fetch acme.com plus all /docs/* pages'), say so." That way a user who knows they have a deep docs site can opt into more upfront. After the initial fetch, if the page is clearly an index (e.g. a /docs, /help, /features hub with many same-domain links to terminology-rich subpages like "Concepts" or "Glossary"), surface that and ask: "this URL is an index linking to ~N pages — want me to pull the top few (or specific ones you pick)?" Wait for the answer; never fan out on your own.

    Multilingual material — extract translation pairs, not just terms. When the user provides paired source-and-translation material (an EN file plus its UA / DE / ES translation, a landing page plus its /uk or /de mirror, or a bilingual CSV/TBX export), don't treat each language as a separate term pool. Align them: each candidate term gets the source-language form and its translation lifted directly from the user's material. This is high-value because the user has already done the linguistic work — guessing or skipping the translation would discard verified domain wording. In Crowdin, terms are grouped into a concept — see step 6 for the three-call apply pattern that links the source-language entry and target-language entry under one concept. If alignment is ambiguous (the translation uses a different synonym for one occurrence, or the term doesn't have an obvious equivalent in the parallel text), flag it on that specific entry — "found in English but no clean UA match in the paired file; add without translation?" — and let the user decide per-term.

    If a project is in scope and the user has nothing extra to add, that's fine — proceed with source strings only. If there's no project (so no source strings to mine) and the user can't provide URLs, files, or pasted text either, tell them the workflow needs something — ask for at least a website or a short product description. Don't invent terms from a product name alone.

    (c) Rounds with approval, or autonomous fill? Use AskUserQuestion. This picks the pacing:

    • Rounds with approval (default). You propose ~10 candidates at a time as a table, the user replies with edits / drops, you apply, repeat. The user stays in the loop on every entry that lands. Cheapest way to catch drift early.
    • Autonomous fill. You mine everything, apply all candidates that meet the quality bar in one batched pass, and hand the user a final report at the end. No per-round review. Fits users in a hurry, with high trust in the source material, and willing to revise after the fact.

    Even in autonomous mode you still pause for two things that autopilot can't safely bypass: (1) translation conflicts where the same source term renders with different roots in paired material — locking the wrong root corrupts every future workflow that uses this glossary, so this needs a human; (2) write failures that repeat across many terms (permission denied on the first three calls = structural problem, not a per-term issue). Everything else — routine quality judgments, dedup, lemma normalization — you decide on your own in autonomous mode.

    Default lean: rounds, because the small-rounds review is cheap insurance. Recommend autonomous only when the user explicitly signals "just go" or "no need to check every batch".

  3. No fixed quota. Don't commit to a target term count upfront, and don't show the user a "of ~50" denominator. The size a glossary needs varies wildly with domain density, source size, and team policy — any number you commit to is a guess that invites padding. Mine for high-quality candidates and stop when candidate quality starts dropping (you find yourself proposing weaker entries to fill the round) or when the user says stop.

    The one situation where you do call out the size of the existing glossary explicitly: when the user describes it as "already has the obvious stuff covered" or it carries dozens of entries already. In that case, name the count, point out that the obvious wins are likely in, and ask whether they still want to keep mining at a higher quality bar — don't quietly proceed as if the glossary were empty.

  4. Mine candidates. Pull terms from every source you have, in this order of usefulness:

    1. External reference material first (website, docs, attached files) — terminology is densest here, often pre-categorized, and canonical names for brands, features, entities, and domain concepts are stated cleanly.
    2. Project source content second — only available if a project is in scope. Use list_strings filtered by the most active files first (highest translation activity or most recent changes). Read enough to form judgments; a few hundred strings is usually enough to surface dominant terminology. "Source content" here means whatever the project holds — UI strings for an app, articles for a knowledge base, prose sections for a docs project, dialogue for a game, and so on. The mining approach is the same across all of them.

    Cross-reference where you can: when a term appears in both an external source and the project's source content, that's a strong signal it belongs in the glossary. When you only have external material to work from (no project / no source content), be a bit stricter about quality, since you can't validate "does this term actually show up where it will be translated".

    Never invent target-language translations. If the user provided paired source-and-translation material (an EN file with its UA mirror, a bilingual CSV/TBX, etc.), lift the translation directly. If the user explicitly asked you to suggest translations into a specific language, you may propose them — but mark each clearly as your guess for the user to confirm. Otherwise, add the source term only. Translations get filled in by linguists, or by re-running this workflow once paired material is available. Guessing "Електронний підпис → ??? in English" produces brittle entries that linguists then have to clean up, which is worse than no translation at all.

    Good candidates fall into four categories:

    Category What it covers Typical do-not-translate?
    Brand Company / product / service / content-property names Usually yes
    Feature Distinct features, modules, sections, entities, or named concepts (a UI area in an app, a chapter in a doc set, a category in a KB, a mechanic in a game) Sometimes
    Technical Domain or industry terminology, recurring nouns with specific meaning Rarely
    Other Anything else that needs to translate consistently every time it appears Case by case

    Brand category — split own vs third-party. Your own product / service / feature / SKU / content-property names always belong (almost always DNT). Third-party brand names that appear in the user's content — Stripe, HubSpot, Salesforce, Garmin, Strava, WordPress, Zendesk, etc. — are also DNT, but a translator usually already knows that. Don't silently fill the round with them; ask the user once whether to include third-party brands in this glossary or rely on translator general knowledge. Some teams want them explicit (locks behavior in MT and the AI Pipeline), others find them noise that dilutes the signal-to-noise of the glossary.

    Use the dictionary form of the term. Extract the lemma — nominative singular for nouns, infinitive for verbs — not whatever inflected form happens to appear in the source. Heavily-inflected languages (UA, RU, DE, …) decline a single noun across many surface forms; "Рахунок-фактура / Рахунка-фактури / Рахунку-фактурі" is the same term, not three. Glossary entries are stored as lemmas; the runtime match against the source string handles morphology. If you propose an inflected form, translators will get spurious mismatches and the glossary will quietly fail at its job.

    Skip:

    • Common words unless they carry a project-specific meaning in this context. For a SaaS app: user, page, button. For a docs / knowledge base: article, section, chapter, note. For a game: level, player, enemy. Skip them by default; only include when they mean something specific in this project (e.g. "User" as a defined role in a permission model, "Chapter" as a first-class content object in a KB).
    • Terms that appear only once or twice (in either external material or the project's source content) — the cost of a glossary entry isn't worth it.
    • Pronouns, prepositions, conjunctions, generic verbs.
    • Anything already in the target glossary — diff against the term list you fetched via list_terms in step 1. (concordance_search_in_glossaries is also available but requires projectId + sourceLanguageId + targetLanguageId, so reach for it only when you need a real cross-glossary search, not for simple dedupe against a known list.)
  5. Propose the first round (~10 candidates). (Skip this step entirely if the user chose autonomous fill in step 2(c) — go straight to step 6 with the full vetted candidate set.) Present them as a markdown table — flat list, grouped by sorting category-then-term, with one row per candidate. Tables are easier to scan than nested bullets, especially when the user wants to point at specific rows ("drop row 3, change the definition of row 5").

    | # | Category | Term | Definition | DNT |
    |---|---|---|---|---|
    | 1 | Brand | Crowdin | Localization management platform | yes |
    | 2 | Brand | OTA | Over-the-Air content delivery system | yes |
    | 3 | Feature | String Editor | The per-string translation interface |  |
    | 4 | Feature | Pre-translation | Automated bulk translation step |  |
    | 5 | Technical | TM | Translation Memory; reuses past translations |  |
    | 6 | Technical | Source string | The original untranslated text |  |
    | 7 | Other | Approval | Confirmation step that marks a translation as final |  |

    Each row: row number (for the user to reference), category, term (lemma — dictionary form), one-sentence definition, DNT marker where applicable. Keep definitions short — they end up in the glossary's concept definition.

    Ask for changes in writing. End the round with one explicit instruction: "Reply with any rows you want to drop (by number), any wording you want changed, or any rows you'd add — and the rest I'll apply as-is." Don't say "approve / edit / drop" by themselves — those sound like UI buttons; the user is in a chat, the only way to act is to type a reply. Spell that out so the user knows what to do.

    No "preview then real round" two-step. The candidates you put in this table are the round. Don't show a long "preview of what I'm thinking" with 20+ items and then promise "the actual round of ~10" in a later message — that doubles the user's reading load, makes it unclear what they're approving, and costs an extra round-trip. If you want to give the user a heads-up direction before they answer the setup questions, write one short sentence ("I'm seeing candidates in Brand, Feature, and Technical — I'll bring back ~10 once you answer the placement question above") and stop. The actual term table ships in one message, sized to ~10, after the user has answered the setup questions.

  6. Apply. The target glossary was already chosen in step 2 — don't re-ask. In rounds mode, this applies the candidates the user just approved in the current round. In autonomous mode, this applies the full vetted candidate set (after step 4 mining is done) in one batched pass.

    Three calls per term, not one. Crowdin's data model separates concept (the cross-language idea) from term (a per-language entry under that concept). add_term only creates a single term in a single language — it does not take translations as fields, and it does not fill concept-level metadata. The clean apply sequence for a candidate with a translation is:

    1. Create the source-language term. Call add_term(glossaryId, languageId: <source>, text: <lemma>, partOfSpeech: <enum>, type: <enum>, status: "preferred", ...). The response contains the new term's conceptId. Don't put the round's definition in description here — that field is per-language, and the round's definition is the concept-level meaning. Leave description empty unless you have a language-specific usage note.
    2. Fill the concept's semantic metadata — MANDATORY. Call update_concept(glossaryId, conceptId, definition: <the round's one-line definition>, subject: <optional domain like "Accounts Payable" or "Solar O&M">). This is where the user-approved definition lives, shared across all languages. You MUST make this call for every term you add — skipping it leaves the concept with a blank definition field, which means translators see only the term itself with no semantic context. The whole point of building a glossary is the definitions; an entry without one is barely useful. update_concept is part of the Crowdin MCP and is always available — do not skip it under any assumption that it might not be.
    3. Add each translation under that conceptId. Call add_term(glossaryId, languageId: <target>, text: <translation>, conceptId: <from step 1>, partOfSpeech: <enum>), once per target language. Passing conceptId is what links the entries — without it you'd create disconnected terms in different languages.

    Skip step 3 when there is no aligned translation to lift from paired material and the user didn't explicitly ask for translations into a specific language. Don't invent translations to pad out language coverage.

    Enum values are lowercase strings. Pass partOfSpeech: "noun" (or "proper noun" — with the space — for brand names), type: "acronym" for abbreviations like OCR / PR / CUF, type: "full form" for normal terms, status: "preferred" by default. The full enums are listed in the MCP tool schema.

    Do-not-translate flag. Crowdin doesn't expose a literal DNT boolean on add_term. Convention for brand / SKU entries: use type: "full form" and set translatable: false on the concept via update_concept so translators see the entry as non-translatable. As a backup, also include "Do not translate" in the term's note so it's visible inline in the term editor.

    Handle write failures gracefully. If an individual add_term call fails (duplicate, permission denied, schema violation, etc.), surface which term failed and why, continue with the rest of the round, and never blind-retry the same failing call. If the same failure repeats across many terms (e.g. permission denied on the first three calls), stop the round entirely and tell the user — the root cause is probably structural (no admin rights on this glossary, glossary attached to a different project than expected, …), and continuing wastes calls.

    Report what was added in one short line — count, target glossary name, and (if relevant) which entries failed and why. Example: "Added 9 terms to Main Glossary. (1 skipped: 'Vendor' was already in the glossary.)" Don't put a denominator on the running total; there is no fixed target.

  7. Ask how to continue — every round. (Skip this step in autonomous mode — there are no rounds to continue between. Go straight to step 8's wrap-up.) After each successful apply, ask the user how they'd like the next round to look:

    • Another round the same size, or
    • A larger round (e.g. 20 or 30), or
    • "Finish the rest in one go" (gather all remaining candidates, present the full table, apply after a single confirmation), or
    • Stop here.

    Never escalate the round size on your own. Stay at ~10 per round until the user explicitly asks for a larger size. The point of small rounds is to let the user steer early and cheaply — auto-escalating throws that away.

  8. Wrap up.

    • Rounds mode: repeat steps 5–7 until the user says stop, or candidate quality drops noticeably (you find yourself proposing weaker entries to fill the round). Re-mine via step 4 only when you've exhausted candidates from the first pass and need a wider source pool (different files, deeper-fetched URLs, more strings) — most rounds reuse what you already mined. When quality drops, say so — "I'm not finding more high-quality candidates without dropping the bar; want me to stop here or scan a different file set?" — and let the user choose.
    • Autonomous mode: the apply pass in step 6 was the whole job — single batch, no further rounds. Hand the user the final report (count added, target glossary, any failed entries, any conflicts you flagged for them to resolve later). If you stopped early because candidate quality dropped, name where you stopped and why — the user can decide whether to follow up with more material.

Quality Over Quantity

Copy link

A glossary of 40 sharp, useful entries is better than 200 entries padded with common words and one-off nouns. If you find yourself reaching for filler to make a round look complete, stop and say so — let the user decide whether to scan more material or wrap up.

Concretely, before proposing an entry, ask yourself: would a new translator working on this content look at this term and benefit from a definition or a do-not-translate flag? If the answer is "they'd know what this means in context", skip it. This test works the same way whether the content is an app UI, a docs page, or a game — the question is always about the translator's need for guidance on this term in this project.

Edge Cases

Copy link
  • Project has many files / languages. Don't try to read everything. Sample from the most active files first (highest translation activity or most recent changes). If you've read a few hundred strings and the same categories of terms keep coming up, you have enough to start.

  • Glossary already has substantial coverage. Diff your candidates against the existing terms before proposing — nothing wastes a round faster than the user noticing you suggested a term that's already in. (Step 3 handles the "do you still want to keep mining?" gate when the glossary is dense.)

  • User signals a change in the round table. The user will reply with edits in writing (e.g. "drop row 3, rename row 5 to 'Workflow run'"). Apply their version, not your original. If they reword a definition or change a do-not-translate flag, that's the source of truth for that entry.

  • Source content is in multiple languages already. The source language is the one the glossary terms should be in; ignore translations when mining (unless they're the paired translation material covered in step 2(b)).

  • User asks for an unusually large first round. Honor it — they're not bound by the default. Just make sure the table stays readable.

  • External URL fails to fetch or returns junk. Tell the user which URL failed and why (404, blocked, no usable content), and continue with the remaining sources. Don't silently drop a source the user explicitly handed you.

  • Attached file is a CSV/TBX glossary export. Treat it as a terminology gift: extract entries directly (term + translation + definition where available), present them in the same round-of-10 table, and let the user confirm before adding. Don't import_glossary silently — the user expected reviewed terms, not a bulk merge.

  • No project AND no reference material offered. Don't proceed. The agent's job is to mine terminology from real material, not to invent it from a name alone. Ask the user for at least a URL, a doc, or a short paragraph describing what they're localizing and its domain. If they truly have nothing, suggest they come back when they do.

  • User wants to "just brainstorm" terms without material. Honor it, but flag what you're doing: tell them these are guesses derived from the name and conversation alone, not from real source material, and offer to revisit once they have content to share.

  • User changes the target glossary mid-stream. If the user says "actually, move all of this to a different glossary" partway through (e.g. between rounds 2 and 3), treat it as a placement reset, not a small detail. Stop, confirm the new target glossary with one AskUserQuestion (same options as step 2 — existing vs new), and decide with the user whether previously-added terms move with the new placement or stay where they landed. Don't quietly start adding to the new glossary while leaving the earlier rounds behind in the old one — that's the kind of silent split that gets discovered weeks later.

  • Parallel sources disagree on a translation. Every time a source term appears in the paired material, check that all its occurrences map to forms of the same target lemma. Two failure modes to catch — they are both real and easy to miss:

    1. Across files. "Workflow" rendered as "Робочий процес" in one chunk and "Воркфлоу" in another.
    2. Within a single file, different verb / noun root. "Match" rendered as "зіставити" in some occurrences and "збігається" (a different verb root, not just a different conjugation) in others. Different inflections of the same lemma are fine — "зіставити / зіставитися / зіставляти" are all the verb зіставити. Different roots are not.

    When you find either pattern, don't silently pick one. Flag the disagreement in the round — "two candidate translations found: A vs B" — and ask the user which is canonical. Glossary translations are normative; picking the wrong one locks the wrong choice into every future translator workflow that uses this glossary.

Tool Reference

Copy link
Purpose Tool
List glossaries in the organization list_glossaries
Get details for one glossary get_glossary
Create a new glossary add_glossary
Attach a glossary to a project (sets defaultGlossaryId) edit_project
List terms in a glossary list_terms
Add a term in one language (returns conceptId to link translations) add_term
Fill / update concept-level metadata (definition, subject, translatable) update_concept
Search for terms across project glossaries (needs projectId + language pair) concordance_search_in_glossaries
List source strings to mine (when a project is in scope) list_strings
List files to pick the most active ones (when a project is in scope) list_files
Fetch a website / docs URL WebFetch
Ask discrete-choice questions AskUserQuestion

All Crowdin MCP tools are namespaced as mcp__crowdin__<tool> (e.g. mcp__crowdin__add_term). WebFetch and AskUserQuestion are Claude Agent SDK built-ins, not Crowdin MCP tools.

Works with
  • crowdin.com
  • Crowdin Enterprise
Details

Released on Sep 4, 2026

Updated on Sep 4, 2026

Published by Crowdin

Identifier:glossary-terms-extraction

All product and company names are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.