Agent skill

I18n

by brickbots in brickbots/PiFinder

PiFinder's internationalization (i18n) workflow — marking strings for translation, running the Babel extract/update/compile pipeline, adding or updating language translations, and filling in missing…

GPL-3.0Auto-check passedFrontend & Design

Install I18n

skills CLI
$ npx skills add brickbots/PiFinder --skill i18n -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install brickbots/PiFinder i18n --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/brickbots/PiFinder.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/i18n .claude/skills/i18n && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
i18n
GitHub stars
250
Token cost
~2.8k tokens
SKILL.md length
1,298 words
Files
1
Skills in repo
3
Repo updated
First seen
Licence
GPL-3.0

At a glance

PiFinder's internationalization (i18n) workflow — marking strings for translation, running the Babel extract/update/compile pipeline, adding or updating language translations, and filling in missing…

  • Works in 3 steps: extract — scans ./PiFinder (Python) and… → update — merges new/changed msgids from… → compile — builds the .mo binaries the…
  • The user mentions translations
  • SKILL.md covers Where things live, Marking strings for translation, The extract → update → compile… and Adding a new language, plus 3 more sections
  • Calls python

What it does

I18n is an agent skill from brickbots/PiFinder. PiFinder's internationalization (i18n) workflow — marking strings for translation, running the Babel extract/update/compile pipeline, adding or updating language translations, and filling in missing msgstr entries in .po files. Use whenever the user mentions translations, i18n, localization, locale, gettext, Babel, .po/.pot/.mo files, language support, or asks about adding/updating a language. Also use when reviewing a diff that touches user-visible UI strings and you need to check whether they're wrapped for…

Its SKILL.md is about 2.8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Frontend & Design, covering Internationalization and Translation. It works with Python. The repository describes itself as: A plate solving telescope finder based around a Raspberry PI and RPI HQ Camera. The licence is GPL-3.0.

When your agent uses it

  • The user mentions translations
  • .po/.pot/.mo files
  • Language support
  • Asks about adding/updating a language

Example prompts

  • “/i18n”

Requirements

  • Python 3

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. extract — scans ./PiFinder (Python) and ./views (Jinja2 templates) per babel.cfg, regenerates locale/messages.pot
  2. update — merges new/changed msgids from the .pot into every existing locale//LC_MESSAGES/messages.po, marking unchanged strings, leaving…
  3. compile — builds the .mo binaries the runtime actually reads

What it can do on your machine

Read from SKILL.md and the folder at commit 3775008. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • python

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

I18n loads about 2.8k tokens when it runs. Until then it costs about 133 tokens; SKILL.md has 1,298 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~133
When it runs · the whole SKILL.md, loaded when a task matches
~2.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from brickbots/PiFinder at commit 3775008, republished under its GPL-3.0 licence (© brickbots). 1,298 words, ~2,817 tokens.

