@chesterbrains/translify-cli 0.1.0 → 0.2.0
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.
- package/README.md +41 -20
- package/dist/commands/publish.js +10 -3
- package/dist/commands/pull.js +7 -1
- package/dist/commands/push.js +9 -0
- package/dist/errors.js +3 -1
- package/dist/http.js +7 -1
- package/dist/index.js +12 -3
- package/dist/render.js +10 -0
- package/dist/version.js +1 -1
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -29,14 +29,14 @@ Requires Node 20 or newer.
|
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
# run without installing
|
|
32
|
-
npx
|
|
32
|
+
npx translify --help
|
|
33
33
|
|
|
34
|
-
# or add it to a project
|
|
35
|
-
npm i -D
|
|
34
|
+
# or add it to a project
|
|
35
|
+
npm i -D translify
|
|
36
36
|
npx translify --help
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
`translify` is an alias of `@chesterbrains/translify-cli`: the same CLI, so `npm i -D @chesterbrains/translify-cli` and `npm i -D translify` both work.
|
|
40
40
|
|
|
41
41
|
## Create a secret key
|
|
42
42
|
|
|
@@ -59,14 +59,12 @@ locales/
|
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
export TRANSLIFY_SECRET_KEY=sk_...
|
|
62
|
-
npx
|
|
63
|
-
npx
|
|
64
|
-
npx
|
|
65
|
-
npx
|
|
62
|
+
npx translify init # detects your layout, writes translify.json
|
|
63
|
+
npx translify push --dry-run # show what would change, write nothing
|
|
64
|
+
npx translify push
|
|
65
|
+
npx translify pull
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
(If you installed it with `npm i -D`, `npx translify ...` is equivalent.)
|
|
69
|
-
|
|
70
68
|
`translify.json`:
|
|
71
69
|
|
|
72
70
|
```json
|
|
@@ -111,9 +109,9 @@ ARB files always use a fixed `namespace`. The `locales` map is only needed for l
|
|
|
111
109
|
Set the key first (`export TRANSLIFY_SECRET_KEY=sk_...` or `translify login`), then:
|
|
112
110
|
|
|
113
111
|
```bash
|
|
114
|
-
npx
|
|
115
|
-
npx
|
|
116
|
-
npx
|
|
112
|
+
npx translify init
|
|
113
|
+
npx translify push
|
|
114
|
+
npx translify pull
|
|
117
115
|
flutter gen-l10n
|
|
118
116
|
```
|
|
119
117
|
|
|
@@ -132,8 +130,9 @@ flutter gen-l10n
|
|
|
132
130
|
|
|
133
131
|
Options:
|
|
134
132
|
|
|
135
|
-
- `push`: `--dry-run`, `--overwrite-targets`, `--prune`, `-y/--yes`, `--namespace <ns>`
|
|
136
|
-
- `pull` and `status`: `--from <working|env:slug>`, `--locale <locale>` (as named on disk), `--namespace <ns
|
|
133
|
+
- `push`: `--dry-run`, `--overwrite-targets`, `--prune`, `-y/--yes`, `--namespace <ns>`, `--status <draft|needs-review|approved>`
|
|
134
|
+
- `pull` and `status`: `--from <working|env:slug>`, `--locale <locale>` (as named on disk), `--namespace <ns>`, `--only-approved`
|
|
135
|
+
- `publish`: `--fail-on-withheld`
|
|
137
136
|
- `--json` on `push`, `pull`, `status` and `publish`: machine-readable output on stdout
|
|
138
137
|
- `--debug` (global): print error details and stack traces
|
|
139
138
|
|
|
@@ -148,6 +147,15 @@ Options:
|
|
|
148
147
|
- `--yes` skips the confirmation prompt for the two destructive flags. Without a terminal and without `--yes`, a destructive push is refused and nothing changes.
|
|
149
148
|
- `--dry-run` shows the effect of any combination and writes nothing.
|
|
150
149
|
|
|
150
|
+
## Review
|
|
151
|
+
|
|
152
|
+
When the project has **Require review** turned on in Translify:
|
|
153
|
+
|
|
154
|
+
- `push` lands target-locale translations it writes as **needs review**. `--status draft` or `--status needs-review` picks the status explicitly, and `--status approved` approves them as they land. The source file is always approved. Each locale row shows the status its cells landed in, and the output counts approved translations that went back to review because this push changed their source text.
|
|
155
|
+
- Without review, `push` approves everything it writes, and `--status draft` / `--status needs-review` are refused (`REVIEW_DISABLED`).
|
|
156
|
+
- `publish` to an environment with **Publish approved text only** ships the last approved text of each translation. The output adds how many translations were **carried over** (the approved text went out instead of a newer, unapproved edit) and how many were **withheld** (never approved, so left out). Publishing still succeeds; `--fail-on-withheld` exits 5 when anything was withheld, for pipelines that must ship everything reviewed.
|
|
157
|
+
- `pull --only-approved` writes the approved text from the working copy and leaves out translations that were never approved, for building a release bundle without publishing. `status --only-approved` compares against the same. It works with `--from working` only: environments already apply the gate.
|
|
158
|
+
|
|
151
159
|
## CI (GitHub Actions)
|
|
152
160
|
|
|
153
161
|
Store the key in the repository secrets as `TRANSLIFY_SECRET_KEY`.
|
|
@@ -166,7 +174,7 @@ jobs:
|
|
|
166
174
|
- uses: actions/checkout@v4
|
|
167
175
|
- uses: actions/setup-node@v4
|
|
168
176
|
with: { node-version: 22 }
|
|
169
|
-
- run: npx
|
|
177
|
+
- run: npx translify push
|
|
170
178
|
env:
|
|
171
179
|
TRANSLIFY_SECRET_KEY: ${{ secrets.TRANSLIFY_SECRET_KEY }}
|
|
172
180
|
```
|
|
@@ -183,7 +191,7 @@ jobs:
|
|
|
183
191
|
- uses: actions/checkout@v4
|
|
184
192
|
- uses: actions/setup-node@v4
|
|
185
193
|
with: { node-version: 22 }
|
|
186
|
-
- run: npx
|
|
194
|
+
- run: npx translify status
|
|
187
195
|
env:
|
|
188
196
|
TRANSLIFY_SECRET_KEY: ${{ secrets.TRANSLIFY_SECRET_KEY }}
|
|
189
197
|
```
|
|
@@ -191,10 +199,10 @@ jobs:
|
|
|
191
199
|
Pull and publish on deploy:
|
|
192
200
|
|
|
193
201
|
```yaml
|
|
194
|
-
- run: npx
|
|
202
|
+
- run: npx translify pull
|
|
195
203
|
env:
|
|
196
204
|
TRANSLIFY_SECRET_KEY: ${{ secrets.TRANSLIFY_SECRET_KEY }}
|
|
197
|
-
- run: npx
|
|
205
|
+
- run: npx translify publish production
|
|
198
206
|
env:
|
|
199
207
|
TRANSLIFY_SECRET_KEY: ${{ secrets.TRANSLIFY_SECRET_KEY }}
|
|
200
208
|
```
|
|
@@ -208,6 +216,7 @@ Pull and publish on deploy:
|
|
|
208
216
|
| 2 | Key problem: invalid, revoked, wrong kind of key, or a missing scope |
|
|
209
217
|
| 3 | Plan quota exceeded |
|
|
210
218
|
| 4 | Network failure, server error, push too large, CLI too old, transaction conflict, or rate limited after 3 retries |
|
|
219
|
+
| 5 | `publish --fail-on-withheld`: the publish went through, but the review gate withheld some translations |
|
|
211
220
|
|
|
212
221
|
The server's error `code` decides the exit code first; the HTTP status is only used for codes the CLI does not know.
|
|
213
222
|
|
|
@@ -230,8 +239,10 @@ The server's error `code` decides the exit code first; the HTTP status is only u
|
|
|
230
239
|
| `SCOPE_MISSING` | 2 | The key lacks the scope named in the message (`PULL`, `PUSH` or `PUBLISH`). Create a key that has it. |
|
|
231
240
|
| `QUOTA_EXCEEDED` | 3 | Your plan limit was reached (usually translation keys). Remove keys, or push a smaller set. |
|
|
232
241
|
| `PUSH_TOO_LARGE` | 4 | Split the push by namespace: `translify push --namespace <ns>`. |
|
|
233
|
-
| `CLI_TOO_OLD` | 4 | Upgrade: `npm i -g @chesterbrains/translify-cli@latest` (or use `npx
|
|
242
|
+
| `CLI_TOO_OLD` | 4 | Upgrade: `npm i -g @chesterbrains/translify-cli@latest` (or use `npx translify@latest`). |
|
|
234
243
|
| `ORPHANS_CHANGED` | 1 | The orphan list changed between the dry run and the real push (keys were added, removed or published). Nothing was written; run `translify push --prune` again to review the new list. |
|
|
244
|
+
| `REVIEW_DISABLED` | 1 | `push --status draft` or `--status needs-review` on a project without review. Turn on **Require review** in the project settings, or drop `--status`. Nothing was written. |
|
|
245
|
+
| `APPROVED_ONLY_REQUIRES_WORKING` | 1 | `--only-approved` was combined with `--from env:<slug>`. Pull an environment without it; it already holds only what its gate allowed. |
|
|
235
246
|
| `TRANSACTION_CONFLICT` | 4 | Another write collided with yours. Retry. |
|
|
236
247
|
| `RATE_LIMITED` | 4 | The CLI already retried 3 times, honouring `Retry-After`. Wait a moment and retry. |
|
|
237
248
|
| `INTERNAL_ERROR` / `DATABASE_ERROR` / 5xx | 4 | A server problem. Retry shortly; if it persists, re-run with `--debug` and report it. |
|
|
@@ -241,3 +252,13 @@ The server's error `code` decides the exit code first; the HTTP status is only u
|
|
|
241
252
|
| `@@locale` does not match | 1 | `@@locale` is optional, but if an `.arb` file has it, it must match the file's locale (case and `_`/`-` are ignored). Fix or remove it and push again. |
|
|
242
253
|
| `No translify.json here` | 1 | Run `translify init` in the repository root. |
|
|
243
254
|
| `status` reports drift | 1 | Run `translify pull` (or `push`) to bring the two sides back in line. |
|
|
255
|
+
|
|
256
|
+
## Releasing the `translify` alias
|
|
257
|
+
|
|
258
|
+
The unscoped `translify` package lives in `alias/`. Its `bin.js` just imports `@chesterbrains/translify-cli`, which it depends on at `>=0.1.0 <1.0.0`, so it follows every 0.x release of the CLI without a republish. It only needs republishing (with a bumped range) for a 1.x major:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
cd alias && npm publish --access public # then approve the staged release
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The alias is not part of the CLI's npm tarball and is ignored by lint, typecheck and tests.
|
package/dist/commands/publish.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { EXIT } from '../errors.js';
|
|
2
2
|
export async function runPublish(environment, deps) {
|
|
3
3
|
const res = await deps.api.post('/cli/v1/publish', { environment });
|
|
4
|
+
const carriedOverCount = res.carriedOverCount ?? 0;
|
|
5
|
+
const withheldCount = res.withheldCount ?? 0;
|
|
6
|
+
// An ungated environment reports 0 / 0; keep its output as it was before review state.
|
|
7
|
+
const gate = carriedOverCount > 0 || withheldCount > 0 ? ` ${carriedOverCount} carried over, ${withheldCount} withheld.` : '';
|
|
4
8
|
deps.out(deps.json === true
|
|
5
|
-
? JSON.stringify({
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
? JSON.stringify({
|
|
10
|
+
response: res,
|
|
11
|
+
summary: { environment, publishedCount: res.publishedCount, carriedOverCount, withheldCount },
|
|
12
|
+
})
|
|
13
|
+
: `Published ${res.publishedCount} translation(s) to ${environment}.${gate}`);
|
|
14
|
+
return deps.failOnWithheld === true && withheldCount > 0 ? EXIT.withheld : EXIT.ok;
|
|
8
15
|
}
|
package/dist/commands/pull.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
3
|
-
import { CliError, EXIT } from '../errors.js';
|
|
3
|
+
import { CliError, EXIT, ONLY_APPROVED_NEEDS_WORKING } from '../errors.js';
|
|
4
4
|
import { pathFor, toFsLocale, toTranslifyLocale } from '../patterns.js';
|
|
5
5
|
const readOrUndefined = async (path) => readFile(path, 'utf8').catch(() => undefined);
|
|
6
6
|
const NESTED_CONFLICT = /nested JSON cannot hold both/;
|
|
@@ -28,6 +28,10 @@ const withDiskLocale = (content, translifyLocale, config) => {
|
|
|
28
28
|
/** A Windows checkout (autocrlf, BOM) must not count as drift. */
|
|
29
29
|
const normalizeEol = (text) => text.replace(/^\uFEFF/, '').replace(/\r\n/g, '\n');
|
|
30
30
|
export async function runPull(opts, deps) {
|
|
31
|
+
// Environments already apply the gate; the server would 400 this, fail before any request.
|
|
32
|
+
if (opts.onlyApproved && opts.from !== undefined && opts.from !== 'working') {
|
|
33
|
+
throw new CliError(EXIT.failed, ONLY_APPROVED_NEEDS_WORKING);
|
|
34
|
+
}
|
|
31
35
|
const changed = [];
|
|
32
36
|
const created = [];
|
|
33
37
|
const responses = [];
|
|
@@ -49,6 +53,8 @@ export async function runPull(opts, deps) {
|
|
|
49
53
|
query.jsonStyle = rule.jsonStyle;
|
|
50
54
|
if (opts.from !== undefined)
|
|
51
55
|
query.from = opts.from;
|
|
56
|
+
if (opts.onlyApproved)
|
|
57
|
+
query.approvedOnly = 'true';
|
|
52
58
|
if (opts.locale !== undefined)
|
|
53
59
|
query.locales = toTranslifyLocale(opts.locale, deps.config);
|
|
54
60
|
const namespace = rule.namespace ?? opts.namespace;
|
package/dist/commands/push.js
CHANGED
|
@@ -3,6 +3,12 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { CliError, EXIT } from '../errors.js';
|
|
4
4
|
import { findFiles } from '../patterns.js';
|
|
5
5
|
import { renderPush, summarizePush } from '../render.js';
|
|
6
|
+
export const PUSH_STATUSES = ['draft', 'needs-review', 'approved'];
|
|
7
|
+
const WIRE_STATUS = {
|
|
8
|
+
draft: 'DRAFT',
|
|
9
|
+
'needs-review': 'NEEDS_REVIEW',
|
|
10
|
+
approved: 'APPROVED',
|
|
11
|
+
};
|
|
6
12
|
const PUSH = '/cli/v1/push';
|
|
7
13
|
const MAX_FILES = 500;
|
|
8
14
|
/** One form field per file (`file0`, `file1`, …): the server matches by field, never by filename. */
|
|
@@ -20,6 +26,9 @@ const buildForm = (uploads, opts, dryRun, prune, pruneDigest) => {
|
|
|
20
26
|
form.set('prune', String(prune));
|
|
21
27
|
if (pruneDigest !== undefined)
|
|
22
28
|
form.set('pruneDigest', pruneDigest);
|
|
29
|
+
// No default on the wire: the server resolves an absent status from the project's review setting.
|
|
30
|
+
if (opts.status !== undefined)
|
|
31
|
+
form.set('status', WIRE_STATUS[opts.status]);
|
|
23
32
|
for (const { field, match, bytes } of uploads)
|
|
24
33
|
form.append(field, new File([bytes], match.path));
|
|
25
34
|
return form;
|
package/dist/errors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Minimal for Task 6.2; Task 6.3 owns this file and extends it.
|
|
2
|
-
export const EXIT = { ok: 0, failed: 1, auth: 2, quota: 3, network: 4 };
|
|
2
|
+
export const EXIT = { ok: 0, failed: 1, auth: 2, quota: 3, network: 4, withheld: 5 };
|
|
3
3
|
/** Every expected failure. `index.ts` prints `message` and exits with `exitCode`; anything else is a bug. */
|
|
4
4
|
export class CliError extends Error {
|
|
5
5
|
exitCode;
|
|
@@ -10,3 +10,5 @@ export class CliError extends Error {
|
|
|
10
10
|
this.details = details;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
+
/** Shared by the local check in `pull` and the server's `APPROVED_ONLY_REQUIRES_WORKING`. */
|
|
14
|
+
export const ONLY_APPROVED_NEEDS_WORKING = '--only-approved works with --from working only; environments already apply the review gate.';
|
package/dist/http.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliError, EXIT } from './errors.js';
|
|
1
|
+
import { CliError, EXIT, ONLY_APPROVED_NEEDS_WORKING } from './errors.js';
|
|
2
2
|
import { USER_AGENT } from './version.js';
|
|
3
3
|
const NOT_JSON = Symbol('notJson');
|
|
4
4
|
const MAX_RETRIES = 3;
|
|
@@ -30,6 +30,8 @@ const CODE_EXIT = {
|
|
|
30
30
|
ORPHANS_CHANGED: EXIT.failed,
|
|
31
31
|
TRANSACTION_CONFLICT: EXIT.network,
|
|
32
32
|
RATE_LIMITED: EXIT.network,
|
|
33
|
+
REVIEW_DISABLED: EXIT.failed,
|
|
34
|
+
APPROVED_ONLY_REQUIRES_WORKING: EXIT.failed,
|
|
33
35
|
};
|
|
34
36
|
const exitFor = (status, code) => {
|
|
35
37
|
const byCode = code === undefined ? undefined : CODE_EXIT[code];
|
|
@@ -82,6 +84,10 @@ const describeBody = (status, body) => {
|
|
|
82
84
|
return `Unknown namespace(s): ${list(body.namespaces)}.`;
|
|
83
85
|
case 'ORPHANS_CHANGED':
|
|
84
86
|
return 'The orphans changed since the dry run (keys were added, removed or published meanwhile). Nothing was written. Run translify push --prune again to review the new list.';
|
|
87
|
+
case 'REVIEW_DISABLED':
|
|
88
|
+
return 'Review is off for this project, so --status draft or needs-review cannot be used. Turn on "Require review" in the project settings, or drop --status.';
|
|
89
|
+
case 'APPROVED_ONLY_REQUIRES_WORKING':
|
|
90
|
+
return ONLY_APPROVED_NEEDS_WORKING;
|
|
85
91
|
case 'TRANSACTION_CONFLICT':
|
|
86
92
|
return 'The server hit a transaction conflict. Try again.';
|
|
87
93
|
case 'RATE_LIMITED':
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { confirm, input, password, select } from '@inquirer/prompts';
|
|
3
|
-
import { Command } from 'commander';
|
|
3
|
+
import { Command, Option } from 'commander';
|
|
4
4
|
import { runInit } from './commands/init.js';
|
|
5
5
|
import { runLogin } from './commands/login.js';
|
|
6
6
|
import { runPublish } from './commands/publish.js';
|
|
7
7
|
import { runPull } from './commands/pull.js';
|
|
8
|
-
import { runPush } from './commands/push.js';
|
|
8
|
+
import { PUSH_STATUSES, runPush } from './commands/push.js';
|
|
9
9
|
import { loadConfig } from './config.js';
|
|
10
10
|
import { askConfirm } from './confirm.js';
|
|
11
11
|
import { resolveKey } from './credentials.js';
|
|
@@ -90,6 +90,7 @@ program
|
|
|
90
90
|
.option('--prune', 'delete keys missing from your source files; published keys are kept; deletes exactly the keys you confirmed, and refuses if anything changed meanwhile (re-run to review)')
|
|
91
91
|
.option('-y, --yes', 'confirm destructive flags without prompting')
|
|
92
92
|
.option('--namespace <ns>', 'only this namespace')
|
|
93
|
+
.addOption(new Option('--status <status>', 'status target-locale cells land in (default: needs-review when the project requires review, approved otherwise)').choices(PUSH_STATUSES))
|
|
93
94
|
.option('--json', 'machine-readable output')
|
|
94
95
|
.action(async (opts) => {
|
|
95
96
|
process.exitCode = await runPush(opts, {
|
|
@@ -103,9 +104,15 @@ program
|
|
|
103
104
|
program
|
|
104
105
|
.command('publish <environment>')
|
|
105
106
|
.description('Publish an environment')
|
|
107
|
+
.option('--fail-on-withheld', 'exit 5 when the review gate withheld any translation (the publish still happens)')
|
|
106
108
|
.option('--json', 'machine-readable output')
|
|
107
109
|
.action(async (environment, opts) => {
|
|
108
|
-
process.exitCode = await runPublish(environment, {
|
|
110
|
+
process.exitCode = await runPublish(environment, {
|
|
111
|
+
api: (await withApi()).api,
|
|
112
|
+
out,
|
|
113
|
+
json: opts.json,
|
|
114
|
+
failOnWithheld: opts.failOnWithheld,
|
|
115
|
+
});
|
|
109
116
|
});
|
|
110
117
|
program
|
|
111
118
|
.command('pull')
|
|
@@ -113,6 +120,7 @@ program
|
|
|
113
120
|
.option('--from <source>', 'working (default) or env:<slug>')
|
|
114
121
|
.option('--locale <locale>', 'only this locale (as named on disk)')
|
|
115
122
|
.option('--namespace <ns>', 'only this namespace')
|
|
123
|
+
.option('--only-approved', 'approved text only; translations never approved are left out (working copy only)')
|
|
116
124
|
.option('--json', 'machine-readable output')
|
|
117
125
|
.action(async (opts) => {
|
|
118
126
|
process.exitCode = await runPull(opts, { ...(await withApi()), out });
|
|
@@ -123,6 +131,7 @@ program
|
|
|
123
131
|
.option('--from <source>', 'working (default) or env:<slug>')
|
|
124
132
|
.option('--locale <locale>', 'only this locale (as named on disk)')
|
|
125
133
|
.option('--namespace <ns>', 'only this namespace')
|
|
134
|
+
.option('--only-approved', 'approved text only; translations never approved are left out (working copy only)')
|
|
126
135
|
.option('--json', 'machine-readable output')
|
|
127
136
|
.action(async (opts) => {
|
|
128
137
|
process.exitCode = await runPull({ ...opts, check: true }, { ...(await withApi()), out });
|
package/dist/render.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
2
|
const MAX_ORPHANS = 20;
|
|
3
3
|
const MAX_UNKNOWN = 5;
|
|
4
|
+
/** APPROVED gets no label: it is all a project without review ever sees. */
|
|
5
|
+
const STATUS_LABEL = { DRAFT: 'draft', NEEDS_REVIEW: 'needs review' };
|
|
4
6
|
/** Totals across locales, for `--json`. */
|
|
5
7
|
export function summarizePush(res) {
|
|
6
8
|
const summary = {
|
|
@@ -13,6 +15,7 @@ export function summarizePush(res) {
|
|
|
13
15
|
orphans: res.orphans.length,
|
|
14
16
|
publishedOrphans: res.orphans.filter((orphan) => orphan.published).length,
|
|
15
17
|
pruned: res.pruned,
|
|
18
|
+
staleReset: res.staleReset ?? 0,
|
|
16
19
|
};
|
|
17
20
|
for (const counts of Object.values(res.locales)) {
|
|
18
21
|
summary.created += counts.created;
|
|
@@ -30,12 +33,19 @@ export function renderPush(res, view = {}) {
|
|
|
30
33
|
pc.dim(`${c.unchanged} unchanged`);
|
|
31
34
|
if (c.skippedFilled > 0)
|
|
32
35
|
line += pc.dim(`, ${c.skippedFilled} kept (already translated; --overwrite-targets replaces them)`);
|
|
36
|
+
const label = c.status === undefined ? undefined : STATUS_LABEL[c.status];
|
|
37
|
+
if (label !== undefined && c.created + c.updated > 0)
|
|
38
|
+
line += pc.magenta(` · ${label}`);
|
|
33
39
|
lines.push(line);
|
|
34
40
|
if (c.unknownKeys.length > 0) {
|
|
35
41
|
const more = c.unknownKeys.length > MAX_UNKNOWN ? ', …' : '';
|
|
36
42
|
lines.push(pc.yellow(` ${c.unknownKeys.length} unknown key(s) skipped: ${c.unknownKeys.slice(0, MAX_UNKNOWN).join(', ')}${more}`));
|
|
37
43
|
}
|
|
38
44
|
}
|
|
45
|
+
const stale = res.staleReset ?? 0;
|
|
46
|
+
if (stale > 0) {
|
|
47
|
+
lines.push(pc.yellow(`${stale} approved translation(s) ${res.dryRun ? 'would go' : 'went'} back to review because their source text changed.`));
|
|
48
|
+
}
|
|
39
49
|
// `pruned` is what a dry run would delete or a real push did delete: unpublished orphans only.
|
|
40
50
|
const unpublished = res.orphans.filter((orphan) => !orphan.published);
|
|
41
51
|
const published = res.orphans.filter((orphan) => orphan.published);
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chesterbrains/translify-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Push, pull and publish Translify translations from your repository",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/chesterbrains/translify-cli.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/chesterbrains/translify-cli#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/chesterbrains/translify-cli/issues"
|
|
13
|
+
},
|
|
6
14
|
"type": "module",
|
|
7
15
|
"bin": {
|
|
8
16
|
"translify": "dist/index.js"
|