Project Files
src / language-packs.ts
/**
* @file language-packs.ts
* @description Language-specific configuration for vibevoice-tts narration.
*
* Each language pack provides:
* - Abbreviation expansion maps
* - Foreign word lists (English words in non-English text)
* - Acronym lists (letter-by-letter pronunciation)
* - Style preset configurations
*/
import type {
LanguagePack,
LanguageCode,
StylePreset,
NarrationStyle,
NarrationMood,
} from "./types";
// ---------------------------------------------------------------------------
// Style Presets
// ---------------------------------------------------------------------------
const STYLE_PRESETS: Readonly<Record<NarrationStyle, StylePreset>> = {
storytelling: {
sentencePause: 0.5,
paragraphPause: 1.0,
scenePause: 2.0,
speed: 0.9,
temperature: 0.75,
wpm: 145,
autoDirections: true,
maxSentences: 3,
description:
"Warm, engaging narration with dramatic pauses. Ideal for stories, tales, and audiobooks.",
},
children: {
sentencePause: 0.6,
paragraphPause: 1.2,
scenePause: 2.5,
speed: 0.85,
temperature: 0.8,
wpm: 130,
autoDirections: true,
maxSentences: 2,
description:
"Slower, clearer speech with frequent pauses. Perfect for children's stories and educational content.",
},
"christmas-tale": {
sentencePause: 0.7,
paragraphPause: 1.5,
scenePause: 3.0,
speed: 0.88,
temperature: 0.7,
wpm: 135,
autoDirections: true,
maxSentences: 3,
description:
"Warm, cozy narration with long, reflective pauses. Ideal for holiday tales and nostalgic stories.",
},
news: {
sentencePause: 0.3,
paragraphPause: 0.6,
scenePause: 1.0,
speed: 1.0,
temperature: 0.6,
wpm: 170,
autoDirections: false,
maxSentences: 4,
description:
"Crisp, rhythmic delivery with minimal pauses. Best for news, reports, and factual content.",
},
technical: {
sentencePause: 0.4,
paragraphPause: 0.8,
scenePause: 1.5,
speed: 0.95,
temperature: 0.65,
wpm: 155,
autoDirections: false,
maxSentences: 4,
description:
"Clear, measured pace with moderate pauses. Optimized for documentation and technical explanations.",
},
dramatic: {
sentencePause: 0.6,
paragraphPause: 1.2,
scenePause: 2.5,
speed: 0.92,
temperature: 0.85,
wpm: 140,
autoDirections: true,
maxSentences: 2,
description:
"Intense, variable pacing with dramatic pauses. For thrillers, poetry, and emotional content.",
},
};
/**
* Get a style preset configuration by name.
*/
export function getStylePreset(style: NarrationStyle): StylePreset {
return STYLE_PRESETS[style];
}
/**
* Get all available style presets.
*/
export function getAllStylePresets(): Readonly<
Record<NarrationStyle, StylePreset>
> {
return STYLE_PRESETS;
}
// ---------------------------------------------------------------------------
// Mood Modifiers
// ---------------------------------------------------------------------------
interface MoodModifier {
speedDelta: number;
temperatureDelta: number;
pauseMultiplier: number;
directionTags: string[];
}
const MOOD_MODIFIERS: Readonly<Record<NarrationMood, MoodModifier>> = {
warm: {
speedDelta: -0.03,
temperatureDelta: -0.05,
pauseMultiplier: 1.2,
directionTags: ["slower", "softer"],
},
mysterious: {
speedDelta: -0.05,
temperatureDelta: 0.05,
pauseMultiplier: 1.4,
directionTags: ["slower", "whisper", "softer"],
},
energetic: {
speedDelta: 0.05,
temperatureDelta: 0.1,
pauseMultiplier: 0.8,
directionTags: ["faster", "emphasis", "louder"],
},
somber: {
speedDelta: -0.08,
temperatureDelta: -0.1,
pauseMultiplier: 1.5,
directionTags: ["slower", "softer"],
},
playful: {
speedDelta: 0.03,
temperatureDelta: 0.1,
pauseMultiplier: 0.9,
directionTags: ["emphasis", "faster"],
},
serious: {
speedDelta: -0.02,
temperatureDelta: -0.05,
pauseMultiplier: 1.1,
directionTags: ["slower", "emphasis"],
},
neutral: {
speedDelta: 0,
temperatureDelta: 0,
pauseMultiplier: 1.0,
directionTags: [],
},
};
/**
* Get mood modifier configuration.
*/
export function getMoodModifier(mood: NarrationMood): MoodModifier {
return MOOD_MODIFIERS[mood];
}
/**
* Apply mood modifier to a style preset, returning adjusted TTS params.
*/
export function applyMoodToPreset(
preset: StylePreset,
mood: NarrationMood,
): {
speed: number;
temperature: number;
sentencePause: number;
paragraphPause: number;
scenePause: number;
directionTags: string[];
} {
const modifier = getMoodModifier(mood);
return {
speed: Math.max(0.5, Math.min(1.5, preset.speed + modifier.speedDelta)),
temperature: Math.max(
0.1,
Math.min(1.5, preset.temperature + modifier.temperatureDelta),
),
sentencePause: preset.sentencePause * modifier.pauseMultiplier,
paragraphPause: preset.paragraphPause * modifier.pauseMultiplier,
scenePause: preset.scenePause * modifier.pauseMultiplier,
directionTags: modifier.directionTags,
};
}
// ---------------------------------------------------------------------------
// Language Packs
// ---------------------------------------------------------------------------
/**
* Polish language pack — abbreviations, foreign words, acronyms.
*/
const PL_PACK: LanguagePack = {
code: "pl",
name: "Polski",
abbreviations: {
// Common Polish abbreviations
"np.": "na przykład",
"itd.": "i tak dalej",
"m.in.": "między innymi",
"tzn.": "to znaczy",
"tj.": "to jest",
np: "na przykład",
itd: "i tak dalej",
"m.in": "między innymi",
tzn: "to znaczy",
tj: "to jest",
// Titles
"prof.": "profesor",
dr: "doktor",
mgr: "magister",
"inż.": "inżynier",
"lic.": "licencjat",
"hab.": "habilitowany",
// Addresses
"ul.": "ulica",
"al.": "aleja",
"pl.": "plac",
"m.": "miasto",
"gm.": "gmina",
"pow.": "powiat",
"woj.": "województwo",
// Time/date
"r.": "roku",
r: "roku",
"godz.": "godzina",
"min.": "minuta",
"sek.": "sekunda",
// Other
"itp.": "i tym podobne",
"zw.": "zwany",
"zw. też": "zwany także",
"ok.": "około",
ok: "około",
"tzw.": "tak zwany",
tzw: "tak zwany",
"jw.": "jak wyżej",
jw: "jak wyżej",
"ww.": "wyżej wymieniony",
ww: "wyżej wymieniony",
"cd.": "ciÄ…g dalszy",
"cdn.": "ciÄ…g dalszy nastÄ…pi",
"str.": "strona",
"t.": "tom",
"cz.": "część",
"wyd.": "wydanie",
"red.": "redakcja",
"opr.": "opracowanie",
"tłum.": "tłumaczenie",
"przyp.": "przypis",
"rys.": "rysunek",
"tab.": "tabela",
},
// English loanwords commonly used in Polish that should be pronounced in English
// English loanwords commonly used in Polish that should be pronounced in English
foreignWords: [
"AI",
"API",
"URL",
"HTTP",
"HTTPS",
"JSON",
"CLI",
"UI",
"UX",
"IT",
"CEO",
"CFO",
"CTO",
"CRM",
"ERP",
"SaaS",
"PaaS",
"IaaS",
"DevOps",
"CI/CD",
"Agile",
"Scrum",
"Sprint",
"Backlog",
"Feature",
"Bug",
"Hotfix",
"Release",
"Deploy",
"Server",
"Cloud",
"Database",
"Framework",
"Library",
"Plugin",
"Widget",
"App",
"Web",
"Internet",
"Email",
"Chat",
"Video",
"Audio",
"Stream",
"Blog",
"Post",
"Like",
"Share",
"Follow",
"Trend",
"Viral",
"Fake news",
"Fake",
"News",
"Media",
"Social media",
"Influencer",
"Startup",
"Scale-up",
"Unicorn",
"Pitch",
"Investor",
"Funding",
"Round",
"Valuation",
"Exit",
"IPO",
"M&A",
"Due diligence",
"NDA",
"SLA",
"KPI",
"OKR",
"ROI",
"B2B",
"B2C",
"C2C",
"D2C",
"e-commerce",
"e-mail",
"e-book",
"e-learning",
"online",
"offline",
"deadline",
"feedback",
"brainstorm",
"workshop",
"meeting",
"call",
"screen",
"laptop",
"smartphone",
"tablet",
"mouse",
"keyboard",
"monitor",
"display",
"printer",
"scanner",
"router",
"wifi",
"bluetooth",
"USB",
"HDMI",
"LED",
"LCD",
"OLED",
"GPU",
"CPU",
"RAM",
"ROM",
"SSD",
"HDD",
"VR",
"AR",
"MR",
"XR",
"IoT",
"AI",
"ML",
"DL",
"NLP",
"CV",
"GAN",
"LLM",
"prompt",
"token",
"embedding",
"model",
"training",
"fine-tuning",
"inference",
"pipeline",
"workflow",
"automation",
"algorithm",
"function",
"variable",
"parameter",
"argument",
"return",
"loop",
"condition",
"exception",
"error",
"debug",
"test",
"mock",
"stub",
"spy",
"fixture",
"suite",
"coverage",
"lint",
"format",
"build",
"compile",
"run",
"execute",
"deploy",
"rollback",
"version",
"branch",
"merge",
"commit",
"push",
"pull",
"fetch",
"clone",
"fork",
"issue",
"PR",
"review",
"approve",
"reject",
"comment",
"assign",
"label",
"milestone",
"project",
"board",
"card",
"list",
"column",
"lane",
"swimlane",
"kanban",
"sprint",
"backlog",
"epic",
"story",
"task",
"subtask",
"bug",
"feature",
"improvement",
"chore",
"docs",
"refactor",
"style",
"test",
"ci",
"cd",
"devops",
"ops",
"dev",
"prod",
"staging",
"qa",
"uat",
"sandbox",
"demo",
"preview",
"canary",
"blue-green",
"rolling",
"zero-downtime",
],
// Acronyms that should be spelled out letter by letter
acronyms: [
"AI",
"API",
"URL",
"HTTP",
"HTTPS",
"JSON",
"CLI",
"UI",
"UX",
"IT",
"CEO",
"CFO",
"CTO",
"CRM",
"ERP",
"SaaS",
"PaaS",
"IaaS",
"NDA",
"SLA",
"KPI",
"OKR",
"ROI",
"B2B",
"B2C",
"C2C",
"D2C",
"IPO",
"VR",
"AR",
"MR",
"XR",
"IoT",
"ML",
"DL",
"NLP",
"CV",
"GAN",
"LLM",
"GPU",
"CPU",
"RAM",
"ROM",
"SSD",
"HDD",
"USB",
"HDMI",
"LED",
"LCD",
"OLED",
"PR",
"QA",
"UAT",
"CI",
"CD",
],
numberFormat: "western",
};
/**
* English language pack (baseline).
*/
const EN_PACK: LanguagePack = {
code: "en",
name: "English",
abbreviations: {
"e.g.": "for example",
"i.e.": "that is",
"etc.": "et cetera",
"vs.": "versus",
"Mr.": "Mister",
"Mrs.": "Missus",
"Ms.": "Miss or Missus",
"Dr.": "Doctor",
"Prof.": "Professor",
"St.": "Saint",
"Jr.": "Junior",
"Sr.": "Senior",
"Inc.": "Incorporated",
"Ltd.": "Limited",
"Co.": "Company",
"Corp.": "Corporation",
"approx.": "approximately",
"dept.": "department",
"est.": "established",
"fig.": "figure",
"Jan.": "January",
"Feb.": "February",
"Mar.": "March",
"Apr.": "April",
"Jun.": "June",
"Jul.": "July",
"Aug.": "August",
"Sep.": "September",
"Sept.": "September",
"Oct.": "October",
"Nov.": "November",
"Dec.": "December",
"Mon.": "Monday",
"Tue.": "Tuesday",
"Wed.": "Wednesday",
"Thu.": "Thursday",
"Fri.": "Friday",
"Sat.": "Saturday",
"Sun.": "Sunday",
},
foreignWords: [],
acronyms: [
"AI",
"API",
"URL",
"HTTP",
"HTTPS",
"JSON",
"CLI",
"UI",
"UX",
"IT",
"CEO",
"CFO",
"CTO",
"CRM",
"ERP",
"SaaS",
"PaaS",
"IaaS",
"NDA",
"SLA",
"KPI",
"OKR",
"ROI",
"B2B",
"B2C",
"C2C",
"D2C",
"IPO",
"VR",
"AR",
"MR",
"XR",
"IoT",
"ML",
"DL",
"NLP",
"CV",
"GAN",
"LLM",
"GPU",
"CPU",
"RAM",
"ROM",
"SSD",
"HDD",
"USB",
"HDMI",
"LED",
"LCD",
"OLED",
"PR",
"QA",
"UAT",
"CI",
"CD",
],
numberFormat: "western",
};
/**
* Get a language pack by code.
*/
export function getLanguagePack(code: LanguageCode): LanguagePack {
const packs: Readonly<Record<LanguageCode, LanguagePack>> = {
pl: PL_PACK,
en: EN_PACK,
// Fallback to English for unsupported languages
de: EN_PACK,
fr: EN_PACK,
es: EN_PACK,
it: EN_PACK,
pt: EN_PACK,
ja: EN_PACK,
ko: EN_PACK,
zh: EN_PACK,
};
return packs[code] ?? EN_PACK;
}
/**
* Get all supported language codes.
*/
export function getSupportedLanguages(): LanguageCode[] {
return ["en", "pl", "de", "fr", "es", "it", "pt", "ja", "ko", "zh"];
}