Download SKILL.mdSave it as .claude/skills/i18n/SKILL.md (or your agent's skills folder).
name
i18n
description
PiFinder's internationalization (i18n) workflow — marking strings for translation, running the Babel extract/update/compile pipeline, adding or updating language translations, and filling in missing msgstr entries in .po files. Use whenever the user mentions translations, i18n, localization, locale, gettext, Babel, .po/.pot/.mo files, language support, or asks about adding/updating a language. Also use when reviewing a diff that touches user-visible UI strings and you need to check whether they're wrapped for translation.

PiFinder i18n

PiFinder uses standard Python gettext with Babel for extraction and compilation. Translations live in python/locale/ and are loaded at startup based on the language config option.

Supported languages today: de, es, fr, zh (plus en as the source/fallback).

Where things live

  • Source strings extractor config: python/babel.cfg
  • Workflow runner: python/noxfile.py → babel session
  • Translation files: python/locale/messages.pot (template) and python/locale/<lang>/LC_MESSAGES/messages.po (per-language)
  • Runtime install: python/PiFinder/main.py calls gettext.translation(...).install() near startup, making _() a builtin everywhere
  • Flask web server: python/PiFinder/server.py uses flask_babel's gettext (imported, not builtin)

Run all nox and pybabel commands from python/ with the project's virtualenv active.

Marking strings for translation

Wrap user-visible strings with _(). It's installed as a Python builtin by main.py at startup, so no import is needed in normal modules. Ruff is configured to recognize _ (see pyproject.toml), so there are no lint warnings.

Good:

python
self.error_message = _("Can't plot")
status = _("Galaxy")

Good — formatting with named placeholders:

python
_("{lat:.2f}, {lon:.2f}\n{alt}m alt").format(lat=lat, lon=lon, alt=alt)

Named placeholders matter: translators can reorder them, and the msgid stays stable across value changes.

Avoid — f-strings inside _():

python
_(f"  {self._SQUARE_} START ALIGN")     # bad
_(f"{count} objects")                    # bad

Babel extracts the f-string verbatim including the interpolated expression, so the msgid changes every time the interpolated value does and translators can never produce a stable match. There are a few of these in python/PiFinder/ui/align.py — they're legacy, not a pattern to follow.

Avoid — concatenation:

python
_("Hello, ") + name + _("!")             # bad, fragments are untranslatable in context
_("Hello, {name}!").format(name=name)    # good

Special case — python/PiFinder/obj_types.py: This file defines a local _ at the top that returns its argument unchanged. That's intentional — it lets pybabel extract pick up the object-type strings as msgids while the module is imported at a time when the global _() may not yet be installed. Don't "fix" it. The bottom of the file has a comment explaining the convention.

Plurals and contexts: Not currently used in PiFinder. If genuinely needed, prefer rewording over introducing ngettext/pgettext for the first time without discussion — it'll add complexity to every existing translator's workflow.

Translator hints: Add a # TRANSLATORS: comment immediately above the string when the source text is ambiguous out of context. Babel's -c TRANSLATORS flag pulls these into the .po file. Example from obj_types.py:

python
"Gx": _("Galaxy"),  # TRANSLATORS: Object type

The extract → update → compile workflow

Whenever source strings change (added, edited, or removed), run:

bash
cd python/
nox -s babel

This runs three pybabel steps in order:

  1. extract — scans ./PiFinder (Python) and ./views (Jinja2 templates) per babel.cfg, regenerates locale/messages.pot
  2. update — merges new/changed msgids from the .pot into every existing locale/<lang>/LC_MESSAGES/messages.po, marking unchanged strings, leaving new ones as empty msgstr "", and flagging close matches as #, fuzzy
  3. compile — builds the .mo binaries the runtime actually reads

If you only need one step (e.g., just compiling after hand-editing a .po), run the matching pybabel command directly from python/:

bash
pybabel extract -F babel.cfg -c TRANSLATORS -o locale/messages.pot ./PiFinder ./views
pybabel update  -i locale/messages.pot -d locale
pybabel compile -d locale

Commit both the updated .po files and the regenerated .mo files (the repo currently checks in .mo).

Adding a new language

Use the ISO 639-1 code (two letters, lowercase). From python/:

bash
pybabel init -i locale/messages.pot -d locale -l <code>

This creates locale/<code>/LC_MESSAGES/messages.po seeded from the current template. Then:

  1. Fill in the msgstr "" entries (see "Generating translations" below).
  2. Run nox -s babel to compile.
  3. Add the language to the validated set in python/PiFinder/main.py (search for the existing de/es/fr/zh list — there's a validation site around the --lang argument handling, and a Flask fallback list in python/PiFinder/server.py). Both spots need the new code or --lang <new> will be rejected and the web server won't recognize it.
  4. Verify by starting the app with --lang <code> and walking through a few menus.

Generating translations (filling in msgstr)

When asked to translate missing strings in a .po file:

  1. Find the gaps. Empty entries look like:

    po
    msgid "Can't plot"
    msgstr ""

    Also watch for #, fuzzy markers — those are Babel's guesses from pybabel update and need human review:

    po
    #, fuzzy
    msgid "Set location"
    msgstr "Lieu actuel"

    Either correct the translation and remove the #, fuzzy line (otherwise gettext ignores the entry at runtime), or replace it entirely.

  2. Translate with context in mind. PiFinder is a telescope finder — many strings are astronomical terms (object types, catalog names), short UI labels constrained by a tiny OLED display, or status messages. Prefer concise translations that fit similar visual width to the English. When the # TRANSLATORS: comment exists, follow its guidance.

  3. Preserve placeholders and whitespace exactly. If the msgid contains {lat:.2f} or \n or trailing spaces, the msgstr must contain the same tokens. Reordering named placeholders is fine; renaming them is not.

  4. Don't translate technical identifiers. Catalog codes (NGC, M, IC), units (°, m, px), and proper nouns generally stay as-is.

    Some abbreviations must never be translated, transliterated, or expanded — even when the words around them are. Keep them as-is:

    • EQ — equatorial coordinate mode (e.g. EQ (Auto), EQ (North-up), EQ (South-up)). Keep verbatim; never translate or expand.
    • RA (acronym for Right Ascension) and Dec (abbreviation for Declination) — keep as abbreviations; never expand or localize. Use the form RA/Dec — note Dec, not DEC — even when a source string is written RA/DEC or DEC:. RA stays uppercase; only the declination part is Dec.
    • Alt/Az — altitude/azimuth. Keep verbatim; do not translate or reorder the two parts.
    • GPS — any message containing GPS keeps it as GPS; it's recognized under that acronym across languages.

    These show up in strings like EQ (North-up), RA/DEC Disp., RA: / DEC:, Alt/Az, and GPS Settings. Translate only the surrounding words (e.g. "Settings", "Disp.") and render the abbreviation in its canonical form (RA/Dec, EQ, Alt/Az, GPS).

  5. After editing, recompile:

    bash
    cd python/ && pybabel compile -d locale
  6. Sanity check by running the app with --lang <code> and confirming the strings render correctly (no truncation, no mojibake, no broken format strings).

When you're filling in many entries at once, do them in batches and call out any strings where you weren't sure of the intended meaning so the maintainer can sanity-check.

Show full SKILL.md (398 more words)Show less
Mark AI-generated translations for human review

Any msgstr value that you (Claude) or any other AI system produces — including light edits or re-orderings of a prior translation — must be tagged with a translator comment on the line directly above the entry, so a human reviewer can find and validate it later:

po
# AI-TRANSLATED (claude): needs human review
msgid "Can't plot"
msgstr "Impossible de tracer"

The exact prefix AI-TRANSLATED matters — it's what makes the entries greppable across all .po files. Include the model or system name in parentheses when known (e.g., claude, gpt-4, deepl) so reviewers can weigh source reliability. A reviewer's workflow looks like:

bash
grep -rn "AI-TRANSLATED" python/locale/

When a human confirms the translation is correct, they remove the comment. An entry without the marker is treated as human-validated. This means: don't add the marker to translations you didn't touch, and don't strip it from entries you only re-formatted.

Why a plain # comment rather than #, fuzzy: the fuzzy flag tells gettext to ignore the msgstr at runtime, so users in the target language would see the English fallback instead of your draft translation. That defeats the point of doing the work. The AI-TRANSLATED comment leaves the translation live in the UI while still being unmistakably findable.

If you have low confidence in a specific translation (technical term, ambiguous context, cultural nuance), pair the marker with #, fuzzy in addition — that string falls back to English until a human approves, which is the safe choice when getting it wrong would be worse than showing English:

po
# AI-TRANSLATED (claude): unsure of astronomical convention in target language
#, fuzzy
msgid "Right Ascension"
msgstr "Ascension droite"

Reviewing diffs for i18n correctness

When asked to audit a diff or PR for i18n issues, check:

  • New user-visible strings in python/PiFinder/ui/, menus, server templates, or error messages — are they wrapped in _()? Internal log messages, exception messages for developers, and debug strings are usually fine to leave alone.
  • F-strings or concatenation inside _()? Flag as bugs (see "Avoid" examples above).
  • String formatting with positional {} or %s? Push toward named placeholders — translators need them.
  • Strings split across lines for code formatting? Make sure the joined result is still one msgid, not multiple fragments.
  • Was nox -s babel run? If .po/.pot files weren't updated alongside the string changes, the translations are now stale.

Quick verification

After any translation work, the fastest end-to-end check:

bash
cd python/
nox -s babel                                                # extract/update/compile
python -m PiFinder.main -fh --camera debug --keyboard local -x --lang <code>

Watch for the strings you touched to render in the target language. If anything still shows in English, the most common causes are: forgot to compile (.mo is stale), the string isn't actually wrapped in _(), or the entry is still marked #, fuzzy.

© brickbots, GPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/i18n of brickbots/PiFinder.

Open the folder on GitHubat commit 3775008

Compare with similar skills

I18n next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

I18n compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
I18n this skillbrickbots/PiFinder250—~2.8kAutomated safety check: PassGPL-3.0
Ok Script I18nAliceJump/ok-end-field554—~828Automated safety check: PassAGPL-3.0
Fluent MigrationBrowserWorks/waterfox-android376—~4.2kAutomated safety check: PassCustom licence
Claude Desktop Chinese Localizationjavaht/claude-desktop-zh-cn7.5k—~1.6kAutomated safety check: PassMIT
Ok Script I18nbaoxin1100/ok-kes101—~903Automated safety check: PassNone
Viewer i18n Translatorliaohch3/claude-tap3.3k—~666Automated safety check: PassMIT

Similar skills

  • Ok Script I18n

    AliceJump/ok-end-field

    Maintain gettext translations for ok-script task UI and runtime messages.

    554 GitHub stars~828 tokensUpdated today
    Frontend & DesignAuto-check passed
  • Fluent Migration

    BrowserWorks/waterfox-android

    A skill your agent uses when a patch or local changes rename, restructure, move, or replace Fluent (.ftl) strings - or migrate legacy .properties strings to Fluent - and you need a migration recipe…

    376 GitHub stars~4.2k tokensUpdated 15 days ago
    Frontend & DesignAuto-check passed
  • Claude Desktop Chinese Localization

    javaht/claude-desktop-zh-cn

    Adds missing Simplified and Traditional Chinese translations to the Claude Desktop Chinese patch across three layers, then checks how many mappings actually hit.

    7.5k GitHub stars~1.6k tokensUpdated 2 days ago
    Frontend & DesignAuto-check passed
  • Ok Script I18n

    baoxin1100/ok-kes

    Add, sync, repair, and compile gettext translations for ok-script Python task classes and task metadata.

    101 GitHub stars~903 tokensUpdated 6 days ago
    Frontend & DesignAuto-check passed
  • Viewer i18n Translator

    liaohch3/claude-tap

    Fills missing translations in claude-tap's viewer_i18n.json by sending untranslated keys to OpenRouter for Japanese, Korean, French, Arabic, German and Russian.

    3.3k GitHub stars~666 tokensUpdated 15 days ago
    Frontend & DesignAuto-check passed
  • Frappe Core Translation

    Impertio-Studio/Frappe_Claude_Skill_Package

    A skill your agent uses when implementing translations/i18n in Frappe v14-v16 apps.

    187 GitHub stars~1.9k tokensUpdated 20 days ago
    Writing & ContentAuto-check passed

More from brickbots/PiFinder

  • Docs

    brickbots/PiFinder

    Author and edit PiFinder's user-facing documentation in the project's house style.

    250 GitHub stars~6.2k tokensUpdated yesterday
    Auto-check passed
  • Pifinder Remote

    brickbots/PiFinder

    Run the PiFinder app headlessly and drive it like a user — launch it with no pygame window or physical display, send keypad presses to navigate menus, capture the screen as a PNG, read live state…

    250 GitHub stars~2.5k tokensUpdated yesterday
    Auto-check passed

Works with

Questions about I18n

What does I18n do?

PiFinder's internationalization (i18n) workflow — marking strings for translation, running the Babel extract/update/compile pipeline, adding or updating language translations, and filling in missing…. I18n is an agent skill from brickbots/PiFinder.po files.

When should I use I18n?

I18n fits situations like: the user mentions translations; .po/.pot/.mo files; language support; asks about adding/updating a language.

How do I install I18n in Claude Code?

Run `npx skills add brickbots/PiFinder --skill i18n -a claude-code`. Or copy the skill folder (.claude/skills/i18n in brickbots/PiFinder) into .claude/skills/i18n in your project. Claude Code loads it when a task matches its description.

How do I install I18n in Codex?

Run `npx skills add brickbots/PiFinder --skill i18n -a codex`. Or copy the skill folder (.claude/skills/i18n in brickbots/PiFinder) into .agents/skills/i18n in your project. Codex loads it when a task matches its description.

Can I use I18n in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add brickbots/PiFinder --skill i18n -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/i18n, .gemini/skills/i18n, .github/skills/i18n and .opencode/skills/i18n in your project.

What does I18n need to run?

Going by SKILL.md and its folder, I18n needs the command-line tools its instructions call (python). Our summary lists: Python 3.

Does I18n access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is I18n safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does I18n use?

I18n is published under the GPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does I18n use?

About 2.8k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to I18n?

Skills that share tags, products or a category with I18n: Ok Script I18n (AliceJump/ok-end-field, 554 stars), Fluent Migration (BrowserWorks/waterfox-android, 376 stars), Claude Desktop Chinese Localization (javaht/claude-desktop-zh-cn, 7.5k stars) and Ok Script I18n (baoxin1100/ok-kes, 101 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains I18n?

brickbots (a GitHub user) maintains it in brickbots/PiFinder, which has 250 GitHub stars. The repository holds 3 skills in this directory. The repository was last updated on October 6, 2026.

Source: brickbots/PiFinder on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.