glotfile 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Glotfile</title>
7
- <script type="module" crossorigin src="/assets/index-DC89onXX.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-B1UwtMSs.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-DPfAS4pJ.css">
9
9
  </head>
10
10
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glotfile",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Local-first, git-native translation management.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "bin",
11
- "dist"
11
+ "dist",
12
+ "skill"
12
13
  ],
13
14
  "engines": {
14
15
  "node": "^20.19.0 || >=22.12.0"
package/skill/SKILL.md ADDED
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: glotfile
3
+ description: Manage translations in a repo that uses glotfile (a local-first, git-native translation catalog). Use when working in a project containing a glotfile.json or glotfile/ directory, or when the user asks to add/edit/translate/export strings, work with locales/i18n, or run any `glotfile` command. Covers the CLI workflows, the glotfile.json schema, adding and editing strings, glossary, and project conventions.
4
+ ---
5
+
6
+ # Managing glotfile
7
+
8
+ Glotfile keeps every translatable string for a project in one committed state file —
9
+ `glotfile.json` (or a `glotfile/` directory when storage is split). A local web UI,
10
+ a CLI, and pluggable AI providers all read and write that one file; platform locale
11
+ files (Flutter ARB, Laravel PHP, i18next/vue-i18n JSON, gettext `.po`, Apple
12
+ `.strings`/`.stringsdict`, Angular XLIFF, Rails YAML) are **generated from it on export**.
13
+
14
+ ## The one rule that prevents most mistakes
15
+
16
+ **The state file is the single source of truth. Exported locale files are generated
17
+ output — never hand-edit them.** Editing `lib/l10n/app_fr.arb` or `resources/lang/fr/`
18
+ directly is wasted work: the next `glotfile export` overwrites it. Change the source
19
+ string in glotfile, then export.
20
+
21
+ To make a change:
22
+ 1. Edit the **state file** (or run a `glotfile` command, or use the UI).
23
+ 2. Run `glotfile export` to regenerate the locale files.
24
+ 3. Commit both the state file and the regenerated outputs.
25
+
26
+ ## Before you touch anything: discover the project's actual config
27
+
28
+ Do not assume the locales, providers, or output formats — read them. They live in the
29
+ state file's `config` block.
30
+
31
+ - Single-file layout: read `glotfile.json` → `.config`.
32
+ - Split layout: read `glotfile/config.json` → `.config`.
33
+
34
+ `config.sourceLocale`, `config.locales`, and `config.outputs[]` tell you what languages
35
+ exist and where exports land. Match the project; don't introduce a new locale or adapter
36
+ unless asked.
37
+
38
+ ## Task → tool map
39
+
40
+ | You want to… | Do this |
41
+ | --- | --- |
42
+ | Add a new string | Add a `KeyEntry` to `keys` (source-locale value, `state: "source"`), then `glotfile translate` + `glotfile export`. See `references/workflows.md`. |
43
+ | Edit an existing source string | Change the source-locale value; downstream translations become stale — re-translate. See `references/workflows.md`. |
44
+ | Translate missing strings | `glotfile translate` (only fills empties; `--all` re-translates everything). |
45
+ | Write locale files | `glotfile export` |
46
+ | Find problems | `glotfile lint` (catalog issues) / `glotfile check` (lint + exports up to date) |
47
+ | Bootstrap from existing locale files | `glotfile import --format <adapter>` — see `references/workflows.md`. |
48
+ | Remove dead keys | `glotfile prune --unused` / `--empty-source` (dry-run unless `--write`). |
49
+ | Enforce term translations | Glossary — see `references/workflows.md`. |
50
+
51
+ ## Reference files — read the one you need
52
+
53
+ - `references/cli-reference.md` — every command, its flags, and when to reach for it.
54
+ - `references/schema.md` — the shape of `glotfile.json`: `Config`, `KeyEntry`, plurals,
55
+ placeholders, locale states. Read before editing the file by hand.
56
+ - `references/workflows.md` — step-by-step recipes: adding/editing strings, onboarding a
57
+ repo via `import`, populating the glossary, building context for better translations.
58
+ - `references/conventions.md` — guardrails and the mental model. Read this if you are
59
+ unsure whether an action fights the tool.
60
+
61
+ ## Provider/API keys
62
+
63
+ AI provider settings and API keys live in **per-machine local settings** (not the
64
+ committed config), so they are absent in fresh clones. If a `translate` or
65
+ `build-context` run fails for a missing key/provider, that is a configuration step for
66
+ the user — don't invent keys or commit them.
@@ -0,0 +1,84 @@
1
+ # glotfile CLI reference
2
+
3
+ Run via `npx glotfile <command>` (or `node bin/glotfile.js <command>` from a checkout).
4
+ Every command accepts the global flags:
5
+
6
+ - `-f, --file <path>` — state file to use (default: `./glotfile.json`).
7
+ - `-h, --help` — show help. `glotfile <command> --help` shows a command's options.
8
+
9
+ The state file is auto-detected: a `glotfile/` directory (split layout) wins over a
10
+ single `glotfile.json`.
11
+
12
+ ## serve
13
+ `glotfile serve [--dev]` — start the local web UI (the default command when none is
14
+ given). Opens a browser at a local URL. `--dev` runs the UI from source with hot reload
15
+ (the API runs on a separate port; open the Vite URL, not the API port). With
16
+ `config.autoExport` on (the default), serving re-exports to disk on every change.
17
+
18
+ ## export
19
+ `glotfile export [--adapter <name>] [--watch]` — write the locale files for every
20
+ configured output in `config.outputs`.
21
+ - `--adapter <name>` — only export this adapter (e.g. `flutter-arb`, `laravel-php`).
22
+ - `--watch` — re-export whenever the state file changes.
23
+
24
+ Adapter names: `flutter-arb`, `laravel-php`, `i18next-json`, `vue-i18n-json`,
25
+ `gettext-po`, `apple-strings`, `apple-stringsdict`, `angular-xliff`, `rails-yaml`.
26
+
27
+ ## translate
28
+ `glotfile translate [--all] [--estimate] [--locale <list>] [--key <glob>]` — AI-translate
29
+ strings into the target locales and write the results back into the state file.
30
+ - By default only **empty** values are translated (existing translations are left alone).
31
+ - `--all` — re-translate every string, overwriting existing translations.
32
+ - `--estimate` — print batch/token/cost estimates and translate nothing.
33
+ - `--locale fr,de` — restrict to these target locales (alias: `--locales`).
34
+ - `--key <glob>` — only keys matching the glob (e.g. `auth.*`).
35
+
36
+ Requires a configured AI provider + API key in per-machine local settings.
37
+
38
+ ## lint
39
+ `glotfile lint [--format text|json|sarif] [--locale <list>] [--rule <list>] [--max-warnings <n>] [--include-suppressed] [--accept]`
40
+ — check the catalog for problems (placeholder mismatches, length, glossary violations,
41
+ spelling, identical-to-source, …).
42
+ - `--max-warnings <n>` — exit non-zero if warnings exceed n (for CI).
43
+ - `--accept` — suppress all current warnings (narrow with `--rule`/`--locale`); each
44
+ suppression expires automatically when its key's source text changes.
45
+ - `--include-suppressed` — also show findings hidden by suppressions.
46
+
47
+ ## check
48
+ `glotfile check [--format text|json|sarif]` — lint the catalog **and** verify the
49
+ exported files on disk are up to date. Use in CI to catch a state file that was changed
50
+ without re-exporting. Exits non-zero on any error.
51
+
52
+ ## import
53
+ `glotfile import --format <name> [--source <dir>] [--source-locale <code>] [--locales <list>] [--cldr] [--force]`
54
+ — create `glotfile.json` from a project's existing locale files. See
55
+ `references/workflows.md` for the onboarding flow.
56
+ - `--source <dir>` — directory to import from (default: the state file's directory).
57
+ - `--source-locale <code>` — which locale is the source of truth.
58
+ - `--cldr` — expand CLDR plural forms.
59
+ - `--force` — overwrite an existing `glotfile.json`.
60
+
61
+ ## build-context
62
+ `glotfile build-context [--all] [--key <glob>] [--limit <n>] [--since <date>]` —
63
+ AI-generate per-key context (where/how a string is used) to improve translation quality.
64
+ **Requires a prior `glotfile scan`** to index code references.
65
+
66
+ ## scan
67
+ `glotfile scan` — index the codebase's references to translation keys, writing
68
+ `.glotfile/usage.json`. Feeds `build-context` and `prune --unused`.
69
+
70
+ ## prune
71
+ `glotfile prune (--empty-source | --unused) [--write]` — remove keys. **Dry-run (lists
72
+ only) unless `--write` is given.**
73
+ - `--empty-source` — keys whose source value is empty.
74
+ - `--unused` — keys with no code reference (runs a scan first so the result is current).
75
+
76
+ ## split
77
+ `glotfile split` — convert a single `glotfile.json` into a `glotfile/` directory
78
+ (`config.json`, `keys.json`, `locales/<code>.json`). Produces smaller, more reviewable
79
+ git diffs on large catalogs. All commands work with either layout.
80
+
81
+ ## skill
82
+ `glotfile skill [--print] [--force]` — install this Claude Code skill into the current
83
+ repo's `.claude/skills/glotfile/`. `--print` writes `SKILL.md` to stdout instead;
84
+ `--force` overwrites an existing install.
@@ -0,0 +1,50 @@
1
+ # glotfile conventions & guardrails
2
+
3
+ The mental model, and the rules that keep you from fighting the tool.
4
+
5
+ ## Local-first and git-native
6
+
7
+ Everything lives in the repo and is meant to be committed. There is no server, no
8
+ database, no account. The state file IS the database. A change isn't done until it's
9
+ saved to the state file, exported, and committed.
10
+
11
+ ## The state file is the source of truth — exports are derived
12
+
13
+ - **Never hand-edit exported locale files** (ARB, PHP, JSON, `.po`, `.strings`,
14
+ `.stringsdict`, XLIFF, YAML). They are regenerated by `glotfile export` and your edit
15
+ will be overwritten. Edit the state file instead, then export.
16
+ - When you see a stale or wrong translation in an exported file, fix it in
17
+ `keys.<name>.values.<locale>` and re-export.
18
+
19
+ ## Keep diffs minimal
20
+
21
+ - Glotfile serializes deterministically (stable key order, fixed indent, trailing
22
+ newline). Don't reformat the state file or reorder keys — let glotfile / the UI write
23
+ it so diffs stay reviewable.
24
+ - Commit the state file and the regenerated outputs together, so the catalog and the
25
+ locale files never drift apart in history. `glotfile check` verifies they're in sync.
26
+
27
+ ## Two on-disk layouts — support both
28
+
29
+ A project is either single-file (`glotfile.json`) or split (`glotfile/` directory).
30
+ `glotfile` auto-detects (split wins). Read whichever exists; don't convert between them
31
+ unless asked (`glotfile split` does the conversion). Large catalogs prefer split for
32
+ smaller diffs.
33
+
34
+ ## Config round-trip
35
+
36
+ Saving Settings from the UI replaces the entire `config` object. If you add a new
37
+ `config.*` field, it must be modeled in the Settings round-trip or it gets silently wiped
38
+ on the next save. When in doubt, set config via the UI/CLI rather than hand-editing.
39
+
40
+ ## Secrets stay out of the committed config
41
+
42
+ AI provider API keys and editor choices live in **per-machine local settings**, never in
43
+ `glotfile.json`. Don't move them into the committed config and don't commit keys. A fresh
44
+ clone won't have them — a failed `translate` due to a missing key is a user setup step.
45
+
46
+ ## Don't assume — read
47
+
48
+ Locales, output adapters, key-naming convention, and provider are all project-specific.
49
+ Read them from the state file and follow the existing patterns rather than introducing
50
+ new ones. Add a locale or output only when explicitly asked.
@@ -0,0 +1,99 @@
1
+ # glotfile.json schema
2
+
3
+ Read this before editing the state file by hand. Glotfile re-serializes the file
4
+ deterministically (stable key order, fixed indent, trailing newline) so diffs stay
5
+ minimal — match that style, or just let a `glotfile` command / the UI write it.
6
+
7
+ Locales are canonicalized to lowercase BCP-47 (e.g. `pt-br`) on load and save.
8
+
9
+ ## Top-level shape
10
+
11
+ ```jsonc
12
+ {
13
+ "version": 1,
14
+ "config": { /* Config — see below */ },
15
+ "glossary": [ /* term entries */ ],
16
+ "keys": { /* keyName -> KeyEntry */ }
17
+ }
18
+ ```
19
+
20
+ In **split** layout this is spread across `glotfile/config.json` (holds `version` +
21
+ `config` + `glossary`), `glotfile/keys.json` (metadata per key), and
22
+ `glotfile/locales/<code>.json` (the values for one locale).
23
+
24
+ ## Config
25
+
26
+ ```jsonc
27
+ {
28
+ "sourceLocale": "en",
29
+ "locales": ["en", "fr", "de"], // every locale in the catalog
30
+ "outputs": [ // one per generated locale file set
31
+ { "adapter": "flutter-arb", "path": "lib/l10n/app_{locale}.arb" }
32
+ // OutputConfig also supports: style, emptyAs ("source"|"empty"|"omit"),
33
+ // indent, finalNewline, includeLocale, localeCase, localeMap, localeAliases
34
+ ],
35
+ "format": { "indent": 2, "sortKeys": true, "finalNewline": true },
36
+ "autoExport": true, // serve re-exports on change (default)
37
+ "spelling": { "customWords": [] },
38
+ "lint": { "rules": {}, "ignore": [], "spelling": {} },
39
+ "scan": { "include": [], "exclude": [], "accessors": [], "patterns": [] }
40
+ }
41
+ ```
42
+
43
+ `{locale}` in an output `path` is replaced with each locale's export token.
44
+ **Saving Settings from the UI replaces the whole `config` object** — any new `config.*`
45
+ section must be modeled in the round-trip or it is silently wiped.
46
+
47
+ ## KeyEntry
48
+
49
+ ```jsonc
50
+ {
51
+ "context": "Shown on the empty-cart screen", // helps the AI translate; optional
52
+ "description": "…",
53
+ "notes": [{ "id": "…", "text": "…", "at": "ISO-8601" }],
54
+ "tags": ["checkout"],
55
+ "maxLength": 40, // lint flags overflow
56
+ "screenshot": "…", // path; used by vision providers
57
+ "skipTranslate": false,
58
+ "plural": { "arg": "count" }, // presence => plural message
59
+ "placeholders": { // typed placeholder metadata
60
+ "count": { "type": "int", "format": "compact", "example": "1,000" }
61
+ },
62
+ "suppressions": [ /* dismissed lint findings; expire when source changes */ ],
63
+ "values": {
64
+ "en": { "value": "Your cart is empty", "state": "source" },
65
+ "fr": { "value": "Votre panier est vide", "state": "machine" }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### LocaleValue
71
+
72
+ A scalar key carries `value`; a **plural** key (one with a `plural` marker) carries
73
+ `forms` instead — one entry per ICU selector:
74
+
75
+ ```jsonc
76
+ "values": {
77
+ "en": {
78
+ "forms": { "one": "{count} item", "other": "{count} items" },
79
+ "state": "source"
80
+ }
81
+ }
82
+ ```
83
+
84
+ `state` is one of:
85
+
86
+ - `source` — the source-locale value (the authoritative text).
87
+ - `machine` — AI-translated, not yet reviewed.
88
+ - `needs-review` — flagged for human review (e.g. source changed after translation).
89
+ - `reviewed` — human-approved.
90
+
91
+ Plural form selectors are CLDR categories (`zero`, `one`, `two`, `few`, `many`, `other`)
92
+ or exact matches like `=0`/`=1`. Not every locale uses every category; the source
93
+ locale's set defines the message's branches.
94
+
95
+ ## Glossary
96
+
97
+ `glossary` is an array of term entries that constrain how specific words/phrases are
98
+ translated (or kept verbatim). The lint `glossary` rule flags violations. See
99
+ `references/workflows.md` for how to add entries.
@@ -0,0 +1,84 @@
1
+ # glotfile workflows
2
+
3
+ Recipes for the common jobs. All of them follow the same loop: **change the state file →
4
+ `glotfile export` → commit both.** Prefer the CLI/UI over hand-editing; edit by hand only
5
+ when no command fits (then match the deterministic format — see `references/schema.md`).
6
+
7
+ ## Add a new string
8
+
9
+ 1. Pick a key name that matches the project's existing convention (look at sibling keys —
10
+ dotted `auth.login.title`, flat, etc.).
11
+ 2. Add a `KeyEntry` to `keys` with the source-locale value and `state: "source"`:
12
+ ```jsonc
13
+ "cart.empty.title": {
14
+ "context": "Heading on the empty-cart screen",
15
+ "values": { "en": { "value": "Your cart is empty", "state": "source" } }
16
+ }
17
+ ```
18
+ Add `context` when the string is ambiguous out of context — it measurably improves
19
+ translation quality.
20
+ 3. `glotfile translate` to fill the other locales (or `--key "cart.*"` to scope it).
21
+ 4. `glotfile export` to write the locale files.
22
+ 5. Wire the key up in the app code using the project's i18n accessor, then commit.
23
+
24
+ For a **plural** message, add a `plural: { "arg": "count" }` marker and use `forms`
25
+ instead of `value` (see `references/schema.md`). Use ICU placeholders like `{count}` and
26
+ keep them identical across locales — the lint placeholder rule enforces this.
27
+
28
+ ## Edit an existing source string
29
+
30
+ 1. Change the **source-locale** value in `keys.<name>.values.<sourceLocale>`.
31
+ 2. Existing translations are now stale. Re-translate them: `glotfile translate --all
32
+ --key "<name>"` (or let the user review). Glotfile tracks the source a translation was
33
+ made from, so stale entries can be flagged `needs-review`.
34
+ 3. `glotfile export`, then commit.
35
+
36
+ Never edit a translation in an exported file to "fix" it — fix it in the state file
37
+ (`state: "reviewed"` once a human approves) and re-export.
38
+
39
+ ## Onboard a repo that already has locale files
40
+
41
+ 1. Identify the existing format and pick the matching adapter (e.g. a Laravel app with
42
+ `resources/lang/{locale}/` → `laravel-php`; a Flutter app with `app_{locale}.arb` →
43
+ `flutter-arb`).
44
+ 2. `glotfile import --format <adapter> --source <dir> --source-locale <code>` — this reads
45
+ the existing files and writes a `glotfile.json`. Add `--cldr` if plurals use CLDR
46
+ forms; `--force` to overwrite an existing state file.
47
+ 3. Review the generated `config.outputs` so a subsequent `glotfile export` writes back to
48
+ the same paths the project already uses.
49
+ 4. `glotfile scan` then `glotfile build-context` (optional) to enrich keys with usage
50
+ context before translating.
51
+
52
+ ## Populate the glossary
53
+
54
+ The glossary constrains how particular terms are translated across the catalog; the lint
55
+ `glossary` rule flags violations. Each entry:
56
+
57
+ ```jsonc
58
+ {
59
+ "term": "Glotfile",
60
+ "doNotTranslate": true, // keep verbatim in every locale
61
+ "caseSensitive": true,
62
+ "notes": "Product name"
63
+ }
64
+ ```
65
+
66
+ Or pin specific translations:
67
+
68
+ ```jsonc
69
+ {
70
+ "term": "cart",
71
+ "translations": { "fr": "panier", "de": "Warenkorb" },
72
+ "notes": "Use the e-commerce sense, not 'chariot'"
73
+ }
74
+ ```
75
+
76
+ Add entries to the top-level `glossary` array. After adding them, `glotfile lint` will
77
+ flag existing translations that don't comply; `glotfile translate --all` re-translates
78
+ with the glossary applied.
79
+
80
+ ## Improve translation quality with context
81
+
82
+ `glotfile scan` (index code references) → `glotfile build-context` (AI writes a short
83
+ `context` per key from how it's used in code) → `glotfile translate`. Keys with good
84
+ `context` translate far more accurately, especially short or ambiguous strings.