localingos 1.0.0 → 1.0.1
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 +122 -166
- package/package.json +1 -1
- package/src/commands/mcp-serve.js +102 -0
package/README.md
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
# Localingos CLI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
3
|
+
AI-powered translation for developers. Localize your app into 56 languages with one command.
|
|
6
4
|
|
|
7
5
|
```bash
|
|
8
|
-
# Install globally
|
|
9
6
|
npm install -g localingos
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
npx localingos init
|
|
13
|
-
npx localingos sync
|
|
7
|
+
localingos init
|
|
8
|
+
localingos sync
|
|
14
9
|
```
|
|
15
10
|
|
|
16
11
|
## Setup
|
|
@@ -25,12 +20,15 @@ Go to [localingos.com](https://localingos.com) → **Developer Tools** → **API
|
|
|
25
20
|
localingos init
|
|
26
21
|
```
|
|
27
22
|
|
|
28
|
-
This
|
|
23
|
+
This creates two config files:
|
|
24
|
+
|
|
25
|
+
- **`localingos.config.json`** — commit this. Contains project settings (no secrets).
|
|
26
|
+
- **`.localingos.json`** — gitignored. Contains your API key for local development.
|
|
29
27
|
|
|
30
28
|
```json
|
|
29
|
+
// localingos.config.json
|
|
31
30
|
{
|
|
32
|
-
"
|
|
33
|
-
"familyId": "your-family-id",
|
|
31
|
+
"projectId": "your-project-id",
|
|
34
32
|
"sourceLocale": "en-US",
|
|
35
33
|
"format": "json-nested",
|
|
36
34
|
"sourceFile": "./src/i18n/en-US.json",
|
|
@@ -39,22 +37,18 @@ This will interactively create a `.localingos.json` config file:
|
|
|
39
37
|
}
|
|
40
38
|
```
|
|
41
39
|
|
|
42
|
-
> ⚠️ Add `.localingos.json` to your `.gitignore` — it contains your API key.
|
|
43
|
-
|
|
44
40
|
If the source file doesn't exist yet, `init` will offer you three options:
|
|
45
|
-
- **🤖 Generate an AI prompt** to
|
|
46
|
-
- **📝 Create a sample file** with hello-world entries
|
|
41
|
+
- **🤖 Generate an AI prompt** to extract all strings from your codebase
|
|
42
|
+
- **📝 Create a sample file** with hello-world entries
|
|
47
43
|
- **⏭️ Skip** and create it yourself
|
|
48
44
|
|
|
49
45
|
### 3. Extract translatable strings (new projects)
|
|
50
46
|
|
|
51
|
-
If you're internationalizing an existing codebase, use:
|
|
52
|
-
|
|
53
47
|
```bash
|
|
54
48
|
localingos extract
|
|
55
49
|
```
|
|
56
50
|
|
|
57
|
-
|
|
51
|
+
Detects your project type (React, Next.js, Vue, Angular, etc.) and generates a tailored AI prompt. Paste it into your AI assistant (Cursor, Copilot, Claude, ChatGPT) and it will:
|
|
58
52
|
1. Scan your codebase for all user-facing strings
|
|
59
53
|
2. Create the source locale file (e.g., `en-US.json`)
|
|
60
54
|
3. Replace hardcoded strings with `t('key')` calls
|
|
@@ -66,63 +60,96 @@ This detects your project type (React, Next.js, Vue, Angular, etc.) and generate
|
|
|
66
60
|
localingos sync
|
|
67
61
|
```
|
|
68
62
|
|
|
69
|
-
|
|
70
|
-
1. **
|
|
71
|
-
2. **
|
|
72
|
-
3. **
|
|
73
|
-
4. **Write** translation files per locale (e.g., `es-ES.json`, `fr-FR.json`)
|
|
63
|
+
One command to:
|
|
64
|
+
1. **Push** source strings to Localingos
|
|
65
|
+
2. **Pull** all available translations
|
|
66
|
+
3. **Write** locale files (e.g., `es-ES.json`, `fr-FR.json`, `zh-CN.json`)
|
|
74
67
|
|
|
75
68
|
## Commands
|
|
76
69
|
|
|
77
|
-
|
|
70
|
+
| Command | Description |
|
|
71
|
+
|---------|-------------|
|
|
72
|
+
| `localingos init` | Interactive setup wizard |
|
|
73
|
+
| `localingos extract` | Generate AI prompt to extract translatable strings |
|
|
74
|
+
| `localingos sync` | Push source strings and pull translations |
|
|
75
|
+
| `localingos push` | Push source strings only |
|
|
76
|
+
| `localingos pull` | Pull translations and write locale files |
|
|
77
|
+
| `localingos mcp-serve` | Start MCP server for AI coding agents |
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
### Options
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
| Option | Description |
|
|
82
|
+
|--------|-------------|
|
|
83
|
+
| `--dry-run` | Preview without making API calls (sync/push) |
|
|
84
|
+
| `--prune` | Remove stale keys from locale files (sync) |
|
|
85
|
+
| `-c, --config <path>` | Path to config file |
|
|
86
|
+
| `-V, --version` | Show version |
|
|
87
|
+
| `-h, --help` | Show help |
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
## MCP Server
|
|
90
90
|
|
|
91
|
-
The
|
|
91
|
+
The CLI includes a built-in [Model Context Protocol](https://modelcontextprotocol.io) server. Give your AI coding agent direct access to your translation workflow.
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
localingos
|
|
95
|
-
localingos sync --dry-run # Preview what would be synced
|
|
94
|
+
localingos mcp-serve
|
|
96
95
|
```
|
|
97
96
|
|
|
98
|
-
|
|
97
|
+
Configure your AI agent:
|
|
99
98
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
localingos
|
|
99
|
+
**Cursor** (`.cursor/mcp.json`):
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"localingos": { "command": "localingos", "args": ["mcp-serve"] }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
105
106
|
```
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
**Claude Desktop** (`claude_desktop_config.json`):
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"mcpServers": {
|
|
112
|
+
"localingos": { "command": "localingos", "args": ["mcp-serve"], "cwd": "/path/to/your/project" }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
113
115
|
```
|
|
114
116
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
117
|
+
**Remote (no install needed):**
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"localingos": {
|
|
122
|
+
"type": "streamable-http",
|
|
123
|
+
"url": "https://mcp.localingos.com?apiKey=YOUR_KEY&projectId=YOUR_PROJECT_ID"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
124
128
|
|
|
125
|
-
|
|
129
|
+
### Available Tools (20)
|
|
130
|
+
|
|
131
|
+
| Tool | Description |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `localingos_sync` | Push source strings & pull translations |
|
|
134
|
+
| `localingos_push` | Push source strings only |
|
|
135
|
+
| `localingos_pull` | Pull translations and write locale files |
|
|
136
|
+
| `localingos_status` | Show project, locales, key counts |
|
|
137
|
+
| `localingos_search` | Search keys/text across source strings |
|
|
138
|
+
| `localingos_extract` | Run i18n extraction script |
|
|
139
|
+
| `localingos_list_projects` | List all projects you have access to |
|
|
140
|
+
| `localingos_get_translatables` | Get all source strings for a project |
|
|
141
|
+
| `localingos_get_translations` | Get all translations for a project |
|
|
142
|
+
| `localingos_get_translation` | Get translations for a single key |
|
|
143
|
+
| `localingos_get_translations_batch` | Get translations for multiple keys |
|
|
144
|
+
| `localingos_update_translations` | Create or update translations manually |
|
|
145
|
+
| `localingos_delete_translatables` | Delete source strings (prune stale keys) |
|
|
146
|
+
| `localingos_translation_jobs` | List translation jobs, filter by state |
|
|
147
|
+
| `localingos_retry_job` | Retry a single failed translation job |
|
|
148
|
+
| `localingos_retry_all_jobs` | Retry all failed/blocked jobs |
|
|
149
|
+
| `localingos_project_members` | List members of a project |
|
|
150
|
+
| `localingos_add_member` | Add a member to a project |
|
|
151
|
+
| `localingos_remove_member` | Remove a member |
|
|
152
|
+
| `localingos_billing_status` | Show billing status, plan, and usage |
|
|
126
153
|
|
|
127
154
|
## Supported Formats
|
|
128
155
|
|
|
@@ -131,143 +158,72 @@ The `--env` flag is for internal development. You can also set the `LOCALINGOS_E
|
|
|
131
158
|
| `json-nested` | Nested JSON objects | `{ "welcome": { "title": "Hello" } }` |
|
|
132
159
|
| `json-flat` | Flat key-value JSON | `{ "welcome.title": "Hello" }` |
|
|
133
160
|
|
|
134
|
-
More formats (YAML, properties, XLIFF) coming soon.
|
|
135
|
-
|
|
136
161
|
## CI/CD Integration
|
|
137
162
|
|
|
138
|
-
|
|
163
|
+
### GitHub Actions — Auto-sync on push
|
|
164
|
+
|
|
165
|
+
Automatically sync translations when source files change on `main`:
|
|
166
|
+
|
|
167
|
+
1. Add your API key as a repository secret: `LOCALINGOS_API_KEY`
|
|
168
|
+
2. Commit `localingos.config.json` (project config, no secrets)
|
|
169
|
+
3. Copy [`examples/github-actions-sync.yml`](examples/github-actions-sync.yml) to `.github/workflows/localingos-sync.yml`
|
|
139
170
|
|
|
140
171
|
```yaml
|
|
141
|
-
#
|
|
172
|
+
# Minimal example
|
|
142
173
|
- name: Sync translations
|
|
143
174
|
run: npx localingos sync
|
|
144
175
|
env:
|
|
145
|
-
# Store API key as a secret, override config
|
|
146
176
|
LOCALINGOS_API_KEY: ${{ secrets.LOCALINGOS_API_KEY }}
|
|
147
177
|
```
|
|
148
178
|
|
|
149
|
-
## How It Works
|
|
150
|
-
|
|
151
|
-
```
|
|
152
|
-
Your codebase Localingos
|
|
153
|
-
┌──────────────┐ ┌──────────────┐
|
|
154
|
-
│ en-US.json │ ──── push ──→ │ Process & │
|
|
155
|
-
│ (source) │ │ Translate │
|
|
156
|
-
│ │ ←── pull ──── │ (AI-powered) │
|
|
157
|
-
│ es-ES.json │ │ │
|
|
158
|
-
│ fr-FR.json │ │ │
|
|
159
|
-
│ de-DE.json │ │ │
|
|
160
|
-
└──────────────┘ └──────────────┘
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
The `sync` command combines push + pull into a single API call for efficiency. New/changed strings are registered for translation processing. Already-translated strings are returned immediately.
|
|
164
|
-
|
|
165
|
-
## Publishing to npm
|
|
166
|
-
|
|
167
|
-
### First-time setup
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
npm login # Log in to your npmjs.com account (requires 2FA)
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Publishing a new version
|
|
174
|
-
|
|
175
|
-
From the `localingos-cli/` directory:
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
# Patch release (0.1.0 → 0.1.1) — bug fixes
|
|
179
|
-
npm version patch && npm publish
|
|
180
|
-
|
|
181
|
-
# Minor release (0.1.0 → 0.2.0) — new features
|
|
182
|
-
npm version minor && npm publish
|
|
183
|
-
|
|
184
|
-
# Major release (0.1.0 → 1.0.0) — breaking changes
|
|
185
|
-
npm version major && npm publish
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
`npm version` automatically:
|
|
189
|
-
- Bumps the version in `package.json`
|
|
190
|
-
- Creates a git commit with message `v0.2.0`
|
|
191
|
-
- Creates a git tag `v0.2.0`
|
|
192
|
-
|
|
193
|
-
After publishing, push the tag to git:
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
git push && git push --tags
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Quick reference
|
|
200
|
-
|
|
201
|
-
| Command | When to use | Example |
|
|
202
|
-
|---------|------------|---------|
|
|
203
|
-
| `npm version patch` | Bug fix, typo, minor tweak | 0.1.0 → 0.1.1 |
|
|
204
|
-
| `npm version minor` | New feature, new format support | 0.1.0 → 0.2.0 |
|
|
205
|
-
| `npm version major` | Breaking config/API change | 0.1.0 → 1.0.0 |
|
|
206
|
-
|
|
207
|
-
## CI/CD Integration
|
|
208
|
-
|
|
209
|
-
### GitHub Actions — Auto-sync on push
|
|
210
|
-
|
|
211
|
-
Automatically sync translations when `.messages.ts` files change on `main`:
|
|
212
|
-
|
|
213
|
-
1. Add your API key as a repository secret: `LOCALINGOS_API_KEY`
|
|
214
|
-
(Settings → Secrets and variables → Actions → New repository secret)
|
|
215
|
-
|
|
216
|
-
2. Make sure `localingos.config.json` is committed (project config, no secrets).
|
|
217
|
-
`.localingos.json` stays in `.gitignore` (contains API key for local dev only).
|
|
218
|
-
|
|
219
|
-
3. Copy [`examples/github-actions-sync.yml`](examples/github-actions-sync.yml) to `.github/workflows/localingos-sync.yml`
|
|
220
|
-
|
|
221
|
-
This workflow:
|
|
222
|
-
- Triggers when `.messages.ts` or `src/i18n/` files change on `main`
|
|
223
|
-
- Runs `localingos sync --prune` to push source strings and pull translations
|
|
224
|
-
- Commits updated translation files back to the repo
|
|
225
|
-
|
|
226
179
|
### GitHub Actions — PR check
|
|
227
180
|
|
|
228
|
-
Fail PRs if i18n source files are out of date
|
|
181
|
+
Fail PRs if i18n source files are out of date:
|
|
229
182
|
|
|
230
183
|
Copy [`examples/github-actions-check.yml`](examples/github-actions-check.yml) to `.github/workflows/localingos-check.yml`
|
|
231
184
|
|
|
232
|
-
This runs `npm run i18n:check` which verifies:
|
|
233
|
-
- `en-US.json` and `en-US.descriptions.json` match the `.messages.ts` files
|
|
234
|
-
- No orphan messages (defined but never referenced in code)
|
|
235
|
-
|
|
236
185
|
### Environment variable
|
|
237
186
|
|
|
238
|
-
The CLI
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
- **`localingos.config.json`** — commit this. Contains project settings (familyId, locale, paths). No secrets.
|
|
243
|
-
- **`.localingos.json`** — gitignored. Contains only your `apiKey` for local development.
|
|
187
|
+
The CLI resolves the API key in this order:
|
|
188
|
+
1. `apiKeyEnv` custom env var (if set in config)
|
|
189
|
+
2. `LOCALINGOS_API_KEY` env var
|
|
190
|
+
3. `apiKey` in `.localingos.json`
|
|
244
191
|
|
|
245
|
-
|
|
246
|
-
# Local dev — the CLI reads apiKey from .localingos.json automatically
|
|
247
|
-
localingos sync
|
|
248
|
-
|
|
249
|
-
# CI — set as a GitHub secret, the CLI reads it from the env var
|
|
250
|
-
LOCALINGOS_API_KEY=your-key localingos sync
|
|
251
|
-
```
|
|
192
|
+
### Monorepo support
|
|
252
193
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
If you have multiple projects in one repo, each with its own `localingos.config.json` and API key, add an `apiKeyEnv` field to each config pointing to a different env var:
|
|
194
|
+
Each project gets its own `localingos.config.json` with a different `apiKeyEnv`:
|
|
256
195
|
|
|
257
196
|
```json
|
|
258
197
|
// apps/web/localingos.config.json
|
|
259
|
-
{ "apiKeyEnv": "LOCALINGOS_KEY_WEB", "
|
|
198
|
+
{ "apiKeyEnv": "LOCALINGOS_KEY_WEB", "projectId": "...", ... }
|
|
260
199
|
|
|
261
200
|
// apps/docs/localingos.config.json
|
|
262
|
-
{ "apiKeyEnv": "LOCALINGOS_KEY_DOCS", "
|
|
201
|
+
{ "apiKeyEnv": "LOCALINGOS_KEY_DOCS", "projectId": "...", ... }
|
|
263
202
|
```
|
|
264
203
|
|
|
265
|
-
Then in your workflow, pass each secret:
|
|
266
|
-
|
|
267
204
|
```yaml
|
|
268
205
|
env:
|
|
269
206
|
LOCALINGOS_KEY_WEB: ${{ secrets.LOCALINGOS_KEY_WEB }}
|
|
270
207
|
LOCALINGOS_KEY_DOCS: ${{ secrets.LOCALINGOS_KEY_DOCS }}
|
|
271
208
|
```
|
|
272
209
|
|
|
273
|
-
|
|
210
|
+
## How It Works
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
Your codebase Localingos
|
|
214
|
+
┌──────────────┐ ┌──────────────┐
|
|
215
|
+
│ en-US.json │ ──── push ──→ │ Process & │
|
|
216
|
+
│ (source) │ │ Translate │
|
|
217
|
+
│ │ ←── pull ──── │ (AI-powered) │
|
|
218
|
+
│ es-ES.json │ │ │
|
|
219
|
+
│ fr-FR.json │ │ │
|
|
220
|
+
│ zh-CN.json │ │ │
|
|
221
|
+
└──────────────┘ └──────────────┘
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Links
|
|
225
|
+
|
|
226
|
+
- [Website](https://localingos.com)
|
|
227
|
+
- [Documentation](https://localingos.com/docs)
|
|
228
|
+
- [MCP Server](https://localingos.com/mcp)
|
|
229
|
+
- [Pricing](https://localingos.com/pricing)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "localingos",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "AI-powered translation CLI — localize your app into 56 languages with one command. Supports React, Next.js, Vue, Angular. MCP server included.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -257,6 +257,108 @@ export async function mcpServeCommand() {
|
|
|
257
257
|
}
|
|
258
258
|
);
|
|
259
259
|
|
|
260
|
+
// ── Project discovery ──────────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
server.tool('localingos_list_projects', 'List all projects the authenticated user has access to.', {}, async () => {
|
|
263
|
+
const result = await api('GET', '/project', apiUrl, config.apiKey);
|
|
264
|
+
const projects = result.projects || [];
|
|
265
|
+
if (!projects.length) return { content: [{ type: 'text', text: 'No projects found.' }] };
|
|
266
|
+
const lines = projects.map(p => `${p.id} — ${p.name || '(unnamed)'}${p.locales?.length ? ` [${p.locales.join(', ')}]` : ''}`);
|
|
267
|
+
return { content: [{ type: 'text', text: `${projects.length} project(s):\n${lines.join('\n')}` }] };
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// ── Translatable management ────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
server.tool('localingos_delete_translatables',
|
|
273
|
+
'Delete source strings from the project by their foreign IDs. Used for pruning stale keys.',
|
|
274
|
+
{ foreignIds: z.array(z.string()).describe('List of foreign IDs (key names) to delete') },
|
|
275
|
+
async ({ foreignIds }) => {
|
|
276
|
+
try {
|
|
277
|
+
await api('DELETE', `/translatable/${config.projectId}`, apiUrl, config.apiKey, { foreignIds });
|
|
278
|
+
return { content: [{ type: 'text', text: `Deleted ${foreignIds.length} translatable(s)` }] };
|
|
279
|
+
} catch (e) { return { content: [{ type: 'text', text: `Failed: ${e.message}` }] }; }
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// ── Translation tools ──────────────────────────────────────────────────
|
|
284
|
+
|
|
285
|
+
server.tool('localingos_get_translation',
|
|
286
|
+
'Get all translations for a single source string (by foreign ID / key name).',
|
|
287
|
+
{ foreignId: z.string().describe('The key name (foreign ID) of the source string') },
|
|
288
|
+
async ({ foreignId }) => {
|
|
289
|
+
const translations = await api('GET', `/translation/${config.projectId}/${encodeURIComponent(foreignId)}`, apiUrl, config.apiKey);
|
|
290
|
+
if (!translations?.length) return { content: [{ type: 'text', text: `No translations found for "${foreignId}"` }] };
|
|
291
|
+
const lines = translations.map(t => `${t.locale}: "${t.text}"`);
|
|
292
|
+
return { content: [{ type: 'text', text: `${translations.length} translation(s) for "${foreignId}":\n${lines.join('\n')}` }] };
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
server.tool('localingos_get_translations_batch',
|
|
297
|
+
'Get translations for multiple source strings at once.',
|
|
298
|
+
{ foreignIds: z.array(z.string()).describe('List of key names to fetch translations for') },
|
|
299
|
+
async ({ foreignIds }) => {
|
|
300
|
+
const translations = await api('POST', `/translation/${config.projectId}/batch`, apiUrl, config.apiKey, { foreignIds });
|
|
301
|
+
if (!translations?.length) return { content: [{ type: 'text', text: 'No translations found.' }] };
|
|
302
|
+
const byKey = {};
|
|
303
|
+
for (const t of translations) (byKey[t.foreignId] ||= []).push(t);
|
|
304
|
+
const lines = Object.entries(byKey).map(([k, ts]) => `${k}: ${ts.map(t => t.locale).join(', ')}`);
|
|
305
|
+
return { content: [{ type: 'text', text: `${translations.length} translation(s) across ${Object.keys(byKey).length} key(s):\n${lines.join('\n')}` }] };
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
server.tool('localingos_update_translations',
|
|
310
|
+
'Create or update translations manually. Useful for correcting AI translations or adding human translations.',
|
|
311
|
+
{ translations: z.array(z.object({
|
|
312
|
+
foreignId: z.string().describe('Key name of the source string'),
|
|
313
|
+
locale: z.string().describe('Target locale code (e.g. es-ES, fr-FR)'),
|
|
314
|
+
text: z.string().describe('The translated text'),
|
|
315
|
+
})).describe('List of translations to create or update') },
|
|
316
|
+
async ({ translations }) => {
|
|
317
|
+
try {
|
|
318
|
+
const translationList = translations.map(t => ({ ...t, projectId: config.projectId }));
|
|
319
|
+
await api('POST', '/translation', apiUrl, config.apiKey, { projectId: config.projectId, translationList });
|
|
320
|
+
return { content: [{ type: 'text', text: `Updated ${translations.length} translation(s)` }] };
|
|
321
|
+
} catch (e) { return { content: [{ type: 'text', text: `Failed: ${e.message}` }] }; }
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
// ── Translation job tools ──────────────────────────────────────────────
|
|
326
|
+
|
|
327
|
+
server.tool('localingos_translation_jobs',
|
|
328
|
+
'List translation jobs for the project. Optionally filter by state (QUEUED, IN_PROGRESS, DONE, FAILED, BLOCKED).',
|
|
329
|
+
{ state: z.enum(['QUEUED', 'IN_PROGRESS', 'DONE', 'FAILED', 'BLOCKED']).optional().describe('Filter by job state'),
|
|
330
|
+
pageSize: z.number().min(1).max(100).optional().default(20).describe('Number of results per page'),
|
|
331
|
+
},
|
|
332
|
+
async ({ state, pageSize }) => {
|
|
333
|
+
const urlPath = state ? `/translation-jobs/project/${config.projectId}/state/${state}` : `/translation-jobs/project/${config.projectId}`;
|
|
334
|
+
const result = await api('POST', urlPath, apiUrl, config.apiKey, { pageSize, nextToken: null });
|
|
335
|
+
const jobs = result.jobs || [];
|
|
336
|
+
if (!jobs.length) return { content: [{ type: 'text', text: `No ${state || ''} jobs found.` }] };
|
|
337
|
+
const lines = jobs.map(j => `${j.id} [${j.jobState}] ${j.sourceLocale}→${(j.targetLocales || []).join(',')} ${j.errorMessage ? '⚠ ' + j.errorMessage : ''}`);
|
|
338
|
+
const footer = result.hasMore ? `\n... and more (${result.totalCount} total)` : '';
|
|
339
|
+
return { content: [{ type: 'text', text: `${result.totalCount} job(s)${state ? ` (${state})` : ''}:\n${lines.join('\n')}${footer}` }] };
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
server.tool('localingos_retry_job',
|
|
344
|
+
'Retry a single failed translation job.',
|
|
345
|
+
{ jobId: z.string().describe('The job ID to retry'), jobType: z.string().describe('The job type (e.g. TRANSLATE)') },
|
|
346
|
+
async ({ jobId, jobType }) => {
|
|
347
|
+
const result = await api('POST', `/translation-jobs/job/${jobId}/${jobType}/retry`, apiUrl, config.apiKey);
|
|
348
|
+
if (!result.success) return { content: [{ type: 'text', text: `Retry failed: ${result.message}` }] };
|
|
349
|
+
return { content: [{ type: 'text', text: `Job ${jobId} queued for retry` }] };
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
server.tool('localingos_retry_all_jobs',
|
|
354
|
+
'Retry all failed and blocked translation jobs in the project.',
|
|
355
|
+
{},
|
|
356
|
+
async () => {
|
|
357
|
+
const result = await api('POST', `/translation-jobs/project/${config.projectId}/retry-all`, apiUrl, config.apiKey);
|
|
358
|
+
return { content: [{ type: 'text', text: result.message || `Retried ${result.totalProcessed} jobs` }] };
|
|
359
|
+
}
|
|
360
|
+
);
|
|
361
|
+
|
|
260
362
|
const transport = new StdioServerTransport();
|
|
261
363
|
await server.connect(transport);
|
|
262
364
|
}
|