formatjs_cli 1.4.2

Command-line interface for FormatJS - A Rust-based CLI for internationalization
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# formatjs_cli

A high-performance Rust-based command-line interface for FormatJS internationalization tools.

## Overview

`formatjs_cli` is a high-performance Rust implementation of core FormatJS CLI workflows, providing fast tools for working with ICU MessageFormat messages in your internationalization workflow.

### Why Use the Native CLI?

The native Rust CLI offers significant advantages over the Node.js-based `@formatjs/cli`:

- **Faster Performance**: 20.90x faster in the checked-in extraction benchmark, with parallel parsing for large compile and verify workloads
- **Zero Node.js Runtime Dependency**: Single binary with no Node.js runtime required for supported features
- **Lower Memory Usage**: Minimal memory footprint compared to Node.js
- **Instant Startup**: No Node.js initialization overhead
- **Easy Distribution**: Standalone binaries for CI/CD pipelines
- **CI/CD Friendly**: Fast, reliable, and cache-friendly

**Benchmark results** (processing 1,000 generated files with 9,406 messages):

- Pure TypeScript Node.js CLI: ~8.5 seconds
- Hybrid Node.js/native CLI: 744.86 ms, 12,628 messages/second
- Rust CLI: 35.65 ms, 263,876 messages/second
- Speedup vs hybrid CLI: 20.90x

**Catalog benchmark results** (20,000 generated messages, Apple Silicon, 2026-05-25; includes process startup and JSON I/O):

| Workflow                       | Before   | After    | Improvement         |
| ------------------------------ | -------- | -------- | ------------------- |
| `compile --ast`                | 180.9 ms | 121.1 ms | 33.1% lower latency |
| `verify --structural-equality` | 373.3 ms | 228.8 ms | 38.7% lower latency |

The native CLI aims to match `@formatjs/cli` for supported workflows. Some Node-specific behavior is intentionally not available in the standalone Rust binary, including loading arbitrary JavaScript formatter files with `--format`.

## Threading

The native CLI parallelizes per-file and per-message work with Rayon. Extraction
uses per-file parallelism, while AST compilation and structural verification can
also parse individual messages in parallel. By default, Rayon uses the number of
available logical CPU cores. Set `RAYON_NUM_THREADS` to cap worker threads in
CPU-constrained environments:

```bash
RAYON_NUM_THREADS=4 formatjs extract "src/**/*.tsx" --out-file messages.json
```

## Features

- **Extract**: Extract messages from JavaScript and TypeScript source files
- **Compile**: Compile messages for production use with optional minification
- **Verify**: Validate message files and check for missing/extra keys
- **Compile-Folder**: Batch compile all translation files in a folder

## Quick Start

```bash
# Install from Cargo
cargo install formatjs_cli

# Extract messages from your source code
formatjs extract "src/**/*.tsx" --out-file messages.json

# Compile translations for production
formatjs compile "translations/*.json" --out-file compiled.json --ast

# Verify translations are complete
formatjs verify "translations/*.json" --source-locale en --missing-keys
```

## Installation

### Cargo

Install the published crate with Cargo:

```bash
cargo install formatjs_cli
```

Cargo installs the command as `formatjs` in `~/.cargo/bin`:

```bash
formatjs --help
formatjs --version
```

If `formatjs` is not found, add Cargo's bin directory to your `PATH`:

```bash
export PATH="$HOME/.cargo/bin:$PATH"
```

For local development from this repository:

```bash
cargo install --path crates/formatjs_cli
formatjs --help
```

You can also run without installing:

```bash
cargo run -p formatjs_cli -- --help
cargo run -p formatjs_cli -- extract "src/**/*.tsx"
```

### Pre-Built Binaries

Download pre-built native binaries from the [GitHub Releases](https://github.com/formatjs/formatjs/releases) page.

**Available binaries:**

- `formatjs_cli-darwin-arm64` - macOS Apple Silicon
- `formatjs_cli-linux-arm64` - Linux ARM64
- `formatjs_cli-linux-x64` - Linux x86_64
- `formatjs_cli-win32-x64.exe` - Windows x64

**Installation steps:**

1. Download the appropriate binary for your platform:

   ```bash
   # macOS Apple Silicon
   curl -L https://github.com/formatjs/formatjs/releases/download/<version>/formatjs_cli-darwin-arm64 -o formatjs

   # Linux x86_64
   curl -L https://github.com/formatjs/formatjs/releases/download/<version>/formatjs_cli-linux-x64 -o formatjs

   # Linux ARM64
   curl -L https://github.com/formatjs/formatjs/releases/download/<version>/formatjs_cli-linux-arm64 -o formatjs

   # Windows x64
   curl.exe -L https://github.com/formatjs/formatjs/releases/download/<version>/formatjs_cli-win32-x64.exe -o formatjs.exe
   ```

2. On macOS or Linux, make it executable:

   ```bash
   chmod +x formatjs
   ```

3. On macOS or Linux, optionally move it to your PATH:

   ```bash
   sudo mv formatjs /usr/local/bin/
   ```

4. Verify installation:
   ```bash
   formatjs --version
   ```

### Using Bazel

Build the CLI using Bazel for host platform:

```bash
bazel build //crates/formatjs_cli:formatjs
```

Run directly with Bazel:

```bash
bazel run //crates/formatjs_cli:formatjs -- --help
```

### Cross-Platform Release Builds

Build the release binaries for Darwin ARM64, Linux x64, Linux ARM64, and Windows x64:

```bash
bazel build --compilation_mode=opt \
  //crates/formatjs_cli:release_binary_darwin_arm64 \
  //crates/formatjs_cli:release_binary_linux_x64 \
  //crates/formatjs_cli:release_binary_linux_arm64 \
  //crates/formatjs_cli:release_binary_windows_x64_gnullvm
```

Use `bazel cquery --output=files <target>` to locate each binary:

```bash
bazel cquery --output=files //crates/formatjs_cli:release_binary_darwin_arm64
bazel cquery --output=files //crates/formatjs_cli:release_binary_linux_x64
bazel cquery --output=files //crates/formatjs_cli:release_binary_linux_arm64
bazel cquery --output=files //crates/formatjs_cli:release_binary_windows_x64_gnullvm
```

### Local Cargo Build

Build and install from the local checkout using Cargo:

```bash
cd crates/formatjs_cli
cargo build --release
cargo install --path .
```

The built binary is `target/release/formatjs`.

## Usage

### Extract Command

Extract string messages from React components that use react-intl:

```bash
formatjs extract "src/**/*.tsx" --out-file messages.json
```

Rust extraction reads `formatjs_intl::message_descriptor!` descriptors and
inline `formatjs_intl::format_message!` calls. Missing IDs use
`[sha512:contenthash:base64:10]`:

```rust
const GREETING: formatjs_intl::MessageDescriptor = formatjs_intl::message_descriptor!(
    default_message: "Hello, {name}!",
    description: "Greeting"
);

let greeting = formatjs_intl::format_message!(
    &intl,
    default_message: "Hello, {name}!",
    description: "Greeting",
    values: { name: user.name() },
);
```

Inline value names are checked against `default_message` at compile time.
Existing values maps remain supported for dynamic or reused values.

```bash
formatjs extract "src/**/*.rs" --out-file messages.json
```

**Full example with options:**

```bash
formatjs extract "src/**/*.{js,ts,tsx}" \
  --out-file extracted.json \
  --id-interpolation-pattern '[sha512:contenthash:base64:6]' \
  --additional-function-names t,__ \
  --flatten \
  --extract-source-location
```

**Options:**

- `[FILES]...` - JavaScript, TypeScript, or Rust glob patterns (e.g., `src/**/*.tsx`)
- `--format <FORMATTER>` - Built-in formatter controlling JSON output shape (`default`, `simple`, `transifex`, `smartling`, `lokalise`, or `crowdin`)
- `--in-file <PATH>` - File containing list of files to extract (one per line)
- `--out-file <PATH>` - Target file for aggregated .json output
- `--id-interpolation-pattern <PATTERN>` - Pattern to auto-generate message IDs (default: `[sha512:contenthash:base64:6]`)
- `--extract-source-location` - Extract metadata about message location in source
- `--additional-component-names <NAMES>` - Additional component names to extract from (comma-separated)
- `--additional-function-names <NAMES>` - Additional function names to extract from (comma-separated)
- `--ignore <PATTERNS>` - Glob patterns to exclude
- `--throws` - Exit with an error when input resolution, traversal, or extraction fails
- `--pragma <PRAGMA>` - Parse custom pragma for file metadata (e.g., `@intl-meta`)
- `--preserve-whitespace` - Preserve whitespace and newlines
- `--flatten` - Hoist selectors and flatten sentences

### Compile Command

Compile extracted translation files into react-intl consumable JSON:

```bash
formatjs compile "lang/*.json" --out-file compiled.json
```

**Full example with options:**

```bash
formatjs compile "lang/*.json" \
  --out-file compiled.json \
  --ast
```

**Options:**

- `[TRANSLATION_FILES]...` - Glob patterns for translation files (e.g., `foo/**/en.json`)
- `--format <FORMATTER>` - Built-in formatter that converts input to `Record<string, string>` (`default`, `simple`, `transifex`, `smartling`, `lokalise`, or `crowdin`)
- `--out-file <PATH>` - Output file path (prints to stdout if not provided)
- `--ast` - Compile to AST instead of strings
- `--skip-errors` - Continue compiling after errors (excludes keys with errors)
- `--pseudo-locale <LOCALE>` - Generate pseudo-locale AST output; requires `--ast`
  - Values: `xx-LS`, `xx-AC`, `xx-HA`, `en-XA`, `en-XB`
- `--ignore-tag` - Treat HTML/XML tags as string literals

### Compile-Folder Command

Batch compile all translation JSON files in a folder:

```bash
formatjs compile-folder lang/ dist/lang/
```

**Full example with options:**

```bash
formatjs compile-folder lang/ dist/lang/ --ast
```

**Options:**

- `<FOLDER>` - Source directory containing translation JSON files
- `<OUT_FOLDER>` - Output directory for compiled files
- `--format <FORMATTER>` - Built-in formatter (`default`, `simple`, `transifex`, `smartling`, `lokalise`, or `crowdin`)
- `--ast` - Compile to AST

### Verify Command

Run checks on translation files to validate correctness:

```bash
formatjs verify "lang/*.json" --source-locale en --missing-keys
```

**Full example with all checks:**

```bash
formatjs verify "lang/*.json" \
  --source-locale en \
  --missing-keys \
  --extra-keys \
  --structural-equality
```

**Options:**

- `[TRANSLATION_FILES]...` - Glob patterns for translation files
- `--source-locale <LOCALE>` - **Required** for checks to work (e.g., `en`)
- `--ignore <PATTERNS>` - Glob patterns to ignore
- `--missing-keys` - Check for missing keys in target locales
- `--extra-keys` - Check for extra keys not in source locale
- `--structural-equality` - Check structural equality of messages

## Compatibility Notes

`formatjs_cli` is intended to match `@formatjs/cli` where the Rust implementation supports the same feature. Known differences include:

- `--format` accepts built-in formatter names only. The Node.js CLI can also load custom JavaScript formatter files.
- Extraction currently targets JavaScript and TypeScript source files. Framework template extraction for Vue, Svelte, Handlebars, Glimmer, GTS, and GJS is handled by the Node.js CLI.

## Development

### Running Tests

Using Bazel:

```bash
bazel test //crates/formatjs_cli:formatjs_cli_test
```

Using Cargo:

```bash
cargo test
```

### Project Structure

```
crates/formatjs_cli/
├── Cargo.toml          # Cargo package manifest
├── BUILD.bazel         # Bazel build configuration
├── README.md           # This file
└── src/
    └── main.rs         # Main CLI implementation
```

## Dependencies

- `clap`: Command-line argument parsing
- `anyhow`: Error handling
- `serde` & `serde_json`: JSON serialization
- `formatjs_icu_messageformat_parser`: ICU MessageFormat parsing
- `formatjs_icu_skeleton_parser`: ICU skeleton parsing

## Performance

This Rust implementation provides significant performance improvements over the Node.js-based CLI, especially for:

- **Large codebases**: 10-100x faster extraction and compilation
- **Batch processing**: Parallel file and message processing for large catalogs
- **CI/CD pipelines**: Faster builds and deployments
- **Memory efficiency**: Lower memory usage for large message catalogs

See the [benchmarks](../../benchmarks/cli-comparison) for detailed performance comparisons.

## Contributing

Contributions are welcome! Please see the main [FormatJS repository](https://github.com/formatjs/formatjs) for contribution guidelines.

## License

MIT

## Related Packages

- [@formatjs/cli]../../packages/cli - Node.js-based CLI
- [formatjs_icu_messageformat_parser]../icu_messageformat_parser - ICU MessageFormat parser
- [formatjs_icu_skeleton_parser]../icu_skeleton_parser - ICU skeleton parser