
Language Swap
- 919 installs
- 38 repo stars
- Updated July 20, 2026
- pika-labs/pika-plugins
language-swap is a Pika Plugins /pika workflow skill that dubs and lip-syncs video into target locales while preserving speaker voice for developers localizing generative media without manual scene rebuilds.
About
language-swap is a curated skill in pika-labs/pika-plugins that translates and dubs video into another language through Pika MCP tools. A single dub_video worker call transcribes, translates, clones the speaker voice server-side, and returns an A/V-synced MP4, with lipsync enabled by default up to 300 seconds and optional bilingual subtitles. Developers can pass segmented dub plans with per-range target languages instead of one whole-video locale, then burn target-language or dual-row captions. The skill is one of nine Pika plugin workflows installable via npx skills add Pika-Labs/Pika-Plugins and requires Pika MCP capabilities such as upload_asset, dub_video, and task_status. Reach for it when users ask to translate video, dub in Spanish or Japanese, or localize talking-head content for global distribution—not for captions-only or transcription-only tasks.
- Pika plugin integration
- Multi-language media output
- Automated locale swapping
- Generative video workflows
- Localization pipeline hooks
Language Swap by the numbers
- 919 all-time installs (skills.sh)
- +49 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #296 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/pika-labs/pika-plugins --skill language-swapAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 919 |
|---|---|
| repo stars | ★ 38 |
| Last updated | July 20, 2026 |
| Repository | pika-labs/pika-plugins ↗ |
How do you dub AI video into another language?
Configure the Pika Labs language-swap plugin to localize AI-generated video, captions, or voice across target locales inside generative media pipelines without rebuilding scenes manually.
Who is it for?
Developers localizing finished talking-head or marketing video for new locales while keeping the original speaker's voice and mouth sync.
Skip if: Skip language-swap for captions-only burns, standalone transcription, or translating on-screen text overlays without redubbing audio.
When should I use this skill?
User says translate this video, dub in a language, make this Spanish, or requests bilingual subtitles on a dubbed output.
What you get
Dubbed MP4 with lip-synced audio, optional SRT transcripts, and caption-burned final_video_url
- dubbed MP4
- optional SRT transcripts
- caption-burned final video
By the numbers
- Default lipsync applies to videos up to 300 seconds (5 minutes)
- One of 9 curated Pika-Plugins /pika:* workflow skills
- Supports segmented dub plans with multiple target_language ranges per video
Files
<!-- source-of-truth: pika-claude-plugin/skills/language-swap -->
/pika:language-swap
Translate and dub a video into another language while preserving the original speaker's voice. Pipeline: dub (one worker call) → lipsync (default ON) → burn target-language captions or bilingual captions.
The dubbing worker does the heavy lifting in a single call: it transcribes, translates, preserves each speaker's voice server-side (no separate clone step), and returns a fully A/V-synced video — so there is no manual transcribe/clone/TTS/replace chain to manage and no duration-drift handling to do by hand.
Segmented / multi-language dub (per-range languages)
Use this when the user wants different languages on different parts of one video (e.g. first half Spanish, second half Japanese), or wants to translate only some sections and keep the rest in the original voice. Both are the same thing: a timeline of segments, each tagged with a language; any uncovered range keeps the original audio.
mcp__plugin_pika_pika__dub_video takes a segments plan instead of target_language (pass exactly one — they are mutually exclusive):
mcp__plugin_pika_pika__dub_video(source_video_url=<video_url>, segments=[
{start_s: 0, end_s: 30, target_language: "es"},
{start_s: 30, end_s: 60, target_language: "ja"}
])How to build the plan: the user needs to know where the content is before they can pick ranges, so transcribe first — extract the audio with mcp__claude_ai_pika__extract_audio_from_video, then mcp__claude_ai_pika__transcribe_audio(audio=<audio_url>, timestamps=true), show the user the timestamped segments, and let them say which time range goes to which language. Then assemble segments[] (seconds, ordered, non-overlapping) and make ONE dub_video call. There is no separate "video understanding" tool — the timestamped transcript is the understanding step.
Behavior of the segmented path:
- Shared voice across all segments. The source speaker is cloned once and every segment — in every language — is spoken in that same cloned voice, then the clone is recycled, all inside the one
dub_videocall. You never clone or delete a voice yourself. - Keep-original. Any time range NOT covered by a segment plays the original audio (voice + background) untouched. To translate only parts of a video, list only the parts you want translated.
- Length-locked. Output stays exactly the source length (each dubbed range is speed-fit to its window), so boundaries line up with the original timeline.
- Provider. Mixed-language-per-range always uses the voice-cloning route automatically; the single-call whole-video dubbing route can't mix languages per range, so don't force a single-call provider for a segmented plan. Every covered language must be supported on the voice-cloning route — if one isn't, surface the error and consult
references/language-coverage.md. - Result. Same dubbed-video result;
target_languageechoes the covered languages comma-joined (e.g."spa,jpn"), and no singletranscript_languageis returned (the track is multi-language). Lipsync (Step 2, default ON, ≤5 min) still runs on the whole dubbed video. For captions (Step 3), use the returned multi-languagesubtitles[]incaption_mode="manual"; auto re-transcription can't pick a single language for a mixed track.
If mcp__plugin_pika_pika__dub_video rejects segments (older deployment without segmented support), fall back to dubbing each range single-language and concatenating — but prefer the one-call segmented path when available.
Behavior defaults
- Target language: required via
--to <language>. Prefer language codes:es,fr,ja,de,pt-BR,zh-Hans. The dubbing worker accepts ISO/BCP-47-like tags and normalizes script/region subtags before calling ElevenLabs (for examplezh-Hans→zh;zh-Hant-TW→zh). - Lipsync: ON by default — re-matches the speaker's mouth to the translated audio (fal sync-lipsync; the full-video lip-matcher, distinct from the portrait-image animator). Pass
--no-lipsyncto skip it when the source has no on-camera face or to avoid the meaningful cost (~$4/min on the sync-2-pro tier). Applies only to videos ≤5 min —edit_lipsynchard-caps at 300 s upstream, so longer sources auto-skip lipsync (see Step 2); the dub itself has no length limit. - BGM / background music: kept by default — the dub lays the translated voice over the original music / SFX bed. Pass
--no-bgmfor a translate-only output: the worker drops the original music and keeps only the translated speech (drop_background_audio=true). - Captions: target-language captions are burned by default. When the user asks for bilingual / dual subtitles, burn the target-language (translated) row on top and the source-language (original) row below it — after dubbing, the translated speech is what's actually being said, so it's the primary row; the original is the secondary reference.
- Bilingual captions: enable when the user passes
--bilingual-subtitlesor asks for "bilingual subtitles", "dual subtitles", "two-language captions", "original + translated subtitles", "双语字幕", or "原文+译文字幕". - Language coverage: if language support is questioned or a language-related upstream error occurs, consult
references/language-coverage.md. Do not proactively surface provider-specific language-list details in normal user replies.
State variables produced and consumed
video_url: input — from positional argsource_input_url: original positional URL — preserved for diagnostics ifvideo_urlis rehostedtarget_language: text — from--to <language>with_lipsync: boolean — defaults true; false only when--no-lipsyncno_bgm: boolean — true when--no-bgm(maps todrop_background_audio=true)bilingual_subtitles: boolean — true when the user asks for bilingual / dual subtitlesdubbed_video_url: dubbed, A/V-synced video — produced by Step 1dub_subtitles: optional target-language timed subtitles from the dub result — consumed by Step 3source_subtitles: optional source-language timed subtitles from the dub result — consumed by Step 3 for bilingual captionsdub_transcript_srt: optional target-language SRT from the dub result — returned for review/debuggingsource_transcript_srt: optional source-language SRT from the dub result — returned for review/debuggingsource_transcript_language: optional source-language code from the dub resultlipsynced_video_url: dubbed video with mouth re-matched — produced by Step 2 (when lipsync runs)caption_target_video_url: final visual video URL before captions are burnedfinal_video_url: video with target-language captions burned in — produced by Step 3
Step 0 — Parse input
Required:
- Positional
video_url— MUST behttps://... --to <language>— target language (free-text or BCP-47 code)
Optional:
--no-lipsync— skip the default mouth-matching step.--no-bgm— translate-only output; drop the original music/SFX bed.--bilingual-subtitles— burn source-language + target-language subtitle rows.
Infer bilingual_subtitles=true from user wording even if the explicit flag is absent.
If --to is missing, STOP and prompt the user — UNLESS the user wants different languages on different parts, or to translate only some sections: that is the per-range segmented path (see "Segmented / multi-language dub" above), which uses a segments plan instead of --to.
For the segmented path, first build the time-range plan: extract the audio with mcp__claude_ai_pika__extract_audio_from_video, then transcribe it with timestamps via mcp__claude_ai_pika__transcribe_audio(audio=<audio_url>, timestamps=true), show the user the timestamped segments, and capture which time range maps to which language into segments[].
Outputs: video_url, target_language, with_lipsync (default true), no_bgm (default false), bilingual_subtitles (default false).
Step 1 — Dub the video (state: dubbed_video_url)
Call mcp__plugin_pika_pika__dub_video with:
source_video_url—<video_url>target_language—<target_language>(ISO/BCP-47-like tag, e.g.es,pt-BR,zh-Hans)source_language—"auto"drop_background_audio—trueonly whenno_bgmis set; otherwise omit (keeps the original music bed)
In Claude plugin installs the tool is exposed as mcp__plugin_pika_pika__dub_video. If your host exposes the same Pika server under a different local namespace, call that fully-qualified local tool with the same arguments. The Claude.ai connector surface may lag this plugin-only tool, so do not assume the connector prefix has it.
mcp__plugin_pika_pika__dub_video is worker-backed: if the response comes back as {task_id, status}, poll mcp__plugin_pika_pika__task_status until completed, then read the dubbed video from the result (video_url for a video source; audio_url for an audio source). Also capture optional subtitles[], transcript_srt, and transcript_language — these are target-language transcript metadata the dub worker produced, consumed in Step 3.
For bilingual captions, also capture optional source_subtitles[], source_transcript_srt, and source_transcript_language. These source-language transcript fields are best-effort. The dubbed media is still valid when transcript fields are absent.
Source not worker-fetchable: if mcp__plugin_pika_pika__dub_video fails because the source URL cannot be fetched — especially HTTP 403 / 4xx, hotlink protection, UA-gated hosts (Wikimedia/news CDNs), or "Access Denied" errors — do not keep retrying the same call. Rehost first:
1. Download the source bytes in the client/host environment using a normal browser/download path or an HTTP client with a real user-agent. 2. Call mcp__claude_ai_pika__upload_asset with the downloaded filename, MIME type, and exact byte size, then upload the bytes to the returned presigned URL. 3. Set source_input_url = <original URL> and replace video_url with the returned Pika CDN public_url. Do not construct CDN URLs manually. 4. Retry Step 1 once against the Pika CDN URL. All later steps must use the updated video_url.
If the client/host also cannot download the source bytes, stop and tell the user the host blocks direct fetch; ask them to upload the file or provide a different URL.
Outputs: dubbed_video_url, dub_subtitles, source_subtitles, dub_transcript_srt, source_transcript_srt, source_transcript_language.
Step 2 — Lipsync (state: lipsynced_video_url)
Default ON. Skip entirely when --no-lipsync is passed (then Step 3 captions dubbed_video_url directly).
Hard 5-minute cap — check duration before calling. mcp__claude_ai_pika__edit_lipsync enforces a 300-second (5-minute) audio limit upstream (sync.so) and rejects anything longer with invalid_input before billing; every variant tier shares the same cap, so falling back through tiers does NOT help. If the dubbed video's duration_seconds (returned by Step 1) is > 300, skip lipsync entirely, go straight to Step 3 captioning dubbed_video_url, and tell the user lipsync isn't available past 5 minutes (the dub itself works at any length). Only run the lipsync call below when duration_seconds ≤ 300.
Cost heads-up first. Lipsync is the dominant cost (~$4/min on the v2-pro tier). Before calling it, estimate from the dubbed video's duration_seconds (returned by Step 1) — ceil(duration_seconds / 60) × $4 — and send the user a one-line heads-up, e.g. "Lipsync on — ~2 min video, est. ~$8 (pass --no-lipsync to skip). Starting now." Then proceed straight into the call; this is a heads-up, not an approval gate.
Call mcp__claude_ai_pika__edit_lipsync(video_url=<dubbed_video_url>) with no audio_url — the worker syncs to the dubbed video's own embedded translated audio. Do not extract the audio just to feed it back in. (variant defaults to v2-pro, with sync-3 / v2 as fallbacks.)
Outputs: lipsynced_video_url (read from url of response). When this step runs, Step 3 captions this video, not dubbed_video_url — otherwise the lip-matching is dropped.
Step 3 — Burn target-language captions (state: final_video_url)
Caption the final video so the output carries readable subtitles (matches the common "translate + subtitle" expectation). Set caption_target_video_url to lipsynced_video_url when lipsync ran (the default), or dubbed_video_url when --no-lipsync skipped it.
If this request is part of a Double video / split-screen comparison flow, build that Double video first and set caption_target_video_url to the final composed video URL. Do not burn captions onto only one panel before the Double video is composed; the bilingual caption burn should happen once, on the final visual output.
Call mcp__claude_ai_pika__add_captions once on caption_target_video_url.
When bilingual_subtitles=true, use manual bilingual mode if both tracks are available: call mcp__claude_ai_pika__add_captions(video_url=<caption_target_video_url>, caption_mode="manual", subtitles=<dub_subtitles>, secondary_subtitles=<source_subtitles>, language=<target_language>, secondary_language=<source_transcript_language if available>, secondary_subtitles_position="below", style="branded-space-mono", position="bottom"). The target-language (translated) row is the primary subtitles and renders on top; the source-language (original) row is the secondary reference and renders below it (secondary_subtitles_position="below") — after dubbing the translated speech is what's actually spoken, so it leads. It works for every dub worker provider branch as long as mcp__plugin_pika_pika__dub_video returns both subtitle tracks.
If bilingual_subtitles=true but source_subtitles is missing, fall back to target-language captions only and tell the user the source transcript was unavailable from the dubbing provider. Do not invent a source-language row by retranscribing the final dubbed audio; that audio is already in the target language.
For target-language-only captions, prefer the target-language subtitles the dub worker already returned: if dub_subtitles is non-empty, call mcp__claude_ai_pika__add_captions(video_url=<caption_target_video_url>, caption_mode="manual", subtitles=<dub_subtitles>, language=<target_language>, style="branded-space-mono", position="bottom"). Manual mode skips a duplicate transcription pass and preserves the dubbing provider's target-language text.
If dub_subtitles is missing, empty, or rejected by mcp__claude_ai_pika__add_captions, fall back to auto: call mcp__claude_ai_pika__add_captions(video_url=<caption_target_video_url>, caption_mode="auto", language=<target_language>, style="branded-space-mono", position="bottom"). Auto mode re-transcribes the dubbed audio; use it only as the fallback because it costs extra time and can introduce CJK/proper-noun drift.
Use style="branded-space-mono" unless the user asks for a punchier style (tiktok / hormozi / karaoke). Skip this step only if the user explicitly asked for audio-only dubbing with no captions.
Outputs: final_video_url (read from url of response).
Step 4 — Return
Reply with final_video_url + the translated transcript (from dub_transcript_srt / the dub result) for user review.
Offer a bilingual-subtitle version. When this run burned target-language-only captions (bilingual_subtitles=false) and a source transcript is available (source_subtitles is non-empty), close the reply by asking whether the user also wants a dual-subtitle version, e.g. "Want a bilingual version with the original + translated subtitles stacked? I can add it." If they say yes, re-run Step 3 in bilingual manual mode on the same caption_target_video_url (the pre-caption visual video) — no re-dub or re-lipsync is needed, only the caption burn changes — and return the new final_video_url. Skip the offer when bilingual captions were already burned (bilingual_subtitles=true), or when source_subtitles is missing — without a source transcript a bilingual version can't be produced (the dubbed audio is already in the target language), so do not offer what can't be delivered.
Failure modes
| Class | Trigger | Mitigation | Fallback |
|---|---|---|---|
| Source URL not worker-fetchable | mcp__plugin_pika_pika__dub_video returns 403 / 4xx, hotlink / UA-gated fetch failure, or "Access Denied" for a public HTTPS URL | Download source bytes in the client/host environment, mcp__claude_ai_pika__upload_asset them to Pika, replace video_url with the Pika CDN URL, then retry Step 1 once | If local download also fails, ask the user to upload the file or provide a different URL |
| Extra target language | Target is Cantonese (yue / cantonese / zh-HK), Thai, Hebrew, Persian, Slovenian, Catalan, Norwegian Nynorsk, or Afrikaans | Supported — call mcp__plugin_pika_pika__dub_video with the target as usual; the original speaker's voice is kept | Background music isn't preserved for these languages (dubbed speech only) |
| Dub call fails (not fetchability) | mcp__plugin_pika_pika__dub_video errors for another reason — unsupported target language, provider/worker 5xx, status: failed from mcp__plugin_pika_pika__task_status | Surface the error to the user; if the message points at the language, check references/language-coverage.md and suggest a supported tag; otherwise suggest a retry. There is no manual chain to fall back to — dub is the single path | None — return the error, do not silently produce a non-dubbed video |
| Dub returns no speech | Silent video — nothing to translate | Surface to user: "no detectable speech in video — nothing to translate" | None |
| Original voice can't be kept | For the languages above, the source is too short or noisy to keep the original speaker's voice | Surface the error and ask the user for a cleaner / longer source clip | None — the dub fails rather than using a different voice |
| Lipsync source too long | Dubbed video >5 min — mcp__claude_ai_pika__edit_lipsync rejects with invalid_input (sync.so 300 s cap); all variant tiers share the cap so retrying won't help | Check duration_seconds from Step 1 first and skip lipsync when >300; caption dubbed_video_url directly and tell the user lipsync caps at 5 min | Dubbed video, no lip-match |
| Lipsync step fails | mcp__claude_ai_pika__edit_lipsync errors (no clear face track, provider 4xx) | Fall back through variant tiers (v2-pro → sync-3 → v2); if all fail, return the dubbed video without lip-matching and tell the user | Audio-replaced video, no lip-match |
| Captions wrong language | Step 3 auto-transcription mis-detects language | Pass explicit language tag; if dub_subtitles exists, use caption_mode="manual" with it instead of auto | Manual subtitles[] |
| Bilingual source row unavailable | User asked for bilingual subtitles but source_subtitles is absent | Use target-language captions and explain the source transcript was unavailable | Target-language captions only |
Compatibility
Primary target: Claude Code. Uses standard MCP tools only. Works on Codex / Cursor / Claude Desktop.
Language Coverage Reference
Use this reference as the current worker-supported fast path.
Purpose
Use this page only when language support is questioned, or when a language-related upstream error occurs during /pika:language-swap.
Do not proactively expose provider-specific language-list details in normal user replies. In ordinary success cases, keep the answer focused on the requested output video.
Boundary
The dub_video worker owns a hardcoded, empirically verified allowlist. It is no longer a blind pass-through. Two routing paths:
- ElevenLabs dubbing handles 32 languages — the codes ElevenLabs
eleven_dubbingactually accepts through the worker path. A target outside this set (and outside the Minimax set below) is rejected before billing with the supported list. - Minimax branch (voice-cloned) handles 8 languages ElevenLabs cannot dub: Cantonese, Thai, Hebrew, Persian, Slovenian, Catalan, Norwegian Nynorsk, Afrikaans. For these the worker clones the source speaker's voice and synthesizes with Minimax. Trade-offs on this path: one cloned voice for the whole clip, and the original background-music bed is replaced by the dubbed speech.
Mandarin gotcha: ElevenLabs accepts only zh. The ISO-639-3 code cmn is rejected upstream; the worker auto-maps cmn/zho/chi to zh.
The upstream dubbing product docs also advertise 90+ languages for a newer product path while noting that the newer API is not live yet. Do not use that 90+ claim as a guarantee for the current MCP worker path unless the API contract changes.
Source note: the 32-language ElevenLabs set and the cmn-rejected behavior were verified empirically against the dubbing proxy. The 8-language Minimax set is Minimax Speech-02's language_boost coverage minus the ElevenLabs 32. Keep provider-specific names and links out of normal user-facing replies unless the user explicitly asks for the external source.
Supported languages
ElevenLabs dubbing — 32 languages (verified)
| Language | Code sent upstream |
|---|---|
| Arabic | ara |
| Bulgarian | bul |
| Croatian | hrv |
| Czech | ces |
| Danish | dan |
| Dutch | nld |
| English | eng |
| Filipino | fil |
| Finnish | fin |
| French | fra |
| German | deu |
| Greek | ell |
| Hindi | hin |
| Hungarian | hun |
| Indonesian | ind |
| Italian | ita |
| Japanese | jpn |
| Korean | kor |
| Malay | msa |
| Mandarin Chinese | zh (NOT cmn) |
| Norwegian (Bokmål) | nor |
| Polish | pol |
| Portuguese | por |
| Romanian | ron |
| Russian | rus |
| Slovak | slk |
| Spanish | spa |
| Swedish | swe |
| Tamil | tam |
| Turkish | tur |
| Ukrainian | ukr |
| Vietnamese | vie |
ISO-639-1 spellings (es, fr, ja, pt-BR, …) are accepted and forwarded; region subtags are tolerated and script subtags are dropped.
Minimax voice-clone branch — 8 languages
These are NOT supported by ElevenLabs dubbing; the worker routes them to Minimax with a cloned source voice.
| Language | Accepted target codes |
|---|---|
| Cantonese | yue, cantonese, zh-HK |
| Thai | tha, th |
| Hebrew | heb, he |
| Persian (Farsi) | fas, fa |
| Slovenian | slv, sl |
| Catalan | cat, ca |
| Norwegian Nynorsk | nno, nn (Bokmål no/nb stays on ElevenLabs) |
| Afrikaans | afr, af |
Not supported
Many languages that appear in ElevenLabs' general speech-generation help-center list (e.g. Welsh, Icelandic, Serbian, Swahili, Urdu, Lithuanian, Irish, and others) are not accepted by the dubbing API and are not in the Minimax set. A model-capability list is not a dubbing allowlist — passing such a code now fails fast with the supported list rather than erroring opaquely mid-pipeline.
Agent Guidance
When the user asks "how many languages does Language Swap support?", answer carefully:
- The fast-path dub supports 40 languages total: 32 via ElevenLabs + 8 via the Minimax voice-clone branch.
- The two paths are an implementation detail — do not surface provider names in normal replies. If a requested language is unsupported, state that plainly and, when helpful, point to the closest supported option.
- For the newer 90+ language product claim: mention it only with the API-not-live caveat from the upstream dubbing overview.
Related skills
How it compares
Pick language-swap for full dub plus lipsync localization; use add_captions or video-captions when you only need subtitles without redubbing audio.
FAQ
Does language-swap preserve the original speaker's voice?
language-swap uses Pika dub_video to transcribe, translate, and synthesize speech in the target language while preserving each speaker's voice server-side in one worker call, avoiding a manual transcribe-clone-TTS chain.
When does language-swap skip lipsync?
language-swap enables lipsync by default but auto-skips edit_lipsync on sources longer than 300 seconds because the upstream lip-matcher hard-caps at 5 minutes. Pass --no-lipsync to skip manually on shorter clips.