appshot-cli 0.2.2 β 0.3.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 +208 -12
- package/dist/cli.js +1 -1
- package/dist/commands/caption.d.ts.map +1 -1
- package/dist/commands/caption.js +74 -2
- package/dist/commands/caption.js.map +1 -1
- package/dist/commands/localize.d.ts.map +1 -1
- package/dist/commands/localize.js +150 -14
- package/dist/commands/localize.js.map +1 -1
- package/dist/services/translation.d.ts +18 -0
- package/dist/services/translation.d.ts.map +1 -0
- package/dist/services/translation.js +201 -0
- package/dist/services/translation.js.map +1 -0
- package/dist/types/ai.d.ts +25 -0
- package/dist/types/ai.d.ts.map +1 -0
- package/dist/types/ai.js +73 -0
- package/dist/types/ai.js.map +1 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -13,13 +13,14 @@ Generate beautiful, App Store-ready screenshots with device frames, gradients, a
|
|
|
13
13
|
- πΌοΈ **Smart Frames** - Automatically detects portrait/landscape and selects appropriate device frame
|
|
14
14
|
- π¨ **Gradients** - Beautiful gradient backgrounds with customizable colors
|
|
15
15
|
- βοΈ **Captions** - Add marketing text with full typography control (above or overlay)
|
|
16
|
-
- π **Localization** - Multi-language support for international app stores
|
|
16
|
+
- π **Localization** - Multi-language support with AI-powered translations for international app stores
|
|
17
17
|
- π± **Multi-Device** - Support for iPhone, iPad, Mac, Apple TV, Vision Pro, and Apple Watch
|
|
18
18
|
- π **App Store Specs** - Built-in support for all official App Store screenshot resolutions
|
|
19
19
|
- β
**Validation** - Verify screenshots meet App Store requirements
|
|
20
20
|
- π **Orientation Detection** - Intelligently handles both portrait and landscape screenshots
|
|
21
21
|
- β‘ **Fast** - Parallel processing with configurable concurrency
|
|
22
|
-
-
|
|
22
|
+
- π€ **AI Translation** - Real-time and batch translation using OpenAI models (GPT-4o, GPT-5, o1, o3)
|
|
23
|
+
- π οΈ **CLI** - Simple command-line interface designed for agents and automation
|
|
23
24
|
|
|
24
25
|
## Quick Start
|
|
25
26
|
|
|
@@ -200,7 +201,7 @@ Options:
|
|
|
200
201
|
- `--force` - Overwrite existing files
|
|
201
202
|
|
|
202
203
|
### `appshot caption`
|
|
203
|
-
Interactively add or edit captions for screenshots with intelligent autocomplete.
|
|
204
|
+
Interactively add or edit captions for screenshots with intelligent autocomplete and AI-powered translation.
|
|
204
205
|
|
|
205
206
|
Features:
|
|
206
207
|
- **Autocomplete suggestions** - Shows previous captions as you type
|
|
@@ -208,10 +209,23 @@ Features:
|
|
|
208
209
|
- **Usage tracking** - Frequently used captions appear first
|
|
209
210
|
- **Learning system** - Improves suggestions over time
|
|
210
211
|
- **Device-specific** - Prioritizes captions used for the same device
|
|
212
|
+
- **Real-time translation** - Instantly translate captions as you type (requires OpenAI API key)
|
|
211
213
|
|
|
212
214
|
Options:
|
|
213
215
|
- `--device <name>` - Device name (required)
|
|
214
|
-
- `--lang <code>` -
|
|
216
|
+
- `--lang <code>` - Primary language code (default: en)
|
|
217
|
+
- `--translate` - Enable AI-powered real-time translation
|
|
218
|
+
- `--langs <codes>` - Target languages for translation (comma-separated)
|
|
219
|
+
- `--model <name>` - OpenAI model to use (default: gpt-4o-mini)
|
|
220
|
+
|
|
221
|
+
Example with translation:
|
|
222
|
+
```bash
|
|
223
|
+
# Add captions with instant translation to Spanish and French
|
|
224
|
+
appshot caption --device iphone --translate --langs es,fr
|
|
225
|
+
|
|
226
|
+
# Use a specific model for translation
|
|
227
|
+
appshot caption --device iphone --translate --langs zh-CN,ja --model gpt-5
|
|
228
|
+
```
|
|
215
229
|
|
|
216
230
|
Keyboard shortcuts:
|
|
217
231
|
- **Tab** - Autocomplete the top suggestion
|
|
@@ -369,24 +383,206 @@ Options:
|
|
|
369
383
|
- `--strict` - Validate against required presets only
|
|
370
384
|
- `--fix` - Suggest fixes for invalid screenshots
|
|
371
385
|
|
|
372
|
-
### `appshot localize`
|
|
373
|
-
Generate translations for captions using
|
|
386
|
+
### `appshot localize`
|
|
387
|
+
Generate translations for all existing captions using OpenAI's powerful language models.
|
|
374
388
|
|
|
375
389
|
Options:
|
|
376
|
-
- `--langs <codes>` - Target languages
|
|
377
|
-
- `--device <name>` - Specific device or all
|
|
378
|
-
- `--
|
|
390
|
+
- `--langs <codes>` - Target languages (comma-separated, required)
|
|
391
|
+
- `--device <name>` - Specific device or all devices
|
|
392
|
+
- `--model <name>` - OpenAI model to use (default: gpt-4o-mini)
|
|
393
|
+
- `--source <lang>` - Source language (default: en)
|
|
394
|
+
- `--review` - Review translations before saving
|
|
395
|
+
- `--overwrite` - Overwrite existing translations
|
|
396
|
+
|
|
397
|
+
Supported models:
|
|
398
|
+
- **GPT-4o models**: gpt-4o, gpt-4o-mini (using max_tokens)
|
|
399
|
+
- **GPT-5 models**: gpt-5, gpt-5-mini, gpt-5-nano (using max_completion_tokens)
|
|
400
|
+
- **o1 models**: o1, o1-mini (reasoning models)
|
|
401
|
+
- **o3 models**: o3, o3-mini (latest reasoning models)
|
|
402
|
+
|
|
403
|
+
Example usage:
|
|
404
|
+
```bash
|
|
405
|
+
# Translate all captions to Spanish, French, and German
|
|
406
|
+
appshot localize --langs es,fr,de
|
|
407
|
+
|
|
408
|
+
# Translate iPhone captions only, with review
|
|
409
|
+
appshot localize --device iphone --langs ja,ko --review
|
|
410
|
+
|
|
411
|
+
# Use GPT-5 for higher quality translations
|
|
412
|
+
appshot localize --langs zh-CN,zh-TW --model gpt-5
|
|
413
|
+
|
|
414
|
+
# Overwrite existing translations with new ones
|
|
415
|
+
appshot localize --langs es,fr --overwrite
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Setup:
|
|
419
|
+
```bash
|
|
420
|
+
# Set your OpenAI API key
|
|
421
|
+
export OPENAI_API_KEY="your-api-key-here"
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## AI-Powered Translation π€
|
|
425
|
+
|
|
426
|
+
Appshot includes powerful AI translation capabilities using OpenAI's latest models, including GPT-4o, GPT-5, and reasoning models (o1, o3).
|
|
427
|
+
|
|
428
|
+
### Setup
|
|
429
|
+
|
|
430
|
+
First, set your OpenAI API key:
|
|
431
|
+
```bash
|
|
432
|
+
export OPENAI_API_KEY="your-api-key-here"
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Real-Time Translation
|
|
436
|
+
|
|
437
|
+
Translate captions instantly as you type them:
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
# Add captions with automatic translation to Spanish and French
|
|
441
|
+
appshot caption --device iphone --translate --langs es,fr
|
|
442
|
+
|
|
443
|
+
# Use a specific model for higher quality
|
|
444
|
+
appshot caption --device iphone --translate --langs zh-CN,ja --model gpt-5
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
When you enter a caption, translations appear immediately below:
|
|
448
|
+
```
|
|
449
|
+
? home.png: Welcome to the future
|
|
450
|
+
es: Bienvenido al futuro
|
|
451
|
+
fr: Bienvenue dans le futur
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Batch Translation
|
|
455
|
+
|
|
456
|
+
Translate all existing captions at once:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
# Translate all captions to multiple languages
|
|
460
|
+
appshot localize --langs es,fr,de,ja,zh-CN
|
|
461
|
+
|
|
462
|
+
# Translate specific device only
|
|
463
|
+
appshot localize --device iphone --langs es,fr
|
|
464
|
+
|
|
465
|
+
# Review translations before saving
|
|
466
|
+
appshot localize --langs es,fr --review
|
|
467
|
+
|
|
468
|
+
# Use GPT-5 for best quality
|
|
469
|
+
appshot localize --langs ja,ko --model gpt-5
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Supported Models
|
|
473
|
+
|
|
474
|
+
Appshot intelligently handles different OpenAI models and their specific requirements:
|
|
475
|
+
|
|
476
|
+
#### GPT-4o Series (General Purpose)
|
|
477
|
+
- **Models**: `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo`
|
|
478
|
+
- **Best for**: Fast, cost-effective translations
|
|
479
|
+
- **Parameter**: Uses `max_tokens`
|
|
480
|
+
- **Temperature**: Configurable (default 0.3)
|
|
481
|
+
|
|
482
|
+
#### GPT-5 Series (Advanced)
|
|
483
|
+
- **Models**: `gpt-5`, `gpt-5-mini`, `gpt-5-nano`
|
|
484
|
+
- **Best for**: High-quality, nuanced translations
|
|
485
|
+
- **Parameter**: Uses `max_completion_tokens`
|
|
486
|
+
- **Temperature**: Fixed at 1.0 (reasoning model requirement)
|
|
487
|
+
|
|
488
|
+
#### o1 Series (Deep Reasoning)
|
|
489
|
+
- **Models**: `o1`, `o1-mini`
|
|
490
|
+
- **Best for**: Complex marketing copy with cultural adaptation
|
|
491
|
+
- **Parameter**: Uses `max_completion_tokens`
|
|
492
|
+
- **Temperature**: Fixed at 1.0
|
|
493
|
+
|
|
494
|
+
#### o3 Series (Latest Reasoning)
|
|
495
|
+
- **Models**: `o3`, `o3-mini`
|
|
496
|
+
- **Best for**: State-of-the-art translation quality
|
|
497
|
+
- **Parameter**: Uses `max_completion_tokens`
|
|
498
|
+
- **Temperature**: Fixed at 1.0
|
|
499
|
+
|
|
500
|
+
### Language Support
|
|
501
|
+
|
|
502
|
+
Built-in support for 25+ languages:
|
|
503
|
+
- **European**: Spanish (es), French (fr), German (de), Italian (it), Portuguese (pt), Dutch (nl), Swedish (sv), Norwegian (no), Danish (da), Finnish (fi), Polish (pl), Russian (ru)
|
|
504
|
+
- **Asian**: Japanese (ja), Korean (ko), Simplified Chinese (zh-CN), Traditional Chinese (zh-TW), Hindi (hi), Thai (th), Vietnamese (vi), Indonesian (id), Malay (ms)
|
|
505
|
+
- **Middle Eastern**: Arabic (ar), Hebrew (he), Turkish (tr)
|
|
506
|
+
- **Portuguese Variants**: Brazilian Portuguese (pt-BR)
|
|
507
|
+
|
|
508
|
+
### Translation Features
|
|
509
|
+
|
|
510
|
+
#### Smart Caching
|
|
511
|
+
Translations are cached to avoid duplicate API calls. If you translate the same caption again, it returns instantly without using API credits.
|
|
512
|
+
|
|
513
|
+
#### Marketing-Optimized Prompts
|
|
514
|
+
The system uses specialized prompts designed for app marketing text:
|
|
515
|
+
- Maintains marketing tone and impact
|
|
516
|
+
- Keeps translations concise
|
|
517
|
+
- Ensures cultural appropriateness
|
|
518
|
+
- Preserves call-to-action strength
|
|
519
|
+
|
|
520
|
+
#### Error Handling
|
|
521
|
+
- Graceful fallback if API is unavailable
|
|
522
|
+
- Clear error messages for rate limits
|
|
523
|
+
- Automatic retry with delays for batch operations
|
|
524
|
+
- Continues with other translations if one fails
|
|
525
|
+
|
|
526
|
+
### Advanced Configuration
|
|
527
|
+
|
|
528
|
+
Create `.appshot/ai-config.json` for custom settings:
|
|
529
|
+
|
|
530
|
+
```json
|
|
531
|
+
{
|
|
532
|
+
"defaultModel": "gpt-5",
|
|
533
|
+
"temperature": 0.3,
|
|
534
|
+
"cache": true,
|
|
535
|
+
"systemPrompt": "You are translating premium app marketing text. Keep it impactful and concise."
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Cost Optimization Tips
|
|
540
|
+
|
|
541
|
+
1. **Use `gpt-4o-mini`** for draft translations (very cost-effective)
|
|
542
|
+
2. **Enable caching** to avoid retranslating identical text
|
|
543
|
+
3. **Batch operations** are more efficient than real-time for large projects
|
|
544
|
+
4. **Review mode** lets you verify before committing translations
|
|
545
|
+
|
|
546
|
+
### Example Workflow
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
# 1. Initialize project
|
|
550
|
+
appshot init
|
|
551
|
+
|
|
552
|
+
# 2. Add screenshots
|
|
553
|
+
cp ~/Desktop/screenshots/*.png screenshots/iphone/
|
|
554
|
+
|
|
555
|
+
# 3. Add captions with instant translation
|
|
556
|
+
appshot caption --device iphone --translate --langs es,fr,de
|
|
557
|
+
|
|
558
|
+
# 4. Build localized screenshots
|
|
559
|
+
appshot build --langs en,es,fr,de
|
|
560
|
+
|
|
561
|
+
# Output structure:
|
|
562
|
+
# final/
|
|
563
|
+
# iphone/
|
|
564
|
+
# en/
|
|
565
|
+
# home.png
|
|
566
|
+
# es/
|
|
567
|
+
# home.png
|
|
568
|
+
# fr/
|
|
569
|
+
# home.png
|
|
570
|
+
# de/
|
|
571
|
+
# home.png
|
|
572
|
+
```
|
|
379
573
|
|
|
380
574
|
## Multi-Language Support
|
|
381
575
|
|
|
382
|
-
Captions
|
|
576
|
+
Captions are stored with all translations in JSON format:
|
|
383
577
|
|
|
384
578
|
```json
|
|
385
579
|
{
|
|
386
580
|
"home.png": {
|
|
387
581
|
"en": "Organize your life",
|
|
388
582
|
"fr": "Organisez votre vie",
|
|
389
|
-
"es": "Organiza tu vida"
|
|
583
|
+
"es": "Organiza tu vida",
|
|
584
|
+
"de": "Organisieren Sie Ihr Leben",
|
|
585
|
+
"ja": "δΊΊηγζ΄ηγγ"
|
|
390
586
|
}
|
|
391
587
|
}
|
|
392
588
|
```
|
|
@@ -798,7 +994,7 @@ def generate_app_store_screenshots():
|
|
|
798
994
|
- [x] Intelligent caption autocomplete
|
|
799
995
|
- [x] Apple Watch optimizations
|
|
800
996
|
- [x] Gradient presets system (24+ gradients)
|
|
801
|
-
- [
|
|
997
|
+
- [x] **AI-Powered Translations** - Translate captions using OpenAI models (GPT-4o, GPT-5, o1, o3)
|
|
802
998
|
- [ ] **MCP Integration Guide** - Documentation for screenshot tool integration
|
|
803
999
|
- [ ] **Agent API Mode** - Structured JSON input/output for all commands
|
|
804
1000
|
- [ ] **Android Device Support** - Google Play Store specifications
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ const program = new Command();
|
|
|
15
15
|
program
|
|
16
16
|
.name('appshot')
|
|
17
17
|
.description('Generate App Storeβready screenshots with frames, gradients, and captions.')
|
|
18
|
-
.version('0.
|
|
18
|
+
.version('0.3.0');
|
|
19
19
|
program.addCommand(initCmd());
|
|
20
20
|
program.addCommand(captionCmd());
|
|
21
21
|
program.addCommand(styleCmd());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caption.d.ts","sourceRoot":"","sources":["../../src/commands/caption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"caption.d.ts","sourceRoot":"","sources":["../../src/commands/caption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBpC,MAAM,CAAC,OAAO,UAAU,UAAU,YA0MjC"}
|
package/dist/commands/caption.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import autocomplete from 'inquirer-autocomplete-standalone';
|
|
3
|
+
import { select } from '@inquirer/prompts';
|
|
3
4
|
import fuzzy from 'fuzzy';
|
|
4
5
|
import { promises as fs } from 'fs';
|
|
5
6
|
import path from 'path';
|
|
6
7
|
import pc from 'picocolors';
|
|
8
|
+
import { translationService } from '../services/translation.js';
|
|
7
9
|
import { loadCaptionHistory, saveCaptionHistory, updateFrequency, addToSuggestions, getSuggestions, learnFromExistingCaptions } from '../utils/caption-history.js';
|
|
8
10
|
export default function captionCmd() {
|
|
9
11
|
const cmd = new Command('caption')
|
|
10
12
|
.description('Interactively add/edit captions in a device folder')
|
|
11
13
|
.requiredOption('--device <name>', 'device name (iphone|ipad|mac|watch)')
|
|
12
|
-
.option('--lang <code>', 'language code', 'en')
|
|
13
|
-
.
|
|
14
|
+
.option('--lang <code>', 'primary language code', 'en')
|
|
15
|
+
.option('--translate', 'enable AI-powered translation')
|
|
16
|
+
.option('--langs <codes>', 'target languages for translation (comma-separated)')
|
|
17
|
+
.option('--model <name>', 'OpenAI model to use', 'gpt-4o-mini')
|
|
18
|
+
.action(async ({ device, lang, translate, langs, model }) => {
|
|
14
19
|
try {
|
|
15
20
|
const dir = path.join(process.cwd(), 'screenshots', device);
|
|
16
21
|
const captionsFile = path.join(process.cwd(), '.appshot', 'captions', `${device}.json`);
|
|
@@ -44,6 +49,49 @@ export default function captionCmd() {
|
|
|
44
49
|
// Load caption history for autocomplete
|
|
45
50
|
const history = await loadCaptionHistory();
|
|
46
51
|
await learnFromExistingCaptions(history);
|
|
52
|
+
// Check translation setup
|
|
53
|
+
let targetLanguages = [];
|
|
54
|
+
let selectedModel = model;
|
|
55
|
+
if (translate) {
|
|
56
|
+
if (!translationService.hasApiKey()) {
|
|
57
|
+
console.error(pc.red('Error:'), 'OpenAI API key not found');
|
|
58
|
+
console.log(pc.dim('Set the OPENAI_API_KEY environment variable to enable translations'));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// Parse target languages
|
|
62
|
+
if (langs) {
|
|
63
|
+
targetLanguages = langs.split(',').map((l) => l.trim());
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Ask for target languages if not provided
|
|
67
|
+
console.log(pc.cyan('\nSelect target languages for translation:'));
|
|
68
|
+
console.log(pc.dim('Common options: es, fr, de, it, pt, ja, ko, zh-CN'));
|
|
69
|
+
const langsInput = await autocomplete({
|
|
70
|
+
message: 'Target languages (comma-separated):',
|
|
71
|
+
default: 'es,fr,de',
|
|
72
|
+
source: async () => []
|
|
73
|
+
});
|
|
74
|
+
targetLanguages = langsInput.split(',').map((l) => l.trim());
|
|
75
|
+
}
|
|
76
|
+
// Validate model selection
|
|
77
|
+
const availableModels = translationService.getAvailableModels();
|
|
78
|
+
if (!availableModels.includes(selectedModel)) {
|
|
79
|
+
console.log(pc.yellow('\nSelect AI model for translation:'));
|
|
80
|
+
selectedModel = await select({
|
|
81
|
+
message: 'Choose model:',
|
|
82
|
+
choices: availableModels.map(m => {
|
|
83
|
+
const info = translationService.getModelInfo(m);
|
|
84
|
+
return {
|
|
85
|
+
value: m,
|
|
86
|
+
description: info ? `Context: ${info.contextWindow}, Max output: ${info.maxTokens}` : ''
|
|
87
|
+
};
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
await translationService.loadConfig();
|
|
92
|
+
console.log(pc.green('β'), `Translation enabled: ${lang} β ${targetLanguages.join(', ')}`);
|
|
93
|
+
console.log(pc.dim(`Using model: ${selectedModel}\n`));
|
|
94
|
+
}
|
|
47
95
|
console.log(pc.bold(`\nAdding captions for ${device} (${lang}):`));
|
|
48
96
|
console.log(pc.dim('Type to search suggestions, use arrow keys to navigate'));
|
|
49
97
|
console.log(pc.dim('Press Tab to autocomplete, Enter to confirm\n'));
|
|
@@ -100,6 +148,30 @@ export default function captionCmd() {
|
|
|
100
148
|
captions[file] = {};
|
|
101
149
|
}
|
|
102
150
|
captions[file][lang] = text;
|
|
151
|
+
// Translate if enabled
|
|
152
|
+
if (translate && text && targetLanguages.length > 0) {
|
|
153
|
+
process.stdout.write(pc.dim(' Translating...'));
|
|
154
|
+
try {
|
|
155
|
+
const translations = await translationService.translate({
|
|
156
|
+
text,
|
|
157
|
+
targetLanguages,
|
|
158
|
+
model: selectedModel
|
|
159
|
+
});
|
|
160
|
+
// Clear the "Translating..." message
|
|
161
|
+
process.stdout.write('\r\x1b[K');
|
|
162
|
+
// Store translations
|
|
163
|
+
for (const [langCode, translation] of Object.entries(translations)) {
|
|
164
|
+
captions[file][langCode] = translation;
|
|
165
|
+
console.log(pc.dim(` ${langCode}: ${translation}`));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
// Clear the "Translating..." message
|
|
170
|
+
process.stdout.write('\r\x1b[K');
|
|
171
|
+
console.error(pc.yellow(' Translation failed:'), error instanceof Error ? error.message : String(error));
|
|
172
|
+
// Continue without translations
|
|
173
|
+
}
|
|
174
|
+
}
|
|
103
175
|
}
|
|
104
176
|
// Save updated captions
|
|
105
177
|
await fs.writeFile(captionsFile, JSON.stringify(captions, null, 2), 'utf8');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caption.js","sourceRoot":"","sources":["../../src/commands/caption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"caption.js","sourceRoot":"","sources":["../../src/commands/caption.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localize.d.ts","sourceRoot":"","sources":["../../src/commands/localize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"localize.d.ts","sourceRoot":"","sources":["../../src/commands/localize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,MAAM,CAAC,OAAO,UAAU,WAAW,YAyLlC"}
|
|
@@ -1,26 +1,162 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { select, confirm } from '@inquirer/prompts';
|
|
3
|
+
import { promises as fs } from 'fs';
|
|
4
|
+
import path from 'path';
|
|
2
5
|
import pc from 'picocolors';
|
|
6
|
+
import { translationService } from '../services/translation.js';
|
|
3
7
|
export default function localizeCmd() {
|
|
4
8
|
return new Command('localize')
|
|
5
|
-
.description('Generate translations
|
|
9
|
+
.description('Generate translations for all captions using AI')
|
|
6
10
|
.requiredOption('--langs <codes>', 'comma-separated language codes (e.g., fr,de,es)')
|
|
7
11
|
.option('--device <name>', 'specific device (iphone|ipad|mac|watch), or all devices')
|
|
8
|
-
.option('--
|
|
12
|
+
.option('--model <name>', 'OpenAI model to use', 'gpt-4o-mini')
|
|
13
|
+
.option('--source <lang>', 'source language', 'en')
|
|
9
14
|
.option('--review', 'review translations before saving')
|
|
15
|
+
.option('--overwrite', 'overwrite existing translations')
|
|
10
16
|
.action(async (opts) => {
|
|
11
17
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
// Check for API key
|
|
19
|
+
if (!translationService.hasApiKey()) {
|
|
20
|
+
console.error(pc.red('Error:'), 'OpenAI API key not found');
|
|
21
|
+
console.log(pc.dim('Set the OPENAI_API_KEY environment variable:'));
|
|
22
|
+
console.log(pc.dim(' export OPENAI_API_KEY="your-api-key"'));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
// Parse languages
|
|
26
|
+
const targetLanguages = opts.langs.split(',').map((l) => l.trim());
|
|
27
|
+
const sourceLang = opts.source;
|
|
28
|
+
// Select model
|
|
29
|
+
let selectedModel = opts.model;
|
|
30
|
+
const availableModels = translationService.getAvailableModels();
|
|
31
|
+
if (!availableModels.includes(selectedModel)) {
|
|
32
|
+
console.log(pc.yellow('\nSelect AI model for translation:'));
|
|
33
|
+
selectedModel = await select({
|
|
34
|
+
message: 'Choose model:',
|
|
35
|
+
choices: availableModels.map(m => {
|
|
36
|
+
const info = translationService.getModelInfo(m);
|
|
37
|
+
return {
|
|
38
|
+
value: m,
|
|
39
|
+
name: m,
|
|
40
|
+
description: info ? `Max output: ${info.maxTokens} tokens` : ''
|
|
41
|
+
};
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
await translationService.loadConfig();
|
|
46
|
+
console.log(pc.bold('\nπ Batch Translation'));
|
|
47
|
+
console.log('Source language:', pc.cyan(sourceLang));
|
|
48
|
+
console.log('Target languages:', pc.cyan(targetLanguages.join(', ')));
|
|
49
|
+
console.log('Model:', pc.cyan(selectedModel));
|
|
50
|
+
console.log();
|
|
51
|
+
// Determine which devices to process
|
|
52
|
+
const captionsDir = path.join(process.cwd(), '.appshot', 'captions');
|
|
53
|
+
let devices = [];
|
|
54
|
+
if (opts.device && opts.device !== 'all') {
|
|
55
|
+
devices = [opts.device];
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Get all device caption files
|
|
59
|
+
try {
|
|
60
|
+
const files = await fs.readdir(captionsDir);
|
|
61
|
+
devices = files
|
|
62
|
+
.filter(f => f.endsWith('.json'))
|
|
63
|
+
.map(f => f.replace('.json', ''));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
console.error(pc.red('Error:'), 'No caption files found');
|
|
67
|
+
console.log(pc.dim('Run'), pc.cyan('appshot caption'), pc.dim('to create captions first'));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Process each device
|
|
72
|
+
let totalTranslated = 0;
|
|
73
|
+
let totalSkipped = 0;
|
|
74
|
+
for (const device of devices) {
|
|
75
|
+
const captionsFile = path.join(captionsDir, `${device}.json`);
|
|
76
|
+
try {
|
|
77
|
+
const content = await fs.readFile(captionsFile, 'utf8');
|
|
78
|
+
const captions = JSON.parse(content);
|
|
79
|
+
console.log(pc.cyan(`\n${device.toUpperCase()}`));
|
|
80
|
+
console.log(pc.dim('β'.repeat(40)));
|
|
81
|
+
// Collect unique captions to translate
|
|
82
|
+
const textsToTranslate = new Set();
|
|
83
|
+
for (const [_filename, caption] of Object.entries(captions)) {
|
|
84
|
+
if (typeof caption === 'string') {
|
|
85
|
+
textsToTranslate.add(caption);
|
|
86
|
+
}
|
|
87
|
+
else if (caption && typeof caption === 'object') {
|
|
88
|
+
const sourceText = caption[sourceLang];
|
|
89
|
+
if (sourceText) {
|
|
90
|
+
// Check if we should translate (no existing translations or overwrite enabled)
|
|
91
|
+
const hasExistingTranslations = targetLanguages.some((lang) => caption[lang]);
|
|
92
|
+
if (!hasExistingTranslations || opts.overwrite) {
|
|
93
|
+
textsToTranslate.add(sourceText);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (textsToTranslate.size === 0) {
|
|
99
|
+
console.log(pc.yellow(' No captions to translate'));
|
|
100
|
+
totalSkipped++;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
console.log(` Found ${textsToTranslate.size} unique caption(s) to translate`);
|
|
104
|
+
// Batch translate
|
|
105
|
+
const translations = await translationService.translateBatch(Array.from(textsToTranslate), targetLanguages, selectedModel, (current, total) => {
|
|
106
|
+
process.stdout.write(`\r Translating: ${current}/${total}`);
|
|
107
|
+
});
|
|
108
|
+
process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear progress line
|
|
109
|
+
// Review translations if requested
|
|
110
|
+
if (opts.review) {
|
|
111
|
+
console.log(pc.yellow('\n Review translations:'));
|
|
112
|
+
for (const [text, trans] of translations.entries()) {
|
|
113
|
+
console.log(`\n Original: ${pc.white(text)}`);
|
|
114
|
+
for (const [lang, translation] of Object.entries(trans)) {
|
|
115
|
+
console.log(` ${lang}: ${pc.green(translation)}`);
|
|
116
|
+
}
|
|
117
|
+
const proceed = await confirm({
|
|
118
|
+
message: ' Accept these translations?',
|
|
119
|
+
default: true
|
|
120
|
+
});
|
|
121
|
+
if (!proceed) {
|
|
122
|
+
translations.delete(text);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Apply translations to captions
|
|
127
|
+
for (const [filename, caption] of Object.entries(captions)) {
|
|
128
|
+
let sourceText;
|
|
129
|
+
if (typeof caption === 'string') {
|
|
130
|
+
sourceText = caption;
|
|
131
|
+
// Convert to object format
|
|
132
|
+
captions[filename] = { [sourceLang]: caption };
|
|
133
|
+
}
|
|
134
|
+
else if (caption && typeof caption === 'object') {
|
|
135
|
+
sourceText = caption[sourceLang];
|
|
136
|
+
}
|
|
137
|
+
if (sourceText && translations.has(sourceText)) {
|
|
138
|
+
const trans = translations.get(sourceText);
|
|
139
|
+
for (const [lang, translation] of Object.entries(trans)) {
|
|
140
|
+
captions[filename][lang] = translation;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Save updated captions
|
|
145
|
+
await fs.writeFile(captionsFile, JSON.stringify(captions, null, 2), 'utf8');
|
|
146
|
+
console.log(pc.green(' β'), `Translated ${translations.size} caption(s)`);
|
|
147
|
+
totalTranslated += translations.size;
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.error(pc.red(` Error processing ${device}:`), error instanceof Error ? error.message : String(error));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Summary
|
|
154
|
+
console.log(pc.bold('\nπ Summary'));
|
|
155
|
+
console.log(pc.green('β'), `Translated ${totalTranslated} unique caption(s)`);
|
|
156
|
+
if (totalSkipped > 0) {
|
|
157
|
+
console.log(pc.yellow('β '), `Skipped ${totalSkipped} device(s) with no captions`);
|
|
158
|
+
}
|
|
159
|
+
console.log(pc.dim('\nRun'), pc.cyan('appshot build --langs ' + targetLanguages.join(',')), pc.dim('to generate localized screenshots'));
|
|
24
160
|
}
|
|
25
161
|
catch (error) {
|
|
26
162
|
console.error(pc.red('Error:'), error instanceof Error ? error.message : String(error));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localize.js","sourceRoot":"","sources":["../../src/commands/localize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"localize.js","sourceRoot":"","sources":["../../src/commands/localize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { OpenAIModel, ModelConfig, TranslationOptions, TranslationResult, AIConfig } from '../types/ai.js';
|
|
2
|
+
export declare class TranslationService {
|
|
3
|
+
private client;
|
|
4
|
+
private config;
|
|
5
|
+
constructor();
|
|
6
|
+
private initialize;
|
|
7
|
+
hasApiKey(): boolean;
|
|
8
|
+
loadConfig(): Promise<void>;
|
|
9
|
+
saveConfig(config: Partial<AIConfig>): Promise<void>;
|
|
10
|
+
private getCacheKey;
|
|
11
|
+
translate(options: TranslationOptions): Promise<TranslationResult>;
|
|
12
|
+
translateBatch(captions: string[], targetLanguages: string[], model?: OpenAIModel, onProgress?: (current: number, total: number) => void): Promise<Map<string, TranslationResult>>;
|
|
13
|
+
getAvailableModels(): OpenAIModel[];
|
|
14
|
+
getModelInfo(model: OpenAIModel): ModelConfig | undefined;
|
|
15
|
+
clearCache(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare const translationService: TranslationService;
|
|
18
|
+
//# sourceMappingURL=translation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translation.d.ts","sourceRoot":"","sources":["../../src/services/translation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACT,MAAM,gBAAgB,CAAC;AAMxB,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAW;;IAYzB,OAAO,CAAC,UAAU;IAQX,SAAS,IAAI,OAAO;IAId,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjE,OAAO,CAAC,WAAW;IAIN,SAAS,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA8HlE,cAAc,CACzB,QAAQ,EAAE,MAAM,EAAE,EAClB,eAAe,EAAE,MAAM,EAAE,EACzB,KAAK,CAAC,EAAE,WAAW,EACnB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACpD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IA+BnC,kBAAkB,IAAI,WAAW,EAAE;IAInC,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS;IAIzD,UAAU,IAAI,IAAI;CAG1B;AAGD,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { MODEL_CONFIGS } from '../types/ai.js';
|
|
6
|
+
// Cache for translations to avoid duplicate API calls
|
|
7
|
+
const translationCache = new Map();
|
|
8
|
+
export class TranslationService {
|
|
9
|
+
client = null;
|
|
10
|
+
config;
|
|
11
|
+
constructor() {
|
|
12
|
+
this.config = {
|
|
13
|
+
defaultModel: 'gpt-4o-mini',
|
|
14
|
+
temperature: 0.3,
|
|
15
|
+
cache: true
|
|
16
|
+
};
|
|
17
|
+
this.initialize();
|
|
18
|
+
}
|
|
19
|
+
initialize() {
|
|
20
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
21
|
+
if (apiKey) {
|
|
22
|
+
this.client = new OpenAI({ apiKey });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
hasApiKey() {
|
|
26
|
+
return !!process.env.OPENAI_API_KEY;
|
|
27
|
+
}
|
|
28
|
+
async loadConfig() {
|
|
29
|
+
try {
|
|
30
|
+
const configPath = path.join(process.cwd(), '.appshot', 'ai-config.json');
|
|
31
|
+
const content = await fs.readFile(configPath, 'utf8');
|
|
32
|
+
const loadedConfig = JSON.parse(content);
|
|
33
|
+
this.config = { ...this.config, ...loadedConfig };
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Config file doesn't exist, use defaults
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async saveConfig(config) {
|
|
40
|
+
const configPath = path.join(process.cwd(), '.appshot', 'ai-config.json');
|
|
41
|
+
this.config = { ...this.config, ...config };
|
|
42
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
43
|
+
await fs.writeFile(configPath, JSON.stringify(this.config, null, 2), 'utf8');
|
|
44
|
+
}
|
|
45
|
+
getCacheKey(text, languages, model) {
|
|
46
|
+
return `${model}:${languages.sort().join(',')}:${text}`;
|
|
47
|
+
}
|
|
48
|
+
async translate(options) {
|
|
49
|
+
if (!this.client) {
|
|
50
|
+
throw new Error('OpenAI API key not found. Set OPENAI_API_KEY environment variable.');
|
|
51
|
+
}
|
|
52
|
+
const model = options.model || this.config.defaultModel;
|
|
53
|
+
const modelConfig = MODEL_CONFIGS[model];
|
|
54
|
+
if (!modelConfig) {
|
|
55
|
+
throw new Error(`Unknown model: ${model}`);
|
|
56
|
+
}
|
|
57
|
+
// Check cache
|
|
58
|
+
if (this.config.cache) {
|
|
59
|
+
const cacheKey = this.getCacheKey(options.text, options.targetLanguages, model);
|
|
60
|
+
const cached = translationCache.get(cacheKey);
|
|
61
|
+
if (cached) {
|
|
62
|
+
return cached;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Build language map for the prompt
|
|
66
|
+
const languageNames = {
|
|
67
|
+
'es': 'Spanish',
|
|
68
|
+
'fr': 'French',
|
|
69
|
+
'de': 'German',
|
|
70
|
+
'it': 'Italian',
|
|
71
|
+
'pt': 'Portuguese',
|
|
72
|
+
'pt-BR': 'Brazilian Portuguese',
|
|
73
|
+
'ru': 'Russian',
|
|
74
|
+
'ja': 'Japanese',
|
|
75
|
+
'ko': 'Korean',
|
|
76
|
+
'zh-CN': 'Simplified Chinese',
|
|
77
|
+
'zh-TW': 'Traditional Chinese',
|
|
78
|
+
'ar': 'Arabic',
|
|
79
|
+
'hi': 'Hindi',
|
|
80
|
+
'nl': 'Dutch',
|
|
81
|
+
'sv': 'Swedish',
|
|
82
|
+
'no': 'Norwegian',
|
|
83
|
+
'da': 'Danish',
|
|
84
|
+
'fi': 'Finnish',
|
|
85
|
+
'pl': 'Polish',
|
|
86
|
+
'tr': 'Turkish',
|
|
87
|
+
'th': 'Thai',
|
|
88
|
+
'vi': 'Vietnamese',
|
|
89
|
+
'id': 'Indonesian',
|
|
90
|
+
'ms': 'Malay',
|
|
91
|
+
'he': 'Hebrew'
|
|
92
|
+
};
|
|
93
|
+
const targetLangs = options.targetLanguages
|
|
94
|
+
.map(code => `${code}: ${languageNames[code] || code}`)
|
|
95
|
+
.join(', ');
|
|
96
|
+
const systemPrompt = options.systemPrompt || this.config.systemPrompt ||
|
|
97
|
+
`You are a professional app localization expert. Translate the given app screenshot caption into the requested languages.
|
|
98
|
+
The captions are marketing text for mobile app screenshots.
|
|
99
|
+
Keep translations concise, impactful, and culturally appropriate.
|
|
100
|
+
Maintain the marketing tone and appeal of the original text.`;
|
|
101
|
+
const userPrompt = `Translate this app screenshot caption into the following languages: ${targetLangs}
|
|
102
|
+
|
|
103
|
+
Caption: "${options.text}"
|
|
104
|
+
|
|
105
|
+
Return ONLY a JSON object with language codes as keys and translations as values. Example:
|
|
106
|
+
{
|
|
107
|
+
"es": "Spanish translation here",
|
|
108
|
+
"fr": "French translation here"
|
|
109
|
+
}`;
|
|
110
|
+
try {
|
|
111
|
+
// Build the request parameters based on model type
|
|
112
|
+
const params = {
|
|
113
|
+
model: modelConfig.model,
|
|
114
|
+
messages: [
|
|
115
|
+
{ role: 'system', content: systemPrompt },
|
|
116
|
+
{ role: 'user', content: userPrompt }
|
|
117
|
+
],
|
|
118
|
+
temperature: modelConfig.temperature,
|
|
119
|
+
response_format: { type: 'json_object' }
|
|
120
|
+
};
|
|
121
|
+
// Use the appropriate max tokens parameter based on model
|
|
122
|
+
if (modelConfig.maxTokensParam === 'max_completion_tokens') {
|
|
123
|
+
params.max_completion_tokens = Math.min(modelConfig.maxTokens, 2000); // Limit for translations
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
params.max_tokens = Math.min(modelConfig.maxTokens, 2000);
|
|
127
|
+
}
|
|
128
|
+
const response = await this.client.chat.completions.create(params);
|
|
129
|
+
const content = response.choices[0]?.message?.content;
|
|
130
|
+
if (!content) {
|
|
131
|
+
throw new Error('No response from OpenAI');
|
|
132
|
+
}
|
|
133
|
+
const translations = JSON.parse(content);
|
|
134
|
+
// Validate that we got all requested languages
|
|
135
|
+
for (const lang of options.targetLanguages) {
|
|
136
|
+
if (!translations[lang]) {
|
|
137
|
+
console.warn(pc.yellow(`Warning: Translation for ${lang} not returned by API`));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Cache the result
|
|
141
|
+
if (this.config.cache) {
|
|
142
|
+
const cacheKey = this.getCacheKey(options.text, options.targetLanguages, model);
|
|
143
|
+
translationCache.set(cacheKey, translations);
|
|
144
|
+
}
|
|
145
|
+
return translations;
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
if (error instanceof OpenAI.APIError) {
|
|
149
|
+
if (error.status === 401) {
|
|
150
|
+
throw new Error('Invalid OpenAI API key');
|
|
151
|
+
}
|
|
152
|
+
else if (error.status === 429) {
|
|
153
|
+
throw new Error('OpenAI API rate limit exceeded. Please try again later.');
|
|
154
|
+
}
|
|
155
|
+
else if (error.status === 404) {
|
|
156
|
+
throw new Error(`Model ${model} not found. You may not have access to this model.`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async translateBatch(captions, targetLanguages, model, onProgress) {
|
|
163
|
+
const results = new Map();
|
|
164
|
+
const total = captions.length;
|
|
165
|
+
for (let i = 0; i < captions.length; i++) {
|
|
166
|
+
if (onProgress) {
|
|
167
|
+
onProgress(i + 1, total);
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const translations = await this.translate({
|
|
171
|
+
text: captions[i],
|
|
172
|
+
targetLanguages,
|
|
173
|
+
model
|
|
174
|
+
});
|
|
175
|
+
results.set(captions[i], translations);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.error(pc.red(`Failed to translate: "${captions[i]}"`));
|
|
179
|
+
console.error(pc.dim(error instanceof Error ? error.message : String(error)));
|
|
180
|
+
// Continue with other translations
|
|
181
|
+
}
|
|
182
|
+
// Add a small delay to avoid rate limits
|
|
183
|
+
if (i < captions.length - 1) {
|
|
184
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return results;
|
|
188
|
+
}
|
|
189
|
+
getAvailableModels() {
|
|
190
|
+
return Object.keys(MODEL_CONFIGS);
|
|
191
|
+
}
|
|
192
|
+
getModelInfo(model) {
|
|
193
|
+
return MODEL_CONFIGS[model];
|
|
194
|
+
}
|
|
195
|
+
clearCache() {
|
|
196
|
+
translationCache.clear();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Singleton instance
|
|
200
|
+
export const translationService = new TranslationService();
|
|
201
|
+
//# sourceMappingURL=translation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translation.js","sourceRoot":"","sources":["../../src/services/translation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type OpenAIModel = 'gpt-4o' | 'gpt-4o-mini' | 'gpt-4-turbo' | 'gpt-5' | 'gpt-5-mini' | 'gpt-5-nano' | 'o1' | 'o1-mini' | 'o3' | 'o3-mini';
|
|
2
|
+
export interface ModelConfig {
|
|
3
|
+
model: OpenAIModel;
|
|
4
|
+
maxTokensParam: 'max_tokens' | 'max_completion_tokens';
|
|
5
|
+
maxTokens: number;
|
|
6
|
+
temperature?: number;
|
|
7
|
+
contextWindow: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const MODEL_CONFIGS: Record<OpenAIModel, ModelConfig>;
|
|
10
|
+
export interface TranslationOptions {
|
|
11
|
+
text: string;
|
|
12
|
+
targetLanguages: string[];
|
|
13
|
+
model?: OpenAIModel;
|
|
14
|
+
systemPrompt?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TranslationResult {
|
|
17
|
+
[lang: string]: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AIConfig {
|
|
20
|
+
defaultModel: OpenAIModel;
|
|
21
|
+
temperature?: number;
|
|
22
|
+
systemPrompt?: string;
|
|
23
|
+
cache?: boolean;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=ai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/types/ai.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,aAAa,GACb,aAAa,GACb,OAAO,GACP,YAAY,GACZ,YAAY,GACZ,IAAI,GACJ,SAAS,GACT,IAAI,GACJ,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,CAAC;IACnB,cAAc,EAAE,YAAY,GAAG,uBAAuB,CAAC;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CAuE1D,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB"}
|
package/dist/types/ai.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export const MODEL_CONFIGS = {
|
|
2
|
+
'gpt-4o': {
|
|
3
|
+
model: 'gpt-4o',
|
|
4
|
+
maxTokensParam: 'max_tokens',
|
|
5
|
+
maxTokens: 16384,
|
|
6
|
+
temperature: 0.3,
|
|
7
|
+
contextWindow: 128000
|
|
8
|
+
},
|
|
9
|
+
'gpt-4o-mini': {
|
|
10
|
+
model: 'gpt-4o-mini',
|
|
11
|
+
maxTokensParam: 'max_tokens',
|
|
12
|
+
maxTokens: 16384,
|
|
13
|
+
temperature: 0.3,
|
|
14
|
+
contextWindow: 128000
|
|
15
|
+
},
|
|
16
|
+
'gpt-4-turbo': {
|
|
17
|
+
model: 'gpt-4-turbo',
|
|
18
|
+
maxTokensParam: 'max_tokens',
|
|
19
|
+
maxTokens: 4096,
|
|
20
|
+
temperature: 0.3,
|
|
21
|
+
contextWindow: 128000
|
|
22
|
+
},
|
|
23
|
+
'gpt-5': {
|
|
24
|
+
model: 'gpt-5',
|
|
25
|
+
maxTokensParam: 'max_completion_tokens',
|
|
26
|
+
maxTokens: 16384,
|
|
27
|
+
temperature: 1, // GPT-5 models have fixed temperature
|
|
28
|
+
contextWindow: 200000
|
|
29
|
+
},
|
|
30
|
+
'gpt-5-mini': {
|
|
31
|
+
model: 'gpt-5-mini',
|
|
32
|
+
maxTokensParam: 'max_completion_tokens',
|
|
33
|
+
maxTokens: 8192,
|
|
34
|
+
temperature: 1, // GPT-5 models have fixed temperature
|
|
35
|
+
contextWindow: 128000
|
|
36
|
+
},
|
|
37
|
+
'gpt-5-nano': {
|
|
38
|
+
model: 'gpt-5-nano',
|
|
39
|
+
maxTokensParam: 'max_completion_tokens',
|
|
40
|
+
maxTokens: 4096,
|
|
41
|
+
temperature: 1, // GPT-5 models have fixed temperature
|
|
42
|
+
contextWindow: 64000
|
|
43
|
+
},
|
|
44
|
+
'o1': {
|
|
45
|
+
model: 'o1',
|
|
46
|
+
maxTokensParam: 'max_completion_tokens',
|
|
47
|
+
maxTokens: 100000,
|
|
48
|
+
temperature: 1, // o1 models have fixed temperature
|
|
49
|
+
contextWindow: 200000
|
|
50
|
+
},
|
|
51
|
+
'o1-mini': {
|
|
52
|
+
model: 'o1-mini',
|
|
53
|
+
maxTokensParam: 'max_completion_tokens',
|
|
54
|
+
maxTokens: 65536,
|
|
55
|
+
temperature: 1,
|
|
56
|
+
contextWindow: 128000
|
|
57
|
+
},
|
|
58
|
+
'o3': {
|
|
59
|
+
model: 'o3',
|
|
60
|
+
maxTokensParam: 'max_completion_tokens',
|
|
61
|
+
maxTokens: 100000,
|
|
62
|
+
temperature: 1,
|
|
63
|
+
contextWindow: 200000
|
|
64
|
+
},
|
|
65
|
+
'o3-mini': {
|
|
66
|
+
model: 'o3-mini',
|
|
67
|
+
maxTokensParam: 'max_completion_tokens',
|
|
68
|
+
maxTokens: 65536,
|
|
69
|
+
temperature: 1,
|
|
70
|
+
contextWindow: 128000
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=ai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/types/ai.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,MAAM,aAAa,GAAqC;IAC7D,QAAQ,EAAE;QACR,KAAK,EAAE,QAAQ;QACf,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,GAAG;QAChB,aAAa,EAAE,MAAM;KACtB;IACD,aAAa,EAAE;QACb,KAAK,EAAE,aAAa;QACpB,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,GAAG;QAChB,aAAa,EAAE,MAAM;KACtB;IACD,aAAa,EAAE;QACb,KAAK,EAAE,aAAa;QACpB,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,GAAG;QAChB,aAAa,EAAE,MAAM;KACtB;IACD,OAAO,EAAE;QACP,KAAK,EAAE,OAAO;QACd,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,CAAC,EAAE,sCAAsC;QACtD,aAAa,EAAE,MAAM;KACtB;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,YAAY;QACnB,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,CAAC,EAAE,sCAAsC;QACtD,aAAa,EAAE,MAAM;KACtB;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,YAAY;QACnB,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,CAAC,EAAE,sCAAsC;QACtD,aAAa,EAAE,KAAK;KACrB;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,IAAI;QACX,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,MAAM;QACjB,WAAW,EAAE,CAAC,EAAE,mCAAmC;QACnD,aAAa,EAAE,MAAM;KACtB;IACD,SAAS,EAAE;QACT,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,CAAC;QACd,aAAa,EAAE,MAAM;KACtB;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,IAAI;QACX,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,MAAM;QACjB,WAAW,EAAE,CAAC;QACd,aAAa,EAAE,MAAM;KACtB;IACD,SAAS,EAAE;QACT,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,CAAC;QACd,aAAa,EAAE,MAAM;KACtB;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appshot-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Generate App Storeβready screenshots with frames, gradients, and captions.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"appshot": "bin/appshot.js"
|
|
@@ -18,10 +18,12 @@
|
|
|
18
18
|
"clean:all": "rm -rf final/ dist/ .appshot/"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@inquirer/prompts": "^7.8.3",
|
|
21
22
|
"commander": "^12.0.0",
|
|
22
23
|
"fuzzy": "^0.1.3",
|
|
23
24
|
"inquirer": "^12.0.0",
|
|
24
25
|
"inquirer-autocomplete-standalone": "^0.8.1",
|
|
26
|
+
"openai": "^5.13.1",
|
|
25
27
|
"picocolors": "^1.0.0",
|
|
26
28
|
"sharp": "^0.33.5"
|
|
27
29
|
},
|