comvi push
Upload the translation files under translationsPath to the project so new keys reach
translators.
comvi push [options]Options
Section titled “Options”| Option | Default | Meaning |
|---|---|---|
-c, --config <path> |
nearest .comvirc.json |
Config file to read |
-l, --locale <locales> |
locales in config, else all detected |
Comma-separated BCP-47 codes to upload |
-n, --ns <namespaces> |
namespaces in config, else all detected |
Comma-separated namespaces to upload |
-p, --path <path> |
translationsPath in config |
Source directory |
--dry-run |
off | Print how many keys would be created, updated and conflict, upload nothing |
--force-mode <mode> |
push.forceMode in config, else ask |
Conflict handling: override, keep, ask or abort. Any other value exits 4 |
A flag replaces the config value for that run; the two lists never merge.
Source layout
Section titled “Source layout”src/locales/├── en.json # default namespace├── de.json├── common/│ ├── en.json│ └── de.json└── auth/ └── en.json{ "greeting": "Hello, {name}!", "nav": { "home": "Home" }, "nav.settings": "Settings"}The layout follows fileTemplate, the same one comvi pull writes. With the default
template the root files are the default namespace and each directory is another
namespace. A file may be flat or nested: {"nav": {"home": "Home"}} uploads the key
nav.home, and dots written in a key are kept. The next pull writes that file back flat.
Every leaf must be a string. A number, boolean, null or array fails the push with exit
4 and names the file and key, and so does a key defined both flat and nested. i18next
plural suffixes such as _one and _other are not converted to ICU; import an i18next
catalog through the dashboard instead.
Conflicts
Section titled “Conflicts”📦 Push preview (dry run): ✅ Created: 3 keys 📝 Updated: 1 translations ⚠️ Conflicts: 2 keys
Run with --force-mode override to overwrite remote values. Run with --force-mode keep to upload only non-conflicting values.A conflict is a key whose platform value differs from the local one. --force-mode decides
what happens to it:
| Mode | Behaviour |
|---|---|
ask |
Prompt for override, keep or abort when at least one conflict exists. The prompt needs a TTY; without one the push fails. A conflict-free push runs unattended |
override |
Local values replace the platform values |
keep |
Platform values stay; only new keys are created |
abort |
Stop at the first conflict; nothing is written, new keys included |
Push never deletes keys. A namespace the project does not have is created and never becomes the default namespace. A locale the project does not have is skipped without an error, and so is a locale outside the API key owner’s language restrictions: the push reports success and those values never arrive. Add the locale to the project first.
Examples
Section titled “Examples”comvi push --locale en --dry-runcomvi push --locale en --force-mode keepon: push: branches: [main] paths: ['src/locales/**']jobs: push: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 - run: npm ci - run: npx comvi push --locale en --force-mode abort env: COMVI_API_KEY: ${{ secrets.COMVI_API_KEY }}comvi push --config ../../.comvirc.json --ns commonCI has no TTY, so pick keep, override or abort there. abort fails the job the
moment a local value would overwrite translator work; keep uploads the new keys and
leaves existing values alone.
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 |
Push done, dry run finished, or no translation files found |
1 |
Missing .comvirc.json, auth failure, network error, abort on a conflict, or ask without a TTY |
4 |
Invalid config, an unknown --force-mode, a non-string leaf, a key defined both flat and nested, or a --dotenv file that does not exist |
Next steps
Section titled “Next steps”- comvi pull: download the project back to disk.
- comvi typegen: regenerate types after pushing new keys.
- Split translations into namespaces: what a namespace costs the app that loads it.
- CI/CD integration: full pipeline examples.