Skip to content

Commit 8b306bc

Browse files
authored
feat(cli): add file watching feature for automatic retranslation (#966)
* feat(cli): add file watching feature for automatic retranslation * fix: fix prettier error * fix: update chnageset debouncing from 20s to 5
1 parent cf9e706 commit 8b306bc

File tree

7 files changed

+458
-4
lines changed

7 files changed

+458
-4
lines changed

.changeset/hip-jars-hammer.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
"lingo.dev": minor
3+
---
4+
5+
Add watch mode to CLI for automatic retranslation on file changes
6+
7+
This release introduces a new watch mode feature that automatically triggers retranslation when changes are detected in source files:
8+
9+
- **New `--watch` flag**: Enables file watching mode that monitors source files for changes
10+
- **New `--debounce` flag**: Configurable debounce delay (default: 5 seconds) to prevent excessive retranslations
11+
- **Intelligent file pattern detection**: Automatically determines which files to watch based on i18n.json bucket configurations
12+
- **Graceful error handling**: Robust error recovery and process management
13+
- **Background operation**: Non-blocking watch mode with proper cleanup on exit (Ctrl+C)
14+
15+
**Usage:**
16+
```bash
17+
# Enable watch mode with default 5-second debounce
18+
lingo.dev run --watch
19+
20+
# Enable watch mode with custom debounce timing
21+
lingo.dev run --watch --debounce 7000
22+
23+
# Combine with other flags
24+
lingo.dev run --watch --target-locale es --bucket json
25+
```
26+
27+
**Technical Implementation:**
28+
- Uses `chokidar` for robust cross-platform file watching
29+
- Integrates seamlessly with existing CLI pipeline (setup → plan → execute)
30+
- Maintains full compatibility with all existing CLI options and workflows
31+
- Includes comprehensive documentation in `WATCH_MODE.md`
32+
33+
This feature significantly improves developer experience by eliminating the need to manually retrigger translations during development.

packages/cli/WATCH_MODE.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Watch Mode Implementation
2+
3+
This document describes the implementation of the watch mode feature for the Lingo.dev CLI.
4+
5+
## Overview
6+
7+
The watch mode (`--watch` flag) automatically monitors source files for changes and triggers retranslation when modifications are detected. This eliminates the need for manual retranslation after each edit and keeps target language files in sync with source file changes.
8+
9+
## Usage
10+
11+
```bash
12+
# Start watch mode
13+
lingo.dev run --watch
14+
15+
# Watch with custom debounce timing (7 seconds)
16+
lingo.dev run --watch --debounce 7000
17+
18+
# Watch with faster debounce for development (2 seconds)
19+
lingo.dev run --watch --debounce 2000
20+
21+
# Watch with additional filters
22+
lingo.dev run --watch --locale es --bucket json
23+
lingo.dev run --watch --file "src/locales/*.json" --debounce 1000
24+
```
25+
26+
## Features
27+
28+
### 1. Automatic File Monitoring
29+
30+
- Watches all source locale files based on your `i18n.json` configuration
31+
- Monitors file changes, additions, and deletions
32+
- Uses stable file watching to avoid false triggers
33+
34+
### 2. Debounced Processing
35+
36+
- Implements configurable debounce mechanism to avoid excessive retranslations
37+
- Default: 5 seconds, customizable with `--debounce` flag
38+
- Groups rapid changes into single translation batches
39+
- Prevents resource waste from frequent file saves
40+
41+
### 3. Intelligent Pattern Detection
42+
43+
- Automatically determines which files to watch based on bucket patterns
44+
- Replaces `[locale]` placeholders with source locale
45+
- Respects filtering options (`--bucket`, `--file`, etc.)
46+
47+
### 4. Real-time Feedback
48+
49+
- Shows which files are being watched on startup
50+
- Displays file change notifications
51+
- Provides translation progress updates
52+
- Shows completion status for each batch
53+
54+
### 5. Graceful Error Handling
55+
56+
- Continues watching even if individual translations fail
57+
- Reports errors without stopping the watch process
58+
- Maintains watch state across translation cycles
59+
60+
## Implementation Details
61+
62+
### File Structure
63+
64+
- `src/cli/cmd/run/watch.ts` - Main watch implementation
65+
- `src/cli/cmd/run/_types.ts` - Updated to include watch flag
66+
- `src/cli/cmd/run/index.ts` - Integration with main run command
67+
68+
### Key Components
69+
70+
#### Watch State Management
71+
72+
```typescript
73+
interface WatchState {
74+
isRunning: boolean;
75+
pendingChanges: Set<string>;
76+
debounceTimer?: NodeJS.Timeout;
77+
}
78+
```
79+
80+
#### File Pattern Resolution
81+
82+
The watch mode automatically determines which files to monitor by:
83+
84+
1. Getting buckets from `i18n.json`
85+
2. Applying user filters (`--bucket`, `--file`)
86+
3. Replacing `[locale]` with source locale
87+
4. Creating file patterns for chokidar
88+
89+
#### Debounce Logic
90+
91+
- Uses configurable debounce timer (default: 5000ms)
92+
- Resets timer on each file change
93+
- Only triggers translation when timer expires
94+
- Prevents overlapping translation runs
95+
- Customizable via `--debounce <milliseconds>` flag
96+
97+
### Dependencies
98+
99+
- `chokidar` - Robust file watching library
100+
- Existing Lingo.dev pipeline (setup, plan, execute)
101+
102+
## Example Workflow
103+
104+
1. **Start Watch Mode**
105+
106+
```bash
107+
lingo.dev run --watch
108+
```
109+
110+
2. **Initial Setup**
111+
112+
- Performs normal translation setup
113+
- Runs initial planning and execution
114+
- Shows summary of completed translations
115+
- Starts file watching
116+
117+
3. **File Change Detection**
118+
119+
```
120+
📝 File changed: locales/en.json
121+
⏳ Debouncing... (5000ms)
122+
```
123+
124+
4. **Automatic Retranslation**
125+
126+
```
127+
🔄 Triggering retranslation...
128+
Changed files: locales/en.json
129+
130+
[Planning] Found 2 translation task(s)
131+
[Localization] Processing tasks...
132+
✅ Retranslation completed
133+
👀 Continuing to watch for changes...
134+
```
135+
136+
## Error Handling
137+
138+
The watch mode is designed to be resilient:
139+
140+
- **Translation Errors**: Reports errors but continues watching
141+
- **File System Errors**: Logs watch errors but maintains process
142+
- **Invalid Files**: Skips problematic files and continues
143+
- **Interrupt Handling**: Gracefully shuts down on Ctrl+C
144+
145+
## Performance Considerations
146+
147+
- **Efficient Pattern Matching**: Only watches relevant source files
148+
- **Debounced Processing**: Prevents excessive API calls
149+
- **Memory Management**: Clears completed change sets
150+
- **Process Isolation**: Each translation runs in isolated context
151+
152+
## Testing
153+
154+
Use the provided demo setup script:
155+
156+
```bash
157+
./demo-watch-setup.sh
158+
cd /tmp/lingo-watch-demo
159+
lingo.dev run --watch
160+
```
161+
162+
Then in another terminal:
163+
164+
```bash
165+
# Add a new translation key
166+
echo '{"hello": "Hello", "world": "World", "welcome": "Welcome to Lingo.dev", "goodbye": "Goodbye"}' > locales/en.json
167+
168+
# Watch as translations are automatically updated
169+
```
170+
171+
## Integration with Existing Features
172+
173+
The watch mode works seamlessly with all existing run command options:
174+
175+
- `--locale` - Watch only affects specified locales
176+
- `--bucket` - Watch only monitors specified bucket types
177+
- `--file` - Watch only monitors matching file patterns
178+
- `--key` - Post-change filtering applies to specific keys
179+
- `--force` - Forces full retranslation on each change
180+
- `--api-key` - Uses specified API key for all operations
181+
- `--concurrency` - Controls translation parallelism
182+
- `--debounce` - Configures debounce delay in milliseconds (default: 5000ms)
183+
184+
## Future Enhancements
185+
186+
Potential improvements for future versions:
187+
188+
1. **Watch Exclusions**: Ignore specific files or patterns
189+
2. **Selective Translation**: Only translate changed keys
190+
3. **Change Summaries**: Show detailed change reports
191+
4. **Multi-project Support**: Watch multiple i18n configurations
192+
5. **Advanced Debounce Modes**: Per-file or per-bucket debouncing

packages/cli/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
"ai": "^4.3.15",
136136
"bitbucket": "^2.12.0",
137137
"chalk": "^5.4.1",
138+
"chokidar": "^4.0.3",
138139
"cli-progress": "^3.12.0",
139140
"cli-table3": "^0.6.5",
140141
"cors": "^2.8.5",
@@ -203,6 +204,7 @@
203204
},
204205
"devDependencies": {
205206
"@types/babel__generator": "^7.27.0",
207+
"@types/chokidar": "^2.1.7",
206208
"@types/cli-progress": "^3.11.6",
207209
"@types/cors": "^2.8.17",
208210
"@types/diff": "^7.0.0",

packages/cli/src/cli/cmd/run/_types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,7 @@ export const flagsSchema = z.object({
4545
debug: z.boolean().default(false),
4646
sourceLocale: z.string().optional(),
4747
targetLocale: z.array(z.string()).optional(),
48+
watch: z.boolean().default(false),
49+
debounce: z.number().positive().default(5000), // 5 seconds default
4850
});
4951
export type CmdRunFlags = z.infer<typeof flagsSchema>;

packages/cli/src/cli/cmd/run/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Command } from "interactive-commander";
22
import setup from "./setup";
33
import plan from "./plan";
44
import execute from "./execute";
5+
import watch from "./watch";
56
import { CmdRunContext, flagsSchema } from "./_types";
67
import {
78
renderClear,
@@ -60,6 +61,15 @@ export default new Command()
6061
"Number of concurrent tasks to run",
6162
(val: string) => parseInt(val),
6263
)
64+
.option(
65+
"--watch",
66+
"Watch source files for changes and automatically retranslate",
67+
)
68+
.option(
69+
"--debounce <milliseconds>",
70+
"Debounce delay in milliseconds for watch mode (default: 5000ms)",
71+
(val: string) => parseInt(val),
72+
)
6373
.action(async (args) => {
6474
let authId: string | null = null;
6575
try {
@@ -98,6 +108,11 @@ export default new Command()
98108
await renderSummary(ctx.results);
99109
await renderSpacer();
100110

111+
// If watch mode is enabled, start watching for changes
112+
if (ctx.flags.watch) {
113+
await watch(ctx);
114+
}
115+
101116
trackEvent(authId, "cmd.run.success", {
102117
config: ctx.config,
103118
flags: ctx.flags,

0 commit comments

Comments
 (0)