Ƃöŵŕàîñ Þŕöĵéçţ Ḿöđéļ
À çöññéçţéđ þŕöĵéçţ ķééþš îţš çöñƒîĝüŕàţîöñ îñ ţĥé ŕéþöšîţöŕý àñđ îţš çöñţéẋţ öñ Ƃöŵŕàîñ, ŵĥéŕé éṽéŕý çĥéçķöüţ àñđ éṽéŕý ţéàḿḿàţé ŕéàđš ţĥé šàḿé ţéŕḿš, ṽöîçé þŕöƒîļéš àñđ đéçîšîöñš. Öñ đîšķ îţ îš à ķàþî þŕöĵéçţ ŵîţĥ à bowrain: ƃļöçķ îñ îţš ŕéçîþé: à šîñĝļé kapi.yaml ŕéçîþé ƒîļé àţ ţĥé þŕöĵéçţ ŕööţ àñđ à šîƃļîñĝ .kapi/ đîŕéçţöŕý, ţĥé šàḿé þŕöĵéçţ ḿöđéļ ţĥé kapi ÇĻÎ üšéš.
Đîŕéçţöŕý Šţŕüçţüŕé
my-app/
├── kapi.yaml # the recipe (committed); fixed, conventional filename
├── .mcp.json # agent wiring written by kapi init (committed)
├── .kapi/ # this checkout's cache, ignored as a whole
│ ├── .gitignore # `*`, written by kapi init
│ ├── filters.local.json # personal saved reader filters
│ ├── vault/ # withheld redaction originals (local-only)
│ ├── sync/ # the tree last declared to the server, and the refs
│ └── work/ # free to delete: rebuilt on the next run
│ ├── store.db # this checkout's projection of its working tree
│ └── cache/
│ ├── extractions/
│ └── collections/
├── flows/ # file-per-flow definitions, named by flows_dir (optional)
└── src/
└── locales/
├── en/
│ └── messages.json
└── fr/
└── messages.json
Öŵñéŕšĥîþ žöñéš àţ ţĥé þŕöĵéçţ ŕööţ:
kapi.yaml: ĥàñđ-éđîţéđ àñđ çöḿḿîţţéđ ţö ĝîţ. Ţĥé ŕéçîþé ĥöļđš ţĥé þŕöĵéçţ çöñƒîĝüŕàţîöñ. Àñ àẋîš àþþŕöṽéđ öñ ţĥé šéŕṽéŕ àļšö ŕéàçĥéš îţ: ţĥé ñéẋţkapi pulléđîţšdefaults.coordinates, ƒöŕ ŕéṽîéŵ îñ ĝîţ..kapi/: ţĥîš çĥéçķöüţ'š çàçĥé, ķéþţ öüţ öƒ ĝîţ.work/store.dbĥöļđš ţĥé çĥéçķöüţ'š çöþý öƒ îţš ŵöŕķîñĝ ţŕéé: ţĥé ƃļöçķ çàçĥé, ţĥé öṽéŕļàýš à ŕüñ ŵŕöţé àñđ ţĥé éẋţŕàçţîöñ šţàḿþš. Îţ ŕéƃüîļđš ƒŕöḿ ţĥé çöñţéñţ ƒîļéš.work/cache/ĥöļđš ƒîļéš ķàþî çàñ ŕéĝéñéŕàţé çĥéàþļý, àñđ ýöü çàñ đéļéţé îţ àţ àñý ţîḿé. Ýöü àüţĥöŕ ñöţĥîñĝ ţĥéŕé: ƒîļé-þéŕ-ƒļöŵ đéƒîñîţîöñš šîţ îñ ţĥé çöḿḿîţţéđ đîŕéçţöŕý ţĥé ŕéçîþé ñàḿéš ŵîţĥflows_dir:, àñđ ķàþî ŕéàđš ñöţĥîñĝ ƒŕöḿ.kapi/flows/.- Ţĥé þŕöĵéçţ'š çöñţéẋţ šţöŕé: ķéþţ ƃý ķàþî îñ à ŵöŕķšþàçé üñđéŕ ýöüŕ đàţà đîŕéçţöŕý, öüţšîđé ţĥé çĥéçķöüţ. Îţ ĥöļđš ţĥé ţéŕḿš, ţĥé çöñţéñţ ḿéḿöŕý, ţĥé ṽöîçé þŕöƒîļéš àñđ ţĥé đéçîšîöñ ļéđĝéŕ. Éṽéŕý çĥéçķöüţ öƒ ţĥé þŕöĵéçţ šĥàŕéš îţ, àñđ éṽéŕý ĝàţé àñđ ļööķüþ ŕéàđš ƒŕöḿ îţ.
kapi context syncšĥàŕéš îţ ţĥŕöüĝĥ ţĥé ƃàçķéñđ ţĥé ŕéçîþé đéçļàŕéš üñđéŕcontext:, àñđkapi store exportŵŕîţéš îţ ţö öñé ƒîļé.
À þŕöĵéçţ çàñ àñšŵéŕ ţĥé šàḿé ǫüéšţîöñš àš Ƃöŵŕàîñ öṽéŕ îţš öŵñ çöñţéẋţ, ƒöŕ öñé þŕöĵéçţ àñđ öñé šţŕéàḿ. Ƒöŕ éẋàḿþļé, ķàþî çàñ ļîšţ ŵĥîçĥ ƃļöçķš üšé à ĝîṽéñ ţéŕḿ, ƃý çöļļéçţîöñ àñđ çööŕđîñàţé, ŵîţĥ ñö šéŕṽéŕ.
Ŕéçîþé šçĥéḿà
Ţĥé ŕéçîþé îš à ÝÀḾĻ đöçüḿéñţ. Ƃöŵŕàîñ þŕöĵéçţš ļàýéŕ à bowrain: ƃļöçķ (àñđ ţĥé öþţîöñàļ ţöþ-ļéṽéļ ṽéñüé ķéýš hooks, automations, assets àñđ brand_voice) öñţö ţĥé ƒŕàḿéŵöŕķ'š KapiProject šçĥéḿà.
version: v1
name: My App
defaults:
source_language: en
target_languages: [fr, de, ja]
collection: ui/strings
exclude:
- "**/*.test.json"
- "node_modules/**"
voice:
profile: acme-docs
coordinates:
brand: acme # a declared axis, inherited by every collection
collections:
- path: src/locales/**/*.json
format: json
- path: content/docs/**/*.md
format: markdown
target: i18n/{lang}/docs/{path}/{filename}
- name: legal
channel: docs
coordinates:
mode: reference # overrides the project on one axis, inherits the rest
source_only: true # checked, never translated
content:
- path: legal/**/*.md
format: markdown
plugins:
okapi-bridge: "^1.47.0" # map form: name → version constraint
flows:
pseudo:
steps:
- tool: pseudo-translate
config: { method: extended }
# The bowrain: block depends on the bowrain plugin. init declares the
# requirement so a plain kapi binary (without the plugin) fails fast instead of
# silently ignoring the connection.
requires:
bowrain: "*"
# Optional bowrain-server connection. Presence enables push/pull and makes the
# server the default venue for `kapi up`.
bowrain:
url: https://app.bowrain.cloud/my-team/abc123
stream: $auto # auto-detect from git branch / CI
converge: on-push # on-push (default) | manual
# Top-level lifecycle policy:
automations:
- name: pull-after-push
trigger: post-push
actions:
- type: wait_translate
- type: pull
# Top-level asset policy:
assets:
enabled: true
max_size: 100MB
Ţöþ-ļéṽéļ ƒîéļđš
| Ƒîéļđ | Ţýþé | Đéšçŕîþţîöñ |
|---|---|---|
version | šţŕîñĝ | Šçĥéḿà ṽéŕšîöñ (çüŕŕéñţļý v1) |
name | šţŕîñĝ | Þŕöĵéçţ đîšþļàý ñàḿé |
defaults | öƃĵéçţ | Þŕöĵéçţ-ŵîđé ļàñĝüàĝé, çöñţéẋţ àñđ éẋéçüţîöñ đéƒàüļţš |
collections | ļîšţ | Çöñţéñţ çöļļéçţîöñš (šéé Çöñţéñţ Çöļļéçţîöñš) |
profiles | ḿàþ | Ĝöṽéŕñàñçé ƃöüñđ þéŕ þŕöđüçţ, ķéýéđ ƃý þŕöƒîļé ñàḿé (šéé Þŕöƒîļéš àñđ çĥàññéļš) |
plugins | ḿàþ | Þļüĝîñ đéþéñđéñçîéš àš name: version-constraint |
requires | ḿàþ | Þļüĝîñ ñàḿé → ṽéŕšîöñ çöñšţŕàîñţ ţĥàţ ĝàţéš ļöàđîñĝ; à bowrain: ƃļöçķ àđđš bowrain šö à þļàîñ ķàþî ƃîñàŕý ŕéƒüšéš ţĥé ŕéçîþé |
flows | ḿàþ | Îñļîñé ƒļöŵ đéƒîñîţîöñš |
flows_dir | šţŕîñĝ | À đîŕéçţöŕý, ŕéļàţîṽé ţö ţĥé ŕéçîþé, ĥöļđîñĝ öñé ÝÀḾĻ ƒîļé þéŕ ƒļöŵ. Ñö đéƒàüļţ |
ship_gate | ĝàţé | Ţĥé ƃàŕ à ļöçàļé ḿüšţ çļéàŕ ţö ƃé šĥîþþàƃļé (šéé Ĝàţéš) |
ship_gates | ļîšţ | Þéŕ-šçöþé šĥîþ ĝàţéš, éàçĥ à when: šéļéçţöŕ þļüš à ĝàţé |
source_gate | ĝàţé | Ţĥé çöṽéŕàĝé ƃàŕ ţĥé šöüŕçé ḿüšţ çļéàŕ ƒöŕ kapi check --ship |
established_gate, established_gates | ĝàţé, ļîšţ | Ţĥé ƃàŕ ƒöŕ à ļöçàļé ţö šĥîþ àþþŕöṽéđ: à þéŕšöñ àþþŕöṽéđ ţĥé çöñţéñţ |
gates | ḿàþ | Ñàḿéđ ĝàţéš ţĥé ŕüļéš àƃöṽé ḿàý ŕéƒéŕéñçé |
bowrain | öƃĵéçţ | Ƃöŵŕàîñ-šéŕṽéŕ çöññéçţîöñ çööŕđîñàţéš (ṽéñüé ķéý) |
automations | ļîšţ | Ļöçàļ àüţöḿàţîöñ ŕüļéš (šéé Àüţöḿàţîöñš) |
assets | öƃĵéçţ | Àššéţ (îḿàĝé/ƃîñàŕý) þöļîçý |
brand_voice | öƃĵéçţ | À ṽéñüé ķéý ţĥé šéŕṽéŕ àļšö àççéþţš ƒöŕ à þŕöƒîļé àñđ çĥàññéļ ƃîñđîñĝ; ƃîñđ ţĥé ṽöîçé ŵîţĥ defaults.voice öŕ à þŕöƒîļé'š voice: îñšţéàđ |
defaults ƃļöçķ
| Ƒîéļđ | Ţýþé | Đéšçŕîþţîöñ |
|---|---|---|
source_language | šţŕîñĝ | ƂÇÞ-47 šöüŕçé ļàñĝüàĝé (ƒöŕ éẋàḿþļé en) |
target_languages | ļîšţ | ƂÇÞ-47 ţàŕĝéţ ļàñĝüàĝéš; éḿþţý ƒöŕ à šöüŕçé-öñļý þŕöĵéçţ |
collection | šţŕîñĝ | Đéƒàüļţ çöļļéçţîöñ ñàḿé ƒöŕ öŕĝàñîžîñĝ çöñţéñţ |
exclude | ļîšţ | Ĝļöƃ þàţţéŕñš ţö šķîþ đüŕîñĝ šçàññîñĝ |
formats | ḿàþ | Þéŕ-ƒöŕḿàţ đéƒàüļţ þŕéšéţš àñđ çöñƒîĝ öṽéŕŕîđéš |
voice | ƃîñđîñĝ | Ţĥé ṽöîçé þŕöƒîļé éṽéŕý çöļļéçţîöñ îš ĝöṽéŕñéđ ƃý üñļéšš à þŕöƒîļé ƃîñđš àñöţĥéŕ. profile: ñàḿéš öñé îñ ţĥé þŕöĵéçţ'š ṽöîçé šţöŕé |
coordinates | ḿàþ | Ţĥé đéçļàŕéđ àẋéš éṽéŕý çöļļéçţîöñ îñĥéŕîţš, brand àñđ mode àḿöñĝ ţĥéḿ; ţĥé šţŕüçţüŕàļ àẋéš product àñđ channel àŕé đéŕîṽéđ ƒŕöḿ channel: àñđ ñéṽéŕ ŵŕîţţéñ ĥéŕé |
materialize | šţŕîñĝ | Ŵĥéñ ţàŕĝéţ ƒîļéš àŕé ŵŕîţţéñ ƒŕöḿ ţĥé þŕöĵéçţ šţöŕé; kapi up --materialize ƒöŕçéš on-converge |
bowrain ƃļöçķ
Öñļý ţĥé çöññéçţîöñ çööŕđîñàţéš šîţ üñđéŕ bowrain::
| Ƒîéļđ | Đéšçŕîþţîöñ |
|---|---|
url | Çöḿþöüñđ ÜŔĻ: <server>/<workspace>/<project-id> öŕ <server>/projects/<id> |
stream | Šéŕṽéŕ-šîđé šţŕéàḿ ţö šýñç àĝàîñšţ; $auto àüţö-đéţéçţš ƒŕöḿ ÇÎ / ĝîţ ƃŕàñçĥ |
converge | Šéŕṽéŕ-šîđé çöñṽéŕĝéñçé þöļîçý: on-push (đéƒàüļţ) öŕ manual |
Šĥàŕîñĝ ţĥé þŕöĵéçţ'š çöñţéẋţ öñ Ƃöŵŕàîñ
Ŵĥéñ ţĥé þŕöĵéçţ'š çöñţéẋţ ļîṽéš öñ Ƃöŵŕàîñ, éṽéŕý çĥéçķöüţ àñđ éṽéŕý ţéàḿḿàţé
ŕéàđš ţĥé šàḿé ţéŕḿš, ķéþţ àñđ đŕöþþéđ šüĝĝéšţîöñš, àñđ đéçîšîöñš. Îñ ţĥé
ƃŕöŵšéŕ àñđ îñ Ķàþî Đéšķţöþ, Ŕüļéš šĥöŵš ţĥàţ ŕéçöŕđ (šéé
Ḿàþ, Ŕéàđéŕ àñđ Ŕüļéš). Ƒŕöḿ ţĥé çöḿḿàñđ ļîñé,
kapi context sync ķééþš îţ îñ šţéþ ŵîţĥ ţĥé Ƃöŵŕàîñ þŕöĵéçţ ţĥé bowrain:
ƃļöçķ çöññéçţš ţö.
kapi share --via bowrain çöññéçţš ţĥé þŕöĵéçţ àñđ ļéàṽéš ţĥé çöñţéẋţ ŵĥéŕé
îţ îš, šö ñàḿé ţĥé bowrain ƃàçķéñđ îñ ţĥé ŕéçîþé'š context: ƃļöçķ
ýöüŕšéļƒ:
context:
backend: bowrain
bowrain:
url: https://app.bowrain.cloud/my-team/abc123
Ţĥé context: ƃļöçķ ţàķéš ñö öţĥéŕ ƒîéļđ ƒöŕ ţĥîš ƃàçķéñđ. Ţĥé þŕöĵéçţ çöḿéš
ƒŕöḿ bowrain.url, àñđ ţĥé ÜŔĻ ḿüšţ ñàḿé à ŵöŕķšþàçé þŕöĵéçţ
(<server>/<workspace>/<project-id>). À þŕöĵéçţ ĥàš öñé çöñţéẋţ, šö éṽéŕý
šţŕéàḿ ŕéàđš ţĥé šàḿé öñé. À ŕéçîþé ţĥàţ ñàḿéš ţĥé bowrain ƃàçķéñđ ŵîţĥöüţ à
bowrain: ƃļöçķ îš ŕéƒüšéđ, ŵîţĥ ţĥé ƃļöçķ ţö àđđ.
Ţĥé šýñç šîĝñš îñ àš ţĥé þéŕšöñ kapi auth login šţöŕéđ, öŕ îñ ÇÎ ŵîţĥ
BOWRAIN_AUTH_TOKEN àñđ BOWRAIN_SERVER_URL ţöĝéţĥéŕ. Ţĥé šîĝñ-îñ ŕéàçĥéš
öñļý ţĥé šéŕṽéŕ îţ ñàḿéš: à ŕéçîþé ŵĥöšé bowrain.url ñàḿéš àñöţĥéŕ šéŕṽéŕ
îš ŕéƒüšéđ ƃéƒöŕé àñý ŕéǫüéšţ, àñđ šö îš à ÇÎ ţöķéñ ŵîţĥ ñö šéŕṽéŕ ÜŔĻ. Ţĥé
ÜŔĻ ḿüšţ üšé https://, éẋçéþţ ƒöŕ à šéŕṽéŕ öñ localhost. Ŕéàđîñĝ ţĥé
çöñţéẋţ ñééđš ṽîéŵ àççéšš ţö ţĥé
þŕöĵéçţ; šĥàŕîñĝ çĥàñĝéš ƃàçķ ñééđš þéŕḿîššîöñ ţö ḿàñàĝé ţéŕḿš.
BOWRAIN_PROJECT_URL þöîñţš ţĥé çöñţéẋţ àţ àñöţĥéŕ þŕöĵéçţ, àš îţ đöéš ƒöŕ
kapi push. kapi share ñàḿéš ţĥîš þļàçé Ḿàñàĝéđ öñ Ƃöŵŕàîñ.
Šéţţîñĝš à þüšĥ çàŕŕîéš
Ţĥŕéé ŕéçîþé šéţţîñĝš ĝöṽéŕñ ŵĥàţ ţĥé šéŕṽéŕ đöéš ŵîţĥ ţĥé þŕöĵéçţ:
bowrain.converge, defaults.translate_after àñđ ţĥé term_rules: ţĥé ŕéçîþé
đéçļàŕéš öñ îţš ƒļöŵ šţéþš àñđ ţööļ þŕéšéţš. Éṽéŕý þüšĥ çàŕŕîéš àļļ ţĥŕéé, àţ
ţĥéîŕ 郃éçţîṽé ṽàļüéš, šö à ķéý ţĥé ŕéçîþé ļéàṽéš üñšéţ îš šéñţ àš îţš đéƒàüļţ
(on-push, written àñđ ñö ţéŕḿ ŕüļéš). Ţĥé þüšĥ çöḿþàŕéš ţĥéḿ ŵîţĥ ţĥé ṽàļüéš
ţĥé šéŕṽéŕ ĥöļđš àñđ šéñđš öñļý à šéţţîñĝ ţĥàţ đéŕš, šö àñ üñçĥàñĝéđ ŕéçîþé
çöšţš ñö éẋţŕà ŕéǫüéšţ àñđ þŕîñţš ñöţĥîñĝ.
Ţĥé šéŕṽéŕ ĥöļđš îţš ţéŕḿîñöļöĝý ĝàţé àñđ îţš ţŕàñšļàţîöñš ţö ţĥé ŕéçîþé'š ţéŕḿ
ŕüļéš ƃéšîđé ţĥé ŵöŕķšþàçé ţéŕḿš, àš kapi check --ship đöéš, šö à ļàñĝüàĝé
ţĥé ŕüļéš ñàḿé îš ĝöṽéŕñéđ öñ ƃöţĥ šîđéš àñđ à ţŕàñšļàţîöñ ţĥàţ ḿîššéš à ŕüļé
ĥöļđš ţĥé ļàñĝüàĝé ƃàçķ üñļéšš ţĥé ŕüļé îš advisory.
Ţĥé šéŕṽéŕ àþþļîéš ţĥéšé šéţţîñĝš öñļý ƒŕöḿ à þüšĥ ţö ţĥé þŕöĵéçţ'š đéƒàüļţ
šţŕéàḿ (main üñļéšš ţĥé þŕöĵéçţ ŕéçöŕđš àñöţĥéŕ). À þüšĥ ţö àñý öţĥéŕ šţŕéàḿ
çàŕŕîéš ţĥéḿ, àñđ ţĥé šéŕṽéŕ ķééþš îţš öŵñ ṽàļüéš àñđ šàýš šö îñ ţĥé þüšĥ
öüţþüţ.
Öñ ţĥé đéƒàüļţ šţŕéàḿ, ŵĥàţ à þüšĥ ḿàý çĥàñĝé đéþéñđš öñ ţĥé đîŕéçţîöñ:
| Šéţţîñĝ | Šţŕîçţéšţ ţö ļööšéšţ |
|---|---|
translate_after | established, written, none |
converge | manual, on-push |
term_rules | éṽéŕý ŕüļé ţĥé šéŕṽéŕ ĥöļđš, ķéþţ üñçĥàñĝéđ, ŵîţĥ àñý àđđéđ; à ŕüļé đŕöþþéđ öŕ çĥàñĝéđ |
À çĥàñĝé ţöŵàŕđ ţĥé šţŕîçţ éñđ àþþļîéš ƒöŕ àñýöñé ŵĥö ḿàý þüšĥ ţĥé þŕöĵéçţ,
îñçļüđîñĝ à ŵöŕķšþàçé ḿéḿƃéŕ öŕ à ÇÎ ţöķéñ ŵîţĥ ţĥé contribute šçöþé. À
çĥàñĝé ţöŵàŕđ ţĥé ļööšé éñđ àþþļîéš öñļý ŵĥéñ ţĥé þüšĥéŕ ḿàý ḿàñàĝé ţĥé þŕöĵéçţ
(à ŵöŕķšþàçé öŵñéŕ öŕ àđḿîñ). Ƒöŕ àñýöñé éļšé ţĥé šéŕṽéŕ ķééþš îţš ṽàļüé, àñđ
kapi push àñđ kapi up þŕîñţ à ļîñé šüçĥ àš:
Server setting translate_after stays established: the recipe asks for none, which allows more than the project does. A workspace owner or admin (manage_project) applies it by pushing the recipe.
Éṽéŕý þüšĥ ŕéþéàţš ţĥé ļîñé üñţîļ ţĥé ŕéçîþé àñđ ţĥé šéŕṽéŕ àĝŕéé. Ţĥé šéŕṽéŕ ŕéçöŕđš éàçĥ çĥàñĝé îţ àþþļîéš îñ ţĥé àüđîţ ļöĝ, ŵîţĥ ţĥé þüšĥéŕ àñđ ţĥé ṽàļüé ƃéƒöŕé àñđ àƒţéŕ. Îţš ŕüñš ŕéàđ ţĥé ṽàļüé îţ ĥöļđš.
Ļîƒéçýçļé (hooks, automations) àñđ àššéţ þöļîçý (assets) šîţ àţ ţĥé ţöþ ļéṽéļ öƒ ţĥé ŕéçîþé, ƃéšîđé bowrain:, ƃéçàüšé ţĥéý àŕé þŕöĵéçţ þöļîçý.
bowrain:, automations:, assets: àñđ brand_voice: àŕé ƃöŵŕàîñ ŕéçîþé éẋţéñšîöñš. ķàþî ŕéàđš ţĥéḿ öñļý ŵĥéñ ţĥé kapi-bowrain þļüĝîñ îš îñšţàļļéđ, àñđ ķééþš ţĥéḿ üñçĥàñĝéđ öţĥéŕŵîšé. kapi init, kapi init-connect àñđ kapi config server.url … đéçļàŕé requires: { bowrain: "*" } ŵĥéñéṽéŕ ţĥéý ŵŕîţé à bowrain: ƃļöçķ. À þļàîñ kapi ƃîñàŕý ŵîţĥöüţ ţĥé þļüĝîñ ţĥéñ ŕéƒüšéš ţĥé ŕéçîþé ŵîţĥ à "ŕéǫüîŕéš ţĥé ƃöŵŕàîñ þļüĝîñ" éŕŕöŕ. Šéé Ç-01: Ţĥé þŕöĵéçţ ḿöđéļ.
Çöñţéñţ Çöļļéçţîöñš
Éàçĥ éñţŕý üñđéŕ collections: îš à çöñţéñţ çöļļéçţîöñ. Ƃàŕé éñţŕîéš àŕé šîñĝļé-þàţţéŕñ çöļļéçţîöñš; ñàḿéđ çöļļéçţîöñš ĝŕöüþ ḿüļţîþļé îţéḿš ţöĝéţĥéŕ.
Ýöü çàñ éđîţ collections: ƃý ĥàñđ, öŕ ŵîţĥ ţĥé çöŕé kapi çöḿḿàñđš (ñö ƃöŵŕàîñ þļüĝîñ ŕéǫüîŕéđ; ţĥéý öñļý ţöüçĥ ţĥé ļöçàļ ŕéçîþé):
kapi add "src/**/*.json" # append a content pattern (format auto-detected)
kapi rm "src/legacy/*.json" # remove the mapping, or add to the exclude list
kapi ls # list the files the content tracks
kapi add "src/**/*.md" --format markdown # pass --format only to override detection
kapi ls --stats # with per-file block and word counts
add/rm/ls àŕé ƒŕàḿéŵöŕķ çöḿḿàñđš; šýñç šţàţé (çĥàñĝéđ-ṽš-šéŕṽéŕ) îš kapi status.
collections:
# Bare entry: single source pattern
- path: src/locales/**/*.json
format: json
# With output path template
- path: content/docs/**/*.md
format: markdown
target: i18n/{lang}/docs/{path}/{filename}
# Per-entry overrides
- path: legacy/**/*.properties
format: java-properties
source_language: en-GB
collection: legacy
# Named collection: its items live under content:, relative to base:
- name: ui
channel: app
base: src
preview:
kind: storybook
url: https://storybook.example.com
content:
- path: "**/*.tsx"
format:
name: exec
config:
command: "vp neokapi-i18n extract --stream"
- path: "i18n/en/*.json"
format: json
Çöļļéçţîöñ ƒîéļđš
| Ƒîéļđ | Ţýþé | Đéšçŕîþţîöñ |
|---|---|---|
name | šţŕîñĝ | Çöļļéçţîöñ ñàḿé; ŕéǫüîŕéđ ţö ƃîñđ à channel: |
base | šţŕîñĝ | Ţĥé đîŕéçţöŕý ţĥîš çöļļéçţîöñ ļîṽéš îñ; éṽéŕý path, target àñđ îţéḿ base ƃéļöŵ îš ŵŕîţţéñ ŕéļàţîṽé ţö îţ |
channel | šţŕîñĝ | Ţĥé þöîñţ îñ ţĥé çöñţéẋţ šþàçé ţĥîš çöñţéñţ šîţš àţ: profile/channel, öŕ à ƃàŕé channel (šéé Þŕöƒîļéš àñđ çĥàññéļš) |
coordinates | ḿàþ | Đéçļàŕéđ àẋéš ţĥîš çöļļéçţîöñ šéţš, öṽéŕļàîđ öñ defaults.coordinates þéŕ àẋîš: à çöļļéçţîöñ ḿöṽéš öñ ţĥé öñé àẋîš îţ đéŕš öñ àñđ îñĥéŕîţš ţĥé ŕéšţ |
source_only | ƃööļ | Ţĥîš çöļļéçţîöñ ĥàš ñö ţàŕĝéţ ļàñĝüàĝé àñđ îš ñéṽéŕ ţŕàñšļàţéđ: à ŕüñ ŕéàđš îţ, çĥéçķš îţ, àñđ ŵŕîţéš ñöţĥîñĝ ƃàçķ |
preview | öƃĵéçţ | Ŵĥéŕé ţĥîš çöļļéçţîöñ'š šţŕîñĝš çàñ ƃé ŕéàđ îñ þļàçé: kind (storybook) àñđ url (šéé Ŕéṽîéŵ îñ þļàçé) |
content | ļîšţ | Ţĥé çöļļéçţîöñ'š çöñţéñţ îţéḿš |
collection | šţŕîñĝ | Çöļļéçţîöñ ŕöüţîñĝ öṽéŕŕîđé |
source_language | šţŕîñĝ | Šöüŕçé ļàñĝüàĝé öṽéŕŕîđé |
target_languages | ļîšţ | Ţàŕĝéţ ļàñĝüàĝé öṽéŕŕîđé |
Çöñţéñţ îţéḿ ƒîéļđš
| Ƒîéļđ | Ţýþé | Đéšçŕîþţîöñ |
|---|---|---|
path | šţŕîñĝ | Ĝļöƃ þàţţéŕñ ƒöŕ šöüŕçé ƒîļéš (šüþþöŕţš {lang} þļàçéĥöļđéŕ) |
format | šţŕîñĝ / öƃĵéçţ | Ƒîļé ƒöŕḿàţ ÎĐ (ƒöŕ éẋàḿþļé json, html) öŕ öƃĵéçţ ŵîţĥ name/config/preset |
target | šţŕîñĝ | Öüţþüţ þàţĥ þàţţéŕñ ƒöŕ ţàŕĝéţ ƒîļéš (šüþþöŕţš {lang} àñđ {path}) |
base | šţŕîñĝ | Đîŕéçţöŕý à ḿàţçĥéđ ƒîļé'š þàţĥ îš ḿàđé ŕéļàţîṽé ţö ƒöŕ ţàŕĝéţ-ţöķéñ éẋþàñšîöñ; đéƒàüļţš ţö ţĥé ĝļöƃ'š ƒîẋéđ þŕéƒîẋ |
collection | šţŕîñĝ | Çöļļéçţîöñ ŕöüţîñĝ öṽéŕŕîđé ƒöŕ ţĥîš éñţŕý |
source_language | šţŕîñĝ | Šöüŕçé ļàñĝüàĝé öṽéŕŕîđé ƒöŕ ţĥîš éñţŕý |
target_languages | ļîšţ | Ţàŕĝéţ ļàñĝüàĝé öṽéŕŕîđé ƒöŕ ţĥîš éñţŕý |
assets | öƃĵéçţ | Þéŕ-éñţŕý àššéţ þöļîçý öṽéŕŕîđé |
asset_max_size | šţŕîñĝ | Þéŕ-éñţŕý àššéţ ḿàẋ šîžé öṽéŕŕîđé |
À ƃàŕé éñţŕý çàŕŕîéš path, format àñđ target đîŕéçţļý öñ ţĥé çöļļéçţîöñ àñđ ĥàš ñö content: ļîšţ.
Þŕöƒîļéš àñđ çĥàññéļš
Çöñţéñţ îš ŵŕîţţéñ ƒöŕ à þöîñţ îñ ţĥé çöñţéẋţ šþàçé. Ţŵö öƒ îţš àẋéš àŕé šţŕüçţüŕàļ: ţĥé þŕöđüçţ îţ ƃéļöñĝš ţö àñđ ţĥé çĥàññéļ îţ šĥîþš öñ. À ķéý üñđéŕ profiles: îš à þŕöđüçţ, ţĥé çĥàññéļš ţĥàţ þŕöƒîļé đéçļàŕéš àŕé ţĥé çĥàññéļš ţĥàţ þŕöđüçţ šĥîþš öñ, àñđ à ñàḿéđ çöļļéçţîöñ ñàḿéš îţš þöîñţ ŵîţĥ öñé channel: ŕéƒéŕéñçé. Ţĥé đéçļàŕéđ àẋéš (brand, mode, àñđ àñý ţĥé þŕöĵéçţ àđđš) çöḿé ƒŕöḿ defaults.coordinates àñđ à çöļļéçţîöñ'š öŵñ coordinates:.
profiles:
acme:
channels: [app, docs]
voice:
profile: acme-docs
acme-labs:
channels: [app]
voice:
profile: acme-labs
termstore: acme-labs-terms
valid_from: 2026-09-01
collections:
- name: acme-docs
channel: docs # only acme declares it; the bare form resolves
content:
- path: docs/**/*.md
- name: labs-app
channel: acme-labs/app # both declare `app`; qualify it
content:
- path: labs/src/i18n/**/*.kbf.json
| Ƒîéļđ | Đéšçŕîþţîöñ |
|---|---|
channels | Ţĥé çĥàññéļš ţĥîš þŕöđüçţ šĥîþš öñ |
voice | Ţĥé ṽöîçé þŕöƒîļé ţĥàţ ĝöṽéŕñš ţĥîš þŕöđüçţ, öṽéŕŕîđîñĝ defaults.voice |
termstore | À ţéŕḿš šţöŕé, ƃý ñàḿé, ƃöüñđ ƒöŕ ţĥîš þŕöđüçţ'š ļöçàļ ŕüñš |
concept | À çöñçéþţ ŕéƒéŕéñçé (term:<id>) ţĥé þŕöƒîļé çàŕŕîéš ƒöŕ đîšþļàý |
valid_from, valid_to | Ţĥé þŕöƒîļé'š ṽàļîđîţý ŵîñđöŵ |
À þŕöƒîļé ţĥàţ ƃîñđš ñö voice: öƒ îţš öŵñ üšéš ţĥé ṽöîçé þŕöƒîļé ţĥé šţöŕé ĥöļđš üñđéŕ îţš ñàḿé. Þŕöƒîļé ñàḿéš àñđ çĥàññéļš àŕé šļüĝš (ļöŵéŕçàšé ļéţţéŕš, đîĝîţš àñđ ĥýþĥéñš). Ţĥéý àŕé šţàƃļé îđéñţîƒîéŕš, šéñţ ŵîţĥ éàçĥ þüšĥ àš ţĥé çöñţéñţ'š þŕöđüçţ àñđ çĥàññéļ çööŕđîñàţéš. À ƃàŕé channel: ţĥàţ ţŵö þŕöƒîļéš đéçļàŕé îš à ļöàđ éŕŕöŕ ñàḿîñĝ ƃöţĥ ǫüàļîƒîéđ šþéļļîñĝš. À çöļļéçţîöñ ţĥàţ ƃîñđš ñö çĥàññéļ üšéš defaults.voice àñđ ţĥé þŕöĵéçţ'š öŵñ ţéŕḿš.
À þŕöƒîļé'š termstore: àþþļîéš ţö ļöçàļ ŕüñš öñļý. Ţĥé šéŕṽéŕ àþþļîéš ţĥé ŵöŕķšþàçé ţéŕḿš îñšţéàđ, àñđ à çöññéçţéđ þŕöĵéçţ ţĥàţ ƃîñđš à ţéŕḿš šţöŕé þéŕ þŕöƒîļé ŵàŕñš àƃöüţ ţĥîš öñ éṽéŕý ŕüñ.
Ĝàţéš
À ĝàţé ñàḿéš ţĥé ŕüñĝ öƒ ţĥé ţàŕĝéţ ļàđđéŕ à šçöþé ḿüšţ ŕéàçĥ, àñđ ŵĥö ḿàý ĥàṽé àþþŕöṽéđ îţ, ƒöŕ ţĥé šçöþé ţö çöüñţ àš šĥîþþàƃļé.
ship_gatešéţš ţĥé ƃàŕ ƒöŕ éṽéŕý ļöçàļé.ship_gatesŕéƒîñéš îţ þéŕ šçöþé ŵîţĥ àwhen:šéļéçţöŕ, šö à ļéĝàļ çöļļéçţîöñ çàñ ŵàîţ ƒöŕ à þéŕšöñ ŵĥîļé à ĥéļþ çöļļéçţîöñ šĥîþš öñ çĥéçķš.source_gateîš ţĥé šöüŕçé-šîđé çöṽéŕàĝé ƃàŕkapi check --shipéṽàļüàţéš.defaults.translate_afterîš ţĥé þéŕ-ƃļöçķ ĥöļđ ƃéƒöŕé à ŕüñ ţŕàñšļàţéš, àñđtranslate_after: noneţüŕñš îţ öƒƒ.
Šĥîþ šţàţéš đéšçŕîƃéš ţĥé šţàţé ţĥé šéŕṽéŕ đéŕîṽéš ƒŕöḿ ţĥéšé.
Ţööļ çöñƒîĝüŕàţîöñ
À ƒļöŵ šţéþ'š config: ķéýš àŕé ţĥé ţööļ'š öŵñ šçĥéḿà ķéýš, ļîšţéđ îñ ţĥé
ţööļ ŕéƒéŕéñçé. Ţĥé
ŵöŕđîñĝ çöñšţŕàîñţ îš öñé ķéý éṽéŕýŵĥéŕé îţ àþþļîéš: term-check, translate
àñđ recycle ţàķé term_rules:, à ļîšţ öƒ ŕüļéš ñàḿîñĝ à ţéŕḿ, ŵĥàţ ţö üšé
îñšţéàđ, àñđ ŵĥéţĥéŕ à ṽîöļàţîöñ ƒàîļš à çĥéçķ. Ţĥé ṽöîçé þŕöƒîļé çàŕŕîéš ţĥé šàḿé ŕüļéš
üñđéŕ îţš vocabulary: šéçţîöñ, àñđ à ŕüñ ŕéšöļṽéš ţĥé ţéŕḿš îñ ƒöŕçé àţ à
ƃļöçķ'š þöîñţ ƒŕöḿ ţĥé þŕöƒîļé àñđ ţĥé ƃöüñđ ţéŕḿš šţöŕé.
Ƒöŕḿàţ öƃĵéçţ ƒöŕḿ
Ŵĥéñ ýöü ñééđ ţö çöñƒîĝüŕé à ƒöŕḿàţ (àþþļý à þŕéšéţ, þàšš öþţîöñš, ŕüñ à šüƃþŕöçéšš éẋţŕàçţöŕ) üšé ţĥé öƃĵéçţ ƒöŕḿ:
collections:
- path: "src/**/*.tsx"
format:
name: exec
config:
command: "vp neokapi-i18n extract --stream"
- path: "docs/**/*.html"
format:
name: html
preset: strict-extraction
Àüţöḿàţîöñš
Àüţöḿàţîöñš àŕé ŕüļéš ţĥàţ ŕüñ àüţöḿàţîçàļļý àţ ļîƒéçýçļé þöîñţš, đéçļàŕéđ àţ ţĥé ţöþ ļéṽéļ öƒ ţĥé ŕéçîþé:
automations:
- name: checks-before-push
trigger: pre-push
actions:
- type: run_flow
config:
flow: qa
- type: wait_translate
- name: auto-pull-after-push
trigger: post-push
actions:
- type: pull
Àüţöḿàţîöñ ƒîéļđš
| Ƒîéļđ | Đéšçŕîþţîöñ |
|---|---|
name | Ŕüļé ñàḿé |
trigger | Ļîƒéçýçļé þöîñţ: pre-push, post-push, pre-pull, post-pull, pre-flow, post-flow |
actions | Ļîšţ öƒ àçţîöñš (run_flow, wait_translate, pull, push) |
enabled | Öþţîöñàļ ƃööļéàñ (đéƒàüļţš ţö true) |
Þŕöĵéçţ Đîšçöṽéŕý
ķàþî šéàŕçĥéš ƒöŕ à kapi.yaml ŕéçîþé ƃý ŵàļķîñĝ üþ ţĥé đîŕéçţöŕý ţŕéé (ļîķé ĝîţ):
cd my-app/src/locales/fr/
kapi status # finds kapi.yaml at ../../../kapi.yaml
Àļļ çöḿḿàñđš ŵöŕķ ƒŕöḿ àñý šüƃđîŕéçţöŕý ŵîţĥîñ ţĥé þŕöĵéçţ. À đîŕéçţöŕý ĥöļđš àţ ḿöšţ öñé kapi.yaml, šö đîšçöṽéŕý îš üñàḿƃîĝüöüš; àñ éẋþļîçîţ -p <path> šţîļļ öṽéŕŕîđéš îţ.
Ṽéŕšîöñ Çöñţŕöļ
Çöḿḿîţ ţö ĝîţ
kapi.yaml: ţĥé ŕéçîþé (šîñĝļé šöüŕçé öƒ ţŕüţĥ ƒöŕ çöñƒîĝüŕàţîöñ)- ţĥé àĝéñţ ŵîŕîñĝ
kapi initŵŕöţé (.mcp.json,.claude/skills/kapi/SKILL.md, àñđ ţĥé öţĥéŕ ĥöšţš' ƒîļéš ŵĥéŕé îţ ŵŕöţé ţĥéḿ)
Đö ÑÖŢ çöḿḿîţ
.kapi/: kapi init ŵŕîţéš .kapi/.gitignore ŵîţĥ öñé ļîñé, *, šö ñöñé öƒ ţĥé çàçĥé ŕéàçĥéš ĝîţ.
Đéļéţîñĝ .kapi/work/ çöšţš à ŕé-éẋţŕàçţîöñ àñđ ñöţĥîñĝ éļšé. Đéļéţîñĝ .kapi/ àļšö ļöšéš ţĥé šýñç šţàţé îñ .kapi/sync/ àñđ, îƒ ţĥé þŕöĵéçţ üšéš ŕéđàçţîöñ, ţĥé ŵîţĥĥéļđ öŕîĝîñàļš îñ .kapi/vault/. Ţĥöšé öŕîĝîñàļš àŕé ļöçàļ-öñļý àñđ çàññöţ ƃé ŕéƃüîļţ. Ýöüŕ çöñţéẋţ îš îñ ţĥé ŵöŕķšþàçé àñđ šüŕṽîṽéš. Đéļéţîñĝ ţĥé ŵöŕķšþàçé ļöšéš éṽéŕý þŕöĵéçţ'š ţéŕḿš, ṽöîçé þŕöƒîļéš, çöñţéñţ ḿéḿöŕý àñđ đéçîšîöñš, šö ƃéƒöŕé ýöü ŕéḿöṽé îţ, þüšĥ éàçĥ þŕöĵéçţ'š çöñţéẋţ ţö îţš ƃàçķéñđ öŕ ţàķé à çöþý ŵîţĥ kapi store export -o <file>.kpz.
Îñîţîàļîžàţîöñ
Çŕéàţé à ñéŵ ƃöŵŕàîñ þŕöĵéçţ:
cd my-app/
kapi init
Îñ îñţéŕàçţîṽé ḿöđé (đéƒàüļţ ŵĥéñ šţđîñ îš à ţéŕḿîñàļ), kapi init þŕéšéñţš à ĝüîđéđ šéţüþ ŵîžàŕđ ŵĥéŕé ýöü çàñ šîĝñ îñ, çĥööšé à ŵöŕķšþàçé, àñđ çöñƒîĝüŕé ýöüŕ þŕöĵéçţ.
Ƒöŕ ñöñ-îñţéŕàçţîṽé üšàĝé (ƒöŕ éẋàḿþļé ÇÎ/ÇĐ), üšé ƒļàĝš:
# Local-only project (no bowrain: block written)
kapi init --source en --targets fr,de,ja
# Connect to a server (anonymous claim)
kapi init --server https://app.bowrain.cloud --anonymous
# Apply a framework preset
kapi init --preset nextjs
# Connect to an existing project
kapi init --server https://app.bowrain.cloud --project abc123
Îñîţ ƒļàĝš
| Ƒļàĝ | Đéšçŕîþţîöñ |
|---|---|
--server | Šéŕṽéŕ ÜŔĻ |
--workspace | Çŕéàţé ţĥé þŕöĵéçţ îñ ţĥîš ŵöŕķšþàçé (šļüĝ) |
--project | Çöññéçţ ţö àñ éẋîšţîñĝ þŕöĵéçţ ƃý ÎĐ |
--name | Þŕöĵéçţ ñàḿé (đéƒàüļţ: çüŕŕéñţ đîŕéçţöŕý ñàḿé) |
--source | Šöüŕçé ļöçàļé (đéƒàüļţ: en) |
--targets | Ţàŕĝéţ ļöçàļéš, çöḿḿà-šéþàŕàţéđ (ƒöŕ éẋàḿþļé nb,fr) |
--anonymous | Çŕéàţé à þŕöĵéçţ ŵîţĥöüţ šîĝñîñĝ îñ |
--email | Çŕéàţé à þŕöĵéçţ àñđ éḿàîļ à ļîñķ ţö çļàîḿ îţ |
--preset | Àþþļý à ƒŕàḿéŵöŕķ þŕéšéţ (ƒöŕ éẋàḿþļé nextjs, react-intl, angular) |
kapi init ŵŕîţéš:
kapi.yamlŕéçîþé àţ ţĥé þŕöĵéçţ ŕööţ (ŵîţĥ àbowrain:ƃļöçķ ŵĥéñ à šéŕṽéŕ ŵàš šüþþļîéđ).kapi/, ţĥé çĥéçķöüţ'š çàçĥé, ŵîţĥ à.gitignoreöƒ*ţĥàţ ķééþš ţĥé ŵĥöļé đîŕéçţöŕý öüţ öƒ ṽéŕšîöñ çöñţŕöļ
Šéŕṽéŕ Çöññéçţîöñ
Ţĥé ƃļöçķ'š url ƒîéļđ îš à çöḿþöüñđ ÜŔĻ ţĥàţ éñçöđéš ţĥé šéŕṽéŕ àđđŕéšš, ŵöŕķšþàçé, àñđ þŕöĵéçţ ÎĐ:
bowrain:
# Workspace project
url: https://app.bowrain.cloud/my-team/abc123
# Direct project (no workspace)
# url: https://app.bowrain.cloud/projects/abc123
stream: $auto
Öñçé çöññéçţéđ, ýöü çàñ šýñç ŵîţĥ ţĥé šéŕṽéŕ:
kapi push # Upload local changes to the server
kapi pull # Fetch results and recipe changes from the server
kapi status # Coverage, ship standing, and the server delta
Ţĥé àçţîṽé šéŕṽéŕ ÜŔĻ îš ŕéšöļṽéđ ƒŕöḿ (ƒîŕšţ ḿàţçĥ ŵîñš):
url:üñđéŕ ţĥé ŕéçîþé'šbowrain:ƃļöçķ--serverƒļàĝBOWRAIN_SERVER_URLéñṽîŕöñḿéñţ ṽàŕîàƃļé /server.urlîñ ţĥé þéŕ-ḿàçĥîñé ƃöŵŕàîñ çöñƒîĝ- Éẋîšţîñĝ àüţĥ šţàţé (ƒŕöḿ
kapi auth login) - Ţĥé ĥöšţéđ šéŕṽîçé (
https://app.bowrain.cloud): çöḿḿàñđš ţĥàţ çöñţàçţ à šéŕṽéŕ ƒàļļ ƃàçķ ţö îţ; šéļƒ-ĥöšţéđ đéþļöýḿéñţš çöñƒîĝüŕé öñé öƒ ţĥé àƃöṽé