Skip to content

comvi push

View .md

Upload the translation files under translationsPath to the project so new keys reach translators.

Terminal
comvi push [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.

src/locales/
src/locales/
├── en.json # default namespace
├── de.json
├── common/
│ ├── en.json
│ └── de.json
└── auth/
└── en.json
src/locales/common/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.

comvi push --dry-run
📦 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.

Preview, then push the source language
comvi push --locale en --dry-run
comvi push --locale en --force-mode keep
.github/workflows/push-translations.yml
on:
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 }}
Push one namespace from a monorepo package
comvi push --config ../../.comvirc.json --ns common

CI 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.

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