short-translate
Translates a talking-head take (or a finished video) into another language with the creator's own cloned voice through ElevenLabs Dubbing, using their ElevenLabs API key - sets the key up, trims the take first so no stutter is paid for, estimates the minutes, sends it after a yes, waits, lays the dubbed voice back on the image, then hands the translated take to short-editing for captions in the new language. Use when the user says translate, dub, dubbing, English version, Spanish version, traduis, version anglaise, doublage, ElevenLabs, or wants a second account in another language.
Savant verdict: Review advised
Security analysis or source-hub review identified patterns requiring attention.
Live evaluation
Not evaluated yet. Workspaces can request a live evaluation.
Security assessment
Scan pending.
Structure
- No license declaredConfirm you may reuse this skill before importing it into your repository.
- The source hub's security checks flagged this skillSnyk: warn (Risk: MEDIUM · 1 issue)
SKILL.md
---
name: short-translate
description: Translates a talking-head take (or a finished video) into another language with the creator's own cloned voice through ElevenLabs Dubbing, using their ElevenLabs API key - sets the key up, trims the take first so no stutter is paid for, estimates the minutes, sends it after a yes, waits, lays the dubbed voice back on the image, then hands the translated take to short-editing for captions in the new language. Use when the user says translate, dub, dubbing, English version, Spanish version, traduis, version anglaise, doublage, ElevenLabs, or wants a second account in another language.
argument-hint: "<take, slug or mp4> <target language> [from <source language>]"
allowed-tools: Bash(ffmpeg *) Bash(ffprobe *)
---
# Short Translate
One take in, the same take in another language out, in the creator's own voice (ElevenLabs clones it from the take itself). The image stays the original: no lip-sync. Then `/short-editing` edits the translated take like any other, with captions in the new language.
Talk to them in their language. `<slug>`: the take's slug; `<lang>`: the target language code (`en`, `es`, `fr`, `de`, `pt`, `it`, `ja`...), `<from>`: the source one.
## 1. The ElevenLabs key (once)
The key lives in `video-agent/.env`, never in a chat message, a script or a commit:
```bash
grep -c ELEVENLABS_API_KEY video-agent/.env 2>/dev/null
```
`0` or no file:
1. They create a key at [elevenlabs.io/app/settings/api-keys](https://elevenlabs.io/app/settings/api-keys) (an ElevenLabs account; dubbing uses the credits of their plan, the free plan adds a watermark).
2. They paste it into `video-agent/.env` themselves, as one line: `ELEVENLABS_API_KEY=sk_...`. Offer to create the empty file and open it. Never ask them to paste the key in the chat; if they do anyway, write it to the file and tell them they can regenerate it.
3. `video-agent/.env` must be in the project's `.gitignore` (add the line `video-agent/.env` if it isn't).
Every command below loads it with `set -a; . video-agent/.env; set +a` and never prints it.
## 2. What to translate
Translate the **take**, not a finished video: a finished video has captions burned in the old language. Then short-editing cuts, captions and times everything on the new voice.
- **A take already edited** (`video-agent/edits/short-<slug>/source/spec.js` exists): send only the kept parts, so no silence, stutter or retake is paid for. Make the plain cut at normal speed with short-editing's engine: add `speed: 1` to the spec for this one command, then
```bash
node <skill>/engine/short.mjs rough video-agent/edits/short-<slug>
```
(`<skill>` = the short-editing skill folder) and remove `speed: 1` again. The source is `video-agent/edits/short-<slug>/output/short-<slug>-rough.mp4`.
- **A raw take**: offer to run `/short-editing` up to its cut list first (steps 1 to 4) for the same reason. In a hurry: send the raw take as it is.
## 3. Estimate, then send
```bash
ffprobe -v error -show_entries format=duration -of csv=p=0 "<source>"
```
Show one line and wait for a yes: `Dub <slug> fr → en · 0:52 of video · billed on your ElevenLabs credits for about 1 minute (check your plan's dubbing price on elevenlabs.io/pricing) · no lip-sync`.
Then send it (about 10 s per minute of upload):
```bash
mkdir -p video-agent/.tmp
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -X POST https://api.elevenlabs.io/v1/dubbing \
-H "xi-api-key: $ELEVENLABS_API_KEY" \
-F "file=@<source>" -F "target_lang=<lang>" -F "source_lang=<from or auto>" \
-F "num_speakers=1" -F "watermark=false" -F "name=<slug>-<lang>" \
> video-agent/.tmp/<slug>-<lang>.job.json
cat video-agent/.tmp/<slug>-<lang>.job.json
```
It returns `{"dubbing_id": "...", "expected_duration_sec": ...}`. Keep the id: if anything stops, polling again with it costs nothing. A 401 is a wrong key; a 402 or "quota" means no credits left on their plan; `watermark` refused: send it again without that field.
## 4. Wait, then download
Poll every 15 s until `status` is `dubbed` (`failed`: show the error and stop). Usually 1 to 5 minutes for a short; run it in the background:
```bash
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -H "xi-api-key: $ELEVENLABS_API_KEY" https://api.elevenlabs.io/v1/dubbing/<dubbing_id>
```
Then the dubbed file:
```bash
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -H "xi-api-key: $ELEVENLABS_API_KEY" -o video-agent/.tmp/<slug>-<lang>.dub https://api.elevenlabs.io/v1/dubbing/<dubbing_id>/audio/<lang>
ffprobe -v error -show_entries stream=codec_type -of csv=p=0 video-agent/.tmp/<slug>-<lang>.dub
```
- It has a `video` stream: it is already the dubbed video, move it to `video-agent/takes/<slug>-<lang>.mp4`.
- Audio only: lay it back on the original image:
```bash
ffmpeg -y -i "<source>" -i video-agent/.tmp/<slug>-<lang>.dub -map 0:v -map 1:a -c:v copy -c:a aac -b:a 192k -shortest video-agent/takes/<slug>-<lang>.mp4
```
Check it: same length as the source (within a second). Then ask them to listen to the first 10 seconds before editing (voice, accent, a wrong word).
## 5. Edit the translated take
`/short-editing <slug>-<lang>`, and tell it:
- **The language changed**: in `video-agent/studio/whisper-config.mjs`, set `WHISPER_LANG` to `<lang>` for this transcription (English: `WHISPER_MODEL = "medium.en"` works best; any other language: `"medium"`), and put it back after. The hook, the boards and `post.md` are written in `<lang>`.
- **Same edit as the original** when there is one: same hook and boards (translated), placed on the matching words of the new transcript.
- The ask (CTA) in `<lang>`: ask them once for its translation, or translate the one in voice.md and show it.
Name everything `<slug>-<lang>` so both versions sit side by side. Then `/publish <slug>-<lang>` on the account for that language.
Delete `video-agent/.tmp/<slug>-*` at the end, except the job file if they may want to download the dub again.
## Honest limits
- No lip-sync: the lips move in the original language. Fine with a lot of motion, a small face or quick cuts; noticeable on a long close-up.
- The voice clone comes from the take: a noisy take or music under the voice gives a worse clone.
- Machine translation: names, jargon and jokes can come out wrong. Ask them (or a native speaker) to check the transcript of the dubbed take before posting.
- Dubbing costs ElevenLabs credits per minute, and the price depends on their plan. Trimming the take first is the cheapest saving.