@ansstory/hias 1.0.6 → 1.0.7

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.
@@ -0,0 +1,551 @@
1
+ # hias-cli
2
+
3
+ > Personal scaffolding CLI with built-in i18n translation toolkit.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm i @ansstory/hias -g
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```
14
+ hias <command> [options]
15
+ ```
16
+
17
+ ---
18
+
19
+ ## Quick Start (i18n Translation)
20
+
21
+ ### 1. Set up API credentials
22
+
23
+ Register a translation provider and create a config file in your project:
24
+
25
+ ```sh
26
+ hias setting
27
+ ```
28
+
29
+ Edit `.hias/setting.json` with your `appId` and `secretKey`:
30
+
31
+ ```json
32
+ {
33
+ "translationSetting": {
34
+ "appId": "your_app_id",
35
+ "secretKey": "your_secret_key",
36
+ "provider": "baidu"
37
+ }
38
+ }
39
+ ```
40
+
41
+ > **No API key?** Leave both empty for fallback mode — Chinese text is used as keys directly. The i18n migration still completes.
42
+
43
+ ### 2. Preview changes (recommended)
44
+
45
+ ```sh
46
+ hias tfo src/views myNamespace --dry-run
47
+ ```
48
+
49
+ Shows a line-level diff and prompts `Apply changes? (y/N)`.
50
+
51
+ ### 3. Apply translation
52
+
53
+ ```sh
54
+ hias tfo src/views myNamespace
55
+ ```
56
+
57
+ Locale files are written to `.hias/lang/myNamespace/`. If `replaceOriginalFile` is `true`, source files are overwritten in place.
58
+
59
+ ---
60
+
61
+ ## Commands
62
+
63
+ ### `create <project>` (alias: `crt`)
64
+
65
+ Create a project from a framework template.
66
+
67
+ An interactive prompt will list all available frameworks. After selection, the template is downloaded from Gitee and placed into `<project>/`.
68
+
69
+ ```sh
70
+ hias create my-app
71
+ ```
72
+
73
+ **Available frameworks:**
74
+
75
+ | Name | Description |
76
+ | ------------ | --------------------------- |
77
+ | `vue2` | Vue 2 template |
78
+ | `vue3` | Vue 3 template |
79
+ | `vue-ts` | Vue 3 + TypeScript template |
80
+ | `uni-vite` | uni-app + Vite template |
81
+ | `nuxt-web` | Nuxt.js web template |
82
+ | `vue-screen` | Vue data screen template |
83
+ | `react` | React JSX template |
84
+ | `react-ts` | React TypeScript template |
85
+
86
+ ---
87
+
88
+ ### `adv <name> [options]`
89
+
90
+ Add a Vue component file.
91
+
92
+ ```sh
93
+ hias adv Demo -d src/components
94
+ hias adv index -d src/views/demo
95
+ ```
96
+
97
+ ### `adr <name> [options]`
98
+
99
+ Add a React JSX component file.
100
+
101
+ ```sh
102
+ hias adr Demo -d src/components
103
+ ```
104
+
105
+ ### `adrt <name> [options]`
106
+
107
+ Add a React TSX component file.
108
+
109
+ ```sh
110
+ hias adrt Demo -d src/components
111
+ ```
112
+
113
+ ### `adrd <name> [options]`
114
+
115
+ Add a Redux JSX store file.
116
+
117
+ ```sh
118
+ hias adrd useDemoStore -d src/store/modules
119
+ ```
120
+
121
+ ### `adrdt <name> [options]`
122
+
123
+ Add a Redux TSX store file.
124
+
125
+ ```sh
126
+ hias adrdt useDemoStore -d src/store/modules
127
+ ```
128
+
129
+ #### Common options for component commands
130
+
131
+ | Option | Description |
132
+ | ------------------- | ----------------------------------------------------------------------------- |
133
+ | `-d, --dest <path>` | Target folder. Defaults to `src/components`. Accepts Windows backslash paths. |
134
+
135
+ If `name` is `index`, the component file is named after the parent folder instead.
136
+
137
+ ---
138
+
139
+ ### `close-port <ports...>`
140
+
141
+ Kill processes occupying one or more ports.
142
+
143
+ ```sh
144
+ hias close-port 8076 8077
145
+ hias close-port 8076,8077
146
+ hias close-port 3000
147
+ ```
148
+
149
+ Supports both space-separated and comma-separated port lists. Works on Windows (`netstat` + `taskkill`), macOS, and Linux (`lsof` + `kill`).
150
+
151
+ ---
152
+
153
+ ### `lang [language]`
154
+
155
+ Set or view the CLI language.
156
+
157
+ ```sh
158
+ hias lang zh-CN
159
+ hias lang en
160
+ hias lang -l # show current language
161
+ ```
162
+
163
+ | Option | Description |
164
+ | ------------ | ------------------------------------- |
165
+ | `-l, --list` | Display the currently active language |
166
+
167
+ Supported languages: `en`, `zh-CN`.
168
+
169
+ Language preference is stored in `~/.hias-cli/config.json`.
170
+
171
+ ---
172
+
173
+ ### Translation Commands
174
+
175
+ The translation system extracts Chinese text from source files, replaces it with `$t()` i18n calls, and generates locale JSON files. Supports Baidu Translate API and Tencent Cloud TMT API.
176
+
177
+ #### `tf [file] [name]`
178
+
179
+ Translate a single file.
180
+
181
+ ```sh
182
+ hias tf src/views/index.vue vue
183
+ hias tf src/views/index.vue vue --verbose
184
+ ```
185
+
186
+ | Argument / Option | Description |
187
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------- |
188
+ | `file` (positional) | Path to the file to translate |
189
+ | `name` (positional) | i18n namespace key (defaults to parent directory name) |
190
+ | `-n, --name <name>` | Alternative way to specify the namespace |
191
+ | `--dry-run` | Preview changes without modifying files (shows line-level diff, prompts to apply) |
192
+ | `-v, --verbose` | Show detailed per-file logs instead of a progress spinner |
193
+ | `--show-extractions` | Only display all extracted Chinese texts with file paths and positions, without performing translation or making API calls |
194
+
195
+ **Supported file types:** `.vue`, `.js`, `.ts`, `.jsx`, `.tsx`, `.json`
196
+
197
+ #### `tfo [folder] [name]`
198
+
199
+ Recursively translate all supported files in a folder.
200
+
201
+ ```sh
202
+ hias tfo src/views views
203
+ hias tfo src/views views --verbose
204
+ ```
205
+
206
+ | Argument / Option | Description |
207
+ | --------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
208
+ | `folder` (positional) | Path to the folder |
209
+ | `name` (positional) | i18n namespace key (defaults to folder name) |
210
+ | `-n, --name <name>` | Alternative way to specify the namespace |
211
+ | `--dry-run` | Preview changes without modifying files (shows line-level diff, prompts to apply) |
212
+ | `-v, --verbose` | Show detailed per-file logs instead of a progress spinner |
213
+ | `--exclude <pattern>` | Exclude files matching a glob pattern (e.g. `**/*.test.js`, `node_modules/**`). Uses minimatch. Can be specified multiple times. |
214
+ | `--show-extractions` | Only display all extracted Chinese texts with file paths and positions, without performing translation or making API calls |
215
+
216
+ #### `setting`
217
+
218
+ Generate a project-level translation config file.
219
+
220
+ ```sh
221
+ hias setting
222
+ ```
223
+
224
+ Creates `.hias/setting.json` in the current project directory.
225
+
226
+ #### `global-setting`
227
+
228
+ Generate a global translation config file.
229
+
230
+ ```sh
231
+ hias global-setting
232
+ ```
233
+
234
+ Creates or updates `~/.hias-cli/config.json`. Preserves any existing `language` setting.
235
+
236
+ #### `clear-config`
237
+
238
+ Remove the global config directory `~/.hias-cli`.
239
+
240
+ ```sh
241
+ hias clear-config
242
+ ```
243
+
244
+ To fully uninstall the CLI and remove user config, run:
245
+
246
+ ```sh
247
+ hias uninstall -g
248
+ ```
249
+
250
+ This command first runs `hias clear-config`, then runs `npm uninstall @ansstory/hias -g`. npm v7+ no longer runs the old `preuninstall` / `postuninstall` lifecycle scripts during package removal, so `npm uninstall` alone will not delete config files in the home directory.
251
+
252
+ #### `gitignore`
253
+
254
+ Add `.hias` to the project's `.gitignore` file. Creates `.gitignore` if it doesn't exist, or appends the entry if it already exists.
255
+
256
+ ```sh
257
+ hias gitignore
258
+ ```
259
+
260
+ After running, the `.gitignore` file will contain an entry for `.hias`.
261
+
262
+ ---
263
+
264
+ #### `rollback`
265
+
266
+ Restore files from the last translation operation and clean up generated output.
267
+
268
+ Each translation run creates a snapshot in `.hias/.langbackup/<timestamp>_<name>/`. Running `rollback` restores the most recent snapshot and removes it, preserving older snapshots for further step-by-step rollback.
269
+
270
+ ```sh
271
+ hias rollback
272
+ hias rollback --list # list available backups
273
+ hias rollback --name 20250101_120000_vue # rollback a specific backup
274
+ hias rollback --keep 3 # keep only the 3 most recent backups, remove older ones
275
+ ```
276
+
277
+ **Behavior:** When `replaceOriginalFile` was `true`, original source files are restored; when it was `false`, only the output directory `.hias/lang/<name>/` is removed.
278
+
279
+ **`--keep` behavior:** Backups are sorted by directory name (timestamp), the oldest ones are removed until only N remain. No action if total backups ≤ N.
280
+
281
+ | Option | Description |
282
+ | ---------------- | ------------------------------------------------------ |
283
+ | `-l, --list` | List all available backups |
284
+ | `--name <name>` | Rollback a specific backup by name |
285
+ | `--keep <count>` | Keep only the N most recent backups, remove older ones |
286
+
287
+ ---
288
+
289
+ ## Configuration
290
+
291
+ ### Config Cascade
292
+
293
+ Translation settings are resolved in the following priority order:
294
+
295
+ 1. **Project-level** — `.hias/setting.json` in the current working directory
296
+ 2. **Global** — `~/.hias-cli/config.json` (created by `hias global-setting`)
297
+ 3. **Defaults** — hardcoded defaults (shown below)
298
+
299
+ ### Setting File Format
300
+
301
+ ```json
302
+ {
303
+ "translationSetting": {
304
+ "locales": ["zh-CN", "en-US"],
305
+ "outDir": ".hias/lang",
306
+ "fallbackToKey": true,
307
+ "replaceOriginalFile": false,
308
+ "provider": "tencent",
309
+ "_provider_hint": "'baidu','tencent'",
310
+ "i18nCallTemplate": "$t",
311
+ "extensions": [".vue", ".js", ".ts", ".jsx", ".tsx", ".json"],
312
+ "appId": "",
313
+ "secretKey": ""
314
+ }
315
+ }
316
+ ```
317
+
318
+ ### Configuration Fields
319
+
320
+ | Field | Type | Default | Description |
321
+ | --------------------- | ---------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
322
+ | `locales` | `string[]` | `["zh-CN", "en-US"]` | Locale code list. **First entry is the source language**, the rest are target languages. E.g. `["zh-CN", "en-US", "ja-JP", "ko-KR"]` generates English, Japanese, and Korean locale files in one pass |
323
+ | `outDir` | `string` | `".hias/lang"` | Output directory for generated locale files and translated copies |
324
+ | `fallbackToKey` | `boolean` | `true` | Whether to generate keys when translation API is unavailable |
325
+ | `replaceOriginalFile` | `boolean` | `false` | When `true`, overwrites source files in place (no copy to `outDir`) |
326
+ | `provider` | `string` | `"tencent"` | Translation provider: `"baidu"` or `"tencent"` |
327
+ | `i18nCallTemplate` | `string` | `"$t"` | i18n function name. When containing `{{key}}`, it's replaced with `namespace.subKey`; otherwise the function wraps it as `$t('key')` |
328
+ | `extensions` | `string[]` | — | Override supported file extensions (e.g. `[".vue", ".ts"]`). Defaults to all supported types |
329
+ | `appId` | `string` | `""` | Baidu AppId or Tencent SecretId |
330
+ | `secretKey` | `string` | `""` | Baidu SecretKey or Tencent SecretKey |
331
+
332
+ ### Translation Providers
333
+
334
+ | Provider | `provider` value | `appId` | `secretKey` |
335
+ | ----------------- | ---------------- | ---------------- | ----------------- |
336
+ | Baidu Translate | `"baidu"` | Baidu AppId | Baidu SecretKey |
337
+ | Tencent Cloud TMT | `"tencent"` | Tencent SecretId | Tencent SecretKey |
338
+
339
+ Leave both `appId` and `secretKey` empty to use fallback mode (Chinese text is used as-is for both key and value in locale files).
340
+
341
+ > **Config validation:** Settings are automatically validated when loaded (e.g., `locales` must be an array, `provider` must be a supported enum value, `appId`/`secretKey` must be strings). Invalid values trigger a warning and are reset to defaults without interrupting the workflow.
342
+
343
+ ### API Provider Setup
344
+
345
+ #### Baidu Translate
346
+
347
+ 1. Visit [Baidu Translate Open Platform](https://fanyi-api.baidu.com/) and register
348
+ 2. Create an application to get **AppId** and **SecretKey**
349
+ 3. Set `"provider": "baidu"` in config
350
+
351
+ #### Tencent Cloud TMT
352
+
353
+ 1. Visit [Tencent Cloud TMT](https://console.cloud.tencent.com/tmt) and enable the service
354
+ 2. Create an API key to get **SecretId** (→ `appId`) and **SecretKey**
355
+ 3. Set `"provider": "tencent"` in config
356
+
357
+ ---
358
+
359
+ ## Output Structure
360
+
361
+ After translation, the following directory structure is created (assuming `locales: ["zh-CN", "en-US", "ja-JP"]`):
362
+
363
+ ```
364
+ .hias/
365
+ ├── setting.json # project-level config
366
+ ├── lang/
367
+ │ └── <namespace>/
368
+ │ ├── zh-CN.json # source locale
369
+ │ ├── en-US.json # target locale ①
370
+ │ ├── ja-JP.json # target locale ②
371
+ │ └── <file>.vue # translated copy (only if replaceOriginalFile=false)
372
+ ├── .translation-cache.json # translation cache (auto-generated)
373
+ └── .langbackup/
374
+ └── <timestamp>_<namespace>/
375
+ └── <file>.vue.bak # original file backup
376
+ ```
377
+
378
+ Locale files use a nested `{ namespace: { key: value } }` format, making them ready for use with `vue-i18n` or similar libraries via module registration.
379
+
380
+ ---
381
+
382
+ ## Translation Behavior
383
+
384
+ ### Source Code Extraction
385
+
386
+ The tool scans source files and extracts Chinese text from:
387
+
388
+ - **Quoted strings**: `'中文'`, `"中文"`, `` `中文{expr}文` `` (template literals with expressions are split, only static Chinese segments extracted)
389
+ - **Vue templates**: `{{ '中文' }}`, `<div>中文</div>`, `:title="'中文'"`
390
+ - **Vue complex expressions**: `{{ isHidden || '中文' }}`, `{{ isHidden ?? '中文' }}` (extracts inline quoted strings from expressions)
391
+ - **Vue directives**: `v-if="text1 == '中文'"`, `v-for="..."` (extracts inline strings from directive expressions)
392
+ - **JSX/TSX text nodes**: `<span>中文 {name}</span>` (mixed text with expressions supported)
393
+ - **JSX/TSX attributes**: `<div title="中文">` or `<div title={'中文'}>`
394
+ - **JSON values**: `"key": "中文值"`
395
+ - **Plain HTML attributes**: `<div title="兴趣">` in Vue templates
396
+
397
+ Comments (`//`, `/* */`, `<!-- -->`) are correctly skipped. `data-*` and `aria-*` attributes are excluded.
398
+
399
+ ### Replacement Pattern
400
+
401
+ | Context | Before | After |
402
+ | -------------------------- | ------------------------- | -------------------------------------------------- |
403
+ | Vue template text node | `<div>兴趣</div>` | `<div>{{ $t('demo.interest') }}</div>` |
404
+ | Vue template interpolation | `{{ '兴趣' }}` | `{{ $t('demo.interest') }}` |
405
+ | Vue complex expression | <code>{{ isHidden &#124;&#124; '兴趣' }}</code> | <code>{{ isHidden &#124;&#124; $t('demo.interest') }}</code> |
406
+ | Vue directive expression | `v-if="text1 == '文本'"` | `v-if="text1 == $t('demo.text')"` |
407
+ | Vue script / JS / TS | `'兴趣'` | `$t('demo.interest')` |
408
+ | JSX text node | `<div>兴趣</div>` | `<div>{$t('demo.interest')}</div>` |
409
+ | JSX attribute | `<div title="兴趣">` | `<div title={$t('demo.interest')}>` |
410
+ | Vue v-bind attribute | `<div :title="'兴趣'">` | `<div :title="$t('demo.interest')">` |
411
+ | Template literal | `` `中文 ${name} 测试` `` | `` `$t('demo.chinese') ${name} $t('demo.test')` `` |
412
+ | JSON value | `"兴趣"` | `"interest"` (replaced with translation directly) |
413
+
414
+ ### Locale File Format
415
+
416
+ **When translation succeeds** (English-based keys, truncated to 40 characters):
417
+
418
+ ```json
419
+ // zh-CN.json
420
+ { "demo": { "interest": "兴趣", "test": "测试" } }
421
+ // en-US.json
422
+ { "demo": { "interest": "interest", "test": "test" } }
423
+ // ja-JP.json (if locales includes ja-JP)
424
+ { "demo": { "interest": "興味", "test": "テスト" } }
425
+ ```
426
+
427
+ **When translation fails** (auto-generated short keys from Chinese, truncated to 40 characters):
428
+
429
+ ```json
430
+ // zh-CN.json and en-US.json
431
+ { "demo": { "提起妈妈的手_一股酸痛便涌上心头": "提起妈妈的手,一股酸痛便涌上心头...", "test": "测试" } }
432
+ ```
433
+
434
+ ### Key Generation Rules
435
+
436
+ | Scenario | Source | Key Generation | Max Length |
437
+ | ------------ | ------------------ | --------------------------------------- | ---------- |
438
+ | API success | Translated English | snake_case of translation | 40 chars |
439
+ | API fallback | Original Chinese | first-line punctuation → `_`, truncated | 40 chars |
440
+
441
+ Keys are deduplicated: if two texts produce the same truncated key, `_1`, `_2`, etc. are appended.
442
+
443
+ ### Translation API Fallback Warning
444
+
445
+ When the translation API call fails (network error, invalid credentials, or rate limit), the tool logs a warning message to the console, e.g.:
446
+
447
+ ```
448
+ ⚠ Translation API failed, falling back to Chinese keys for this batch
449
+ ```
450
+
451
+ It then falls back to Chinese-based auto-generated keys (see [Key Generation Rules](#key-generation-rules) above). This ensures the i18n migration can proceed even without API connectivity.
452
+
453
+ > **Per-text fallback:** If some texts in a batch succeed while others fail, only the failed texts use fallback mode — successful texts still use English keys. This prevents a single failure from degrading the entire batch to Chinese keys.
454
+
455
+ ### Translation Cache
456
+
457
+ Successful API translation results are cached to `.hias/.translation-cache.json`. The next time the same text is encountered (regardless of namespace), it's served from cache, avoiding redundant network requests and API costs.
458
+
459
+ - Cache is keyed by original text with target language as sub-key: `{ "text": { "en": "translated" } }`
460
+ - Corrupted or malformed cache files are silently ignored without affecting the normal flow
461
+ - Writes are deferred (`setImmediate`) to avoid blocking the translation pipeline
462
+
463
+ ### Unused Translation Cleanup
464
+
465
+ After replacing Chinese text with `$t()` calls, the tool checks existing locale JSON files and **removes keys that are no longer referenced anywhere in the source code**. This prevents locale files from accumulating stale entries over time.
466
+
467
+ Cleanup runs against the same namespace used for the current translation operation, and only affects keys that have zero references across all scanned source files.
468
+
469
+ ### Namespace Normalization
470
+
471
+ Existing `$t('oldNamespace.key')` calls in source files are automatically updated to the current namespace name during translation.
472
+
473
+ ### Dry-Run Mode
474
+
475
+ When `--dry-run` is specified, the tool shows a line-level diff of all changes without actually modifying files. After the preview, it prompts `Apply changes? (y/N)` — entering `y` or `yes` executes the changes, anything else cancels.
476
+
477
+ ### `i18nCallTemplate` Customization
478
+
479
+ The `i18nCallTemplate` field controls the i18n function name and call format:
480
+
481
+ | Template Value | Generated Output |
482
+ | ---------------------------- | ------------------------------------------- |
483
+ | `"$t"` (default) | `$t('demo.interest')` |
484
+ | `"$te"` | `$te('demo.interest')` |
485
+ | `"{{key}}"` | `demo.interest` (raw key, no function call) |
486
+ | `"t('{{key}}')"` | `t('demo.interest')` |
487
+ | `"i18n.global.t('{{key}}')"` | `i18n.global.t('demo.interest')` |
488
+
489
+ When the template **contains** `{{key}}`, it's a template string where `{{key}}` is replaced with `namespace.subKey`. When it does **not** contain `{{key}}`, it wraps as `template('namespace.subKey')`.
490
+
491
+ ### Workflow Tips
492
+
493
+ - **Always run `--dry-run` first** when trying translation on a new codebase to verify the extraction results
494
+ - **Use `tfo` with a single file's parent folder** for targeted translation: `hias tfo src/views/MyComponent myComp`
495
+ - **Use `--exclude` to skip config or generated files**: `hias tfo src/views views --exclude "**/*.test.js" --exclude node_modules`
496
+ - **Use separate namespaces per module** to keep locale files small and focused. For example, `src/views/user/` → namespace `user`, `src/views/admin/` → namespace `admin`
497
+ - **Run rollback after verifying** if `replaceOriginalFile` was on: `hias rollback`
498
+ - **Multiple rollbacks**: each `rollback` undoes one snapshot. Run repeatedly to go back further
499
+ - **Re-running translation**: existing `$t()` calls are stripped before re-extraction, so running twice is safe and will only add new translations for any remaining Chinese text
500
+
501
+ ---
502
+
503
+ ## Global Config
504
+
505
+ The global config file is located at `~/.hias-cli/config.json` and is shared by both the language setting and translation settings:
506
+
507
+ ```json
508
+ {
509
+ "language": "zh-CN",
510
+ "translationSetting": {
511
+ "locales": ["zh-CN", "en-US"],
512
+ "outDir": ".hias/lang",
513
+ "fallbackToKey": true,
514
+ "replaceOriginalFile": false,
515
+ "provider": "tencent",
516
+ "_provider_hint": "'baidu','tencent'",
517
+ "i18nCallTemplate": "$t",
518
+ "extensions": [".vue", ".js", ".ts", ".jsx", ".tsx", ".json"],
519
+ "appId": "",
520
+ "secretKey": ""
521
+ }
522
+ }
523
+ ```
524
+
525
+ Managed by:
526
+
527
+ - **`hias lang <language>`** — sets `language`
528
+ - **`hias global-setting`** — creates/updates `translationSetting`
529
+ - **`hias clear-config`** — removes `~/.hias-cli`
530
+ - **`hias uninstall -g`** — runs `hias clear-config`, then uninstalls the global package
531
+
532
+ ---
533
+
534
+ ## All Commands Reference
535
+
536
+ | Command | Alias | Description |
537
+ | ----------------------- | ----- | ----------------------------------------- |
538
+ | `create <project>` | `crt` | Create project from framework template |
539
+ | `adv <name>` | – | Add Vue component |
540
+ | `adr <name>` | – | Add React JSX component |
541
+ | `adrt <name>` | – | Add React TSX component |
542
+ | `adrd <name>` | – | Add Redux JSX store |
543
+ | `adrdt <name>` | – | Add Redux TSX store |
544
+ | `close-port <ports...>` | – | Kill processes on ports |
545
+ | `lang [language]` | – | Set or view CLI language |
546
+ | `tf [file] [name]` | – | Translate a single file |
547
+ | `tfo [folder] [name]` | – | Translate all files in a folder |
548
+ | `setting` | – | Generate project-level translation config |
549
+ | `global-setting` | – | Generate global translation config |
550
+ | `gitignore` | – | Add `.hias` to `.gitignore` |
551
+ | `rollback` | – | Roll back the last translation operation |