UNPKG

fonema

Version:

Ultra-minimal Spanish text cleaning library for TTS - 100% Effect-TS & ESM

368 lines (280 loc) โ€ข 13.4 kB
# fonema ๐Ÿ‘„ Ultra-minimal Spanish text cleaning library for TTS. 100% Effect-TS, ESM-only, zero dependencies. Made by @blissito ## Installation ```bash npm install fonema effect ``` ## API ### `cleanTextForTTS(text: string)` Main function that applies all Spanish text cleaning transformations in a single pipeline. **Returns:** `Effect<string, TextCleaningError>` ### Utility Functions - `convertSpanishNumber(num: number): string` - Convert numbers to Spanish words - `expandSpanishAbbreviation(abbrev: string): string` - Expand Spanish abbreviations - `convertEmojiToSpanish(emoji: string): string` - Convert emoji to Spanish description ## Features - **Numbers**: `1,234` โ†’ `"mil doscientos treinta y cuatro"` - **Ordinals**: `1ยบ` โ†’ `"primero"`, `2ยช` โ†’ `"segunda"` - **Dates**: `15/03/2024` โ†’ `"quince de marzo de dos mil veinticuatro"` - **Abbreviations**: `Dr.` โ†’ `"Doctor"`, `etc.` โ†’ `"etcรฉtera"` - **Percentages**: `25%` โ†’ `"veinticinco por ciento"` - **Emojis**: `๐Ÿ˜€` โ†’ `"emoji de cara sonriente"`, `๐Ÿš€` โ†’ `"emoji de cohete"` - **Markdown**: `**bold**` โ†’ `"bold"`, `*italic*` โ†’ `"italic"`, removes headers, lists, links - **Code blocks**: Removes ```blocks, preserves inline`code` - **URLs/emails**: Complete removal - **Punctuation**: RAE-compliant normalization ## Inicio Rรกpido ```typescript import { cleanTextForTTS } from "fonema"; import { Effect } from "effect"; // Ejemplo que demuestra todas las capacidades de limpieza const text = ` El Dr. Garcรญa, experto en IA y PLN, ha estado utilizando fonema v1.2.3 en su investigaciรณn durante 5 aรฑos. Naciรณ el 15/03/1980 y atiende en C/ Mayor, 123, 2ยบB. Contacto: dr.garcia@clinica.com | https://drgarcia.es Su รบltimo estudio muestra una mejora del 75% en la naturalidad del TTS usando la normalizaciรณn de texto de fonema: \`\`\`typescript // Antes: "El Dr. Garcรญa atiende en C/ Mayor, 123" // Despuรฉs de fonema: "El Doctor Garcรญa atiende en Calle Mayor, ciento veintitrรฉs" const texto = "El Dr. Garcรญa atiende en C/ Mayor, 123"; const textoLimpio = await Effect.runPromise(cleanTextForTTS(texto)); \`\`\` ยกAgenda tu cita al 555-123-4567!`; const programa = cleanTextForTTS(text); Effect.runSync(programa); /* โ†’ "El Doctor Garcรญa, experto en I A y P L N, ha estado utilizando fonema v uno punto dos punto tres en su investigaciรณn durante cinco aรฑos. Naciรณ el quince de marzo de mil novecientos ochenta y atiende en Calle Mayor, ciento veintitrรฉs, segundo B. Contacto: Su รบltimo estudio muestra una mejora del setenta y cinco por ciento en la naturalidad del T T S usando la normalizaciรณn de texto de fonema: ยกAgenda tu cita al cinco cinco cinco, uno veintitrรฉs, cuarenta y cinco, sesenta y siete!" */ ``` ### ยฟQuรฉ hizo fonema? 1. **Abreviaturas**: `Dr.` โ†’ `Doctor` 2. **Fechas**: `15/03/1980` โ†’ `quince de marzo de mil novecientos ochenta` 3. **Direcciones**: `C/ Mayor` โ†’ `Calle Mayor` 4. **Nรบmeros**: - `123` โ†’ `ciento veintitrรฉs` - `1.2.3` โ†’ `uno punto dos punto tres` - `5` โ†’ `cinco` 5. **Porcentajes**: `75%` โ†’ `setenta y cinco por ciento` 6. **Emojis**: `๐Ÿš€` โ†’ `emoji de cohete`, `๐Ÿ˜€` โ†’ `emoji de cara sonriente` 7. **Eliminรณ**: - Email: `dr.garcia@clinica.com` - URL: `https://drgarcia.es` - Bloque de cรณdigo completo 8. **Nรบmeros de telรฉfono**: `555-123-4567` โ†’ `cinco cinco cinco, uno veintitrรฉs, cuarenta y cinco, sesenta y siete` 9. **Formato de piso**: `2ยบB` โ†’ `segundo B` 10. **Versiones**: `v1.2.3` โ†’ `v uno punto dos punto tres` 11. **Abreviaturas tรฉcnicas**: `IA` โ†’ `I A`, `PLN` โ†’ `P L N`, `TTS` โ†’ `T T S` ## Examples ### Standalone Usage ```typescript import { cleanTextForTTS, convertSpanishNumber, convertEmojiToSpanish, } from "fonema"; import { Effect } from "effect"; // Number conversion convertSpanishNumber(1234); // โ†’ "mil doscientos treinta y cuatro" // Emoji conversion convertEmojiToSpanish("๐Ÿš€"); // โ†’ "emoji de cohete" convertEmojiToSpanish("๐Ÿ˜€"); // โ†’ "emoji de cara sonriente" convertEmojiToSpanish("โค๏ธ"); // โ†’ "emoji de corazรณn rojo" // Full text cleaning const program = Effect.gen(function* () { const result = yield* cleanTextForTTS( "El 15/03/2024 el Dr. Smith presentรณ el 50% del proyecto. ยกFue increรญble! ๐Ÿš€๐Ÿ˜€" ); console.log(result); // โ†’ "El quince de marzo de dos mil veinticuatro el Doctor Smith presentรณ el cincuenta por ciento del proyecto. ยกFue increรญble! emoji de cohete emoji de cara sonriente" }); Effect.runSync(program); ``` ### Error Handling ```typescript import { cleanTextForTTS } from "fonema"; import { Effect } from "effect"; const program = cleanTextForTTS("Some text").pipe( Effect.catchAll((error) => Effect.succeed(`Fallback: ${error.message}`)) ); Effect.runSync(program); ``` ### TTS Integration Examples #### Google Cloud Text-to-Speech ```typescript import { cleanTextForTTS } from "fonema"; import { Effect } from "effect"; import { TextToSpeechClient } from "@google-cloud/text-to-speech"; const client = new TextToSpeechClient(); const synthesizeSpanishText = (text: string) => Effect.gen(function* () { // Clean text with fonema const cleanedText = yield* cleanTextForTTS(text); // Google TTS request const [response] = yield* Effect.tryPromise(() => client.synthesizeSpeech({ input: { text: cleanedText }, voice: { languageCode: "es-ES", name: "es-ES-Neural2-A", }, audioConfig: { audioEncoding: "MP3" }, }) ); return response.audioContent; }); // Usage const program = synthesizeSpanishText( "El Dr. Garcรญa tiene 25 aรฑos y naciรณ el 15/03/1998." ); Effect.runPromise(program).then((audioBuffer) => { // Handle audio buffer }); ``` #### ElevenLabs Integration ```typescript import { cleanTextForTTS } from "fonema"; import { Effect } from "effect"; const elevenLabsTTS = (text: string, voiceId: string) => Effect.gen(function* () { const cleanedText = yield* cleanTextForTTS(text); const response = yield* Effect.tryPromise(() => fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, { method: "POST", headers: { Accept: "audio/mpeg", "Content-Type": "application/json", "xi-api-key": process.env.ELEVENLABS_API_KEY!, }, body: JSON.stringify({ text: cleanedText, model_id: "eleven_multilingual_v2", voice_settings: { stability: 0.5, similarity_boost: 0.5, }, }), }) ); return yield* Effect.tryPromise(() => response.arrayBuffer()); }); ``` #### OpenAI TTS Integration ```typescript import { cleanTextForTTS } from "fonema"; import { Effect } from "effect"; import OpenAI from "openai"; const openai = new OpenAI(); const openAITTS = (text: string) => Effect.gen(function* () { const cleanedText = yield* cleanTextForTTS(text); const mp3 = yield* Effect.tryPromise(() => openai.audio.speech.create({ model: "tts-1", voice: "nova", input: cleanedText, }) ); return yield* Effect.tryPromise(() => mp3.arrayBuffer()); }); ``` #### Generic TTS Service Integration ```typescript import { cleanTextForTTS } from "fonema"; import { Effect } from "effect"; interface TTSService { synthesize(text: string, options: any): Promise<ArrayBuffer>; } const createTTSPipeline = (service: TTSService) => (text: string, options: any) => Effect.gen(function* () { // Always clean Spanish text first const cleanedText = yield* cleanTextForTTS(text); // Then pass to any TTS service return yield* Effect.tryPromise(() => service.synthesize(cleanedText, options) ); }); // Usage with any TTS service const myTTSPipeline = createTTSPipeline(myTTSService); const program = myTTSPipeline("Text with nรบmeros 123 and Dr. abbreviations", { voice: "spanish-voice", speed: 1.0, }); ``` ## Limpieza de Markdown fonema limpia automรกticamente el formato markdown preservando el contenido: ```typescript import { cleanTextForTTS } from "fonema"; import { Effect } from "effect"; const markdownText = ` # Tรญtulo Principal ## Subtรญtulo Este texto tiene **negritas** y *cursivas*. Tambiรฉn __negritas__ y _cursivas_ con guiones bajos. Y texto ~~tachado~~ que se limpia. - Lista con viรฑetas - Segundo elemento 1. Lista numerada 2. Segundo elemento > Cita en blockquote [Enlace a sitio web](https://example.com) ![Imagen](imagen.jpg) Cรณdigo \`inline\` se preserva. `; const programa = cleanTextForTTS(markdownText); Effect.runSync(programa); /* Resultado: "Tรญtulo Principal Subtรญtulo Este texto tiene negritas y cursivas. Tambiรฉn negritas y cursivas con guiones bajos. Y texto tachado que se limpia. Lista con viรฑetas Segundo elemento Lista numerada Segundo elemento Cita en blockquote Enlace a sitio web Cรณdigo inline se preserva." */ ``` ### Elementos Markdown Soportados - โœ… **Negritas**: `**texto**` y `__texto__` โ†’ `texto` - โœ… **Cursivas**: `*texto*` y `_texto_` โ†’ `texto` - โœ… **Tachado**: `~~texto~~` โ†’ `texto` - โœ… **Tรญtulos**: `# Tรญtulo` โ†’ `Tรญtulo` - โœ… **Listas**: `- item` y `1. item` โ†’ `item` - โœ… **Blockquotes**: `> cita` โ†’ `cita` - โœ… **Enlaces**: `[texto](url)` โ†’ `texto` - โœ… **Imรกgenes**: `![alt](url)` โ†’ (se eliminan) - โœ… **Cรณdigo inline**: `` `cรณdigo` `` โ†’ `cรณdigo` - โœ… **Cรณdigo en bloque**: ` ```cรณdigo``` ` โ†’ (se elimina) - โœ… **HTML tags**: `<tag>contenido</tag>` โ†’ `contenido` ## Manejo de Emojis fonema convierte automรกticamente emojis a descripciones en espaรฑol natural para TTS: ```typescript import { cleanTextForTTS, convertEmojiToSpanish } from "fonema"; import { Effect } from "effect"; // Conversiรณn individual de emojis convertEmojiToSpanish("๐Ÿš€"); // โ†’ "emoji de cohete" convertEmojiToSpanish("๐Ÿ˜€"); // โ†’ "emoji de cara sonriente" convertEmojiToSpanish("โค๏ธ"); // โ†’ "emoji de corazรณn rojo" convertEmojiToSpanish("๐ŸŽ‰"); // โ†’ "emoji de fiesta" // Texto con mรบltiples emojis const textoConEmojis = "ยกHola! ๐Ÿ˜€ Me encanta programar ๐Ÿš€ y usar React โค๏ธ para crear apps increรญbles ๐ŸŽ‰"; const programa = cleanTextForTTS(textoConEmojis); Effect.runSync(programa); /* Resultado: "ยกHola! emoji de cara sonriente Me encanta programar emoji de cohete y usar React emoji de corazรณn rojo para crear apps increรญbles emoji de fiesta" */ ``` ### Emojis Soportados fonema incluye mรกs de 400 emojis comunes con descripciones en espaรฑol: - **Caras y emociones**: ๐Ÿ˜€ ๐Ÿ˜ƒ ๐Ÿ˜„ ๐Ÿ˜ ๐Ÿ˜† ๐Ÿ˜… ๐Ÿคฃ ๐Ÿ˜‚ ๐Ÿ™‚ ๐Ÿ™ƒ ๐Ÿ˜‰ ๐Ÿ˜Š ๐Ÿ˜‡ ๐Ÿฅฐ ๐Ÿ˜ ๐Ÿคฉ ๐Ÿ˜˜ ๐Ÿ˜— โ˜บ๏ธ ๐Ÿ˜š ๐Ÿ˜™ ๐Ÿฅฒ ๐Ÿ˜‹ ๐Ÿ˜› ๐Ÿ˜œ ๐Ÿคช ๐Ÿ˜ ๐Ÿค‘ ๐Ÿค— ๐Ÿคญ ๐Ÿคซ ๐Ÿค” ๐Ÿค ๐Ÿคจ ๐Ÿ˜ ๐Ÿ˜‘ ๐Ÿ˜ถ ๐Ÿ˜ ๐Ÿ˜’ ๐Ÿ™„ ๐Ÿ˜ฌ ๐Ÿคฅ ๐Ÿ˜” ๐Ÿ˜• ๐Ÿ™ โ˜น๏ธ ๐Ÿ˜ฃ ๐Ÿ˜– ๐Ÿ˜ซ ๐Ÿ˜ฉ ๐Ÿฅบ ๐Ÿ˜ข ๐Ÿ˜ญ ๐Ÿ˜ค ๐Ÿ˜  ๐Ÿ˜ก ๐Ÿคฌ ๐Ÿคฏ ๐Ÿ˜ณ ๐Ÿฅต ๐Ÿฅถ ๐Ÿ˜ฑ ๐Ÿ˜จ ๐Ÿ˜ฐ ๐Ÿ˜ฅ ๐Ÿ˜“ ๐Ÿค— ๐Ÿค” ๐Ÿ˜ด ๐Ÿ’ค ๐Ÿ˜ช ๐Ÿ˜ต ๐Ÿค ๐Ÿฅด ๐Ÿคข ๐Ÿคฎ ๐Ÿคง ๐Ÿ˜ท ๐Ÿค’ ๐Ÿค• - **Corazones**: โค๏ธ ๐Ÿงก ๐Ÿ’› ๐Ÿ’š ๐Ÿ’™ ๐Ÿ’œ ๐Ÿ–ค ๐Ÿค ๐ŸคŽ ๐Ÿ’” โฃ๏ธ ๐Ÿ’• ๐Ÿ’ž ๐Ÿ’“ ๐Ÿ’— ๐Ÿ’– ๐Ÿ’˜ ๐Ÿ’ ๐Ÿ’Ÿ - **Gestos y manos**: ๐Ÿ‘ ๐Ÿ‘Ž ๐Ÿ‘Œ โœŒ๏ธ ๐Ÿคž ๐ŸคŸ ๐Ÿค˜ ๐Ÿค™ ๐Ÿ‘ˆ ๐Ÿ‘‰ ๐Ÿ‘† ๐Ÿ‘‡ โ˜๏ธ โœ‹ ๐Ÿคš ๐Ÿ–๏ธ ๐Ÿ–– ๐Ÿ‘‹ ๐Ÿค ๐Ÿ™ โœ๏ธ ๐Ÿ‘ ๐Ÿ™Œ ๐Ÿ‘ ๐Ÿคฒ ๐Ÿคœ ๐Ÿค› โœŠ ๐Ÿ‘Š ๐Ÿซถ - **Objetos y sรญmbolos**: ๐Ÿ”ฅ ๐Ÿ’ฏ ๐Ÿ’ซ โญ ๐ŸŒŸ โœจ โšก ๐Ÿ’ฅ ๐Ÿ’ข ๐Ÿ’จ ๐Ÿ’ฆ ๐Ÿ’ง ๐ŸŒˆ โ˜€๏ธ โ›… โ˜๏ธ ๐ŸŒง๏ธ โ›ˆ๏ธ ๐ŸŒฉ๏ธ โ„๏ธ โ˜ƒ๏ธ โ›„ ๐ŸŒช๏ธ ๐ŸŒŠ - **Comida y bebidas**: ๐ŸŽ ๐ŸŒ ๐Ÿ“ ๐Ÿ‡ ๐Ÿ‰ ๐ŸŠ ๐Ÿฅ‘ ๐Ÿ… ๐Ÿฅ• ๐ŸŒฝ ๐Ÿฅ– ๐Ÿž ๐Ÿง€ ๐Ÿฅ“ ๐Ÿ– ๐Ÿ— ๐Ÿ• ๐Ÿ” ๐ŸŒญ ๐Ÿฅช ๐ŸŒฎ ๐ŸŒฏ ๐Ÿœ ๐Ÿ ๐Ÿš ๐Ÿ› ๐Ÿค ๐Ÿฃ ๐Ÿฆ ๐Ÿฐ ๐ŸŽ‚ ๐Ÿช ๐Ÿซ ๐Ÿฌ ๐Ÿญ โ˜• ๐Ÿต ๐Ÿฅค ๐Ÿบ ๐Ÿท ๐Ÿฅ‚ ๐Ÿพ - **Animales**: ๐Ÿถ ๐Ÿฑ ๐Ÿญ ๐Ÿน ๐Ÿฐ ๐ŸฆŠ ๐Ÿป ๐Ÿผ ๐Ÿจ ๐Ÿฏ ๐Ÿฆ ๐Ÿฎ ๐Ÿท ๐Ÿธ ๐Ÿต ๐Ÿ™ˆ ๐Ÿ™‰ ๐Ÿ™Š ๐Ÿ’ ๐Ÿ” ๐Ÿง ๐Ÿฆ ๐Ÿค ๐Ÿฃ ๐Ÿฅ ๐Ÿฆ† ๐Ÿฆ… ๐Ÿฆ‰ ๐Ÿฆ‡ ๐Ÿบ ๐Ÿ— ๐Ÿด ๐Ÿฆ„ ๐Ÿ ๐Ÿ› ๐Ÿฆ‹ ๐ŸŒ ๐Ÿž ๐Ÿœ ๐Ÿฆ— ๐Ÿ•ท๏ธ ๐Ÿฆ‚ ๐Ÿข ๐Ÿ ๐ŸฆŽ ๐Ÿ™ ๐Ÿฆ‘ ๐Ÿฆ ๐Ÿฆ€ ๐Ÿก ๐Ÿ  ๐ŸŸ ๐Ÿฌ ๐Ÿณ ๐Ÿ‹ ๐Ÿฆˆ - **Actividades y deportes**: โšฝ ๐Ÿ€ ๐Ÿˆ โšพ ๐ŸฅŽ ๐ŸŽพ ๐Ÿ ๐Ÿ‰ ๐Ÿฅ ๐ŸŽฑ ๐Ÿช€ ๐Ÿ“ ๐Ÿธ ๐Ÿฅ… โ›ณ ๐Ÿช ๐Ÿน ๐ŸŽฃ ๐Ÿคฟ ๐ŸฅŠ ๐Ÿฅ‹ ๐ŸŽฝ ๐Ÿ›น ๐Ÿ›ท โ›ธ๏ธ ๐ŸฅŒ ๐ŸŽฟ โ›ท๏ธ ๐Ÿ‚ ๐Ÿช‚ ๐Ÿ‹๏ธ ๐Ÿคธ ๐Ÿคผ ๐Ÿคฝ ๐Ÿคพ ๐Ÿคน ๐Ÿง˜ ๐Ÿ›€ ๐Ÿ›Œ - **Viajes y lugares**: ๐Ÿš— ๐Ÿš• ๐Ÿš™ ๐ŸšŒ ๐ŸšŽ ๐ŸŽ๏ธ ๐Ÿš“ ๐Ÿš‘ ๐Ÿš’ ๐Ÿš ๐Ÿ›ป ๐Ÿšš ๐Ÿš› ๐Ÿšœ ๐Ÿ๏ธ ๐Ÿ›ต ๐Ÿšฒ ๐Ÿ›ด ๐Ÿš โœˆ๏ธ ๐Ÿ›ฉ๏ธ ๐Ÿš€ ๐Ÿ›ธ ๐Ÿšข โ›ต ๐Ÿšค โ›ด๏ธ ๐Ÿ›ฅ๏ธ ๐Ÿš‚ ๐Ÿšƒ ๐Ÿš„ ๐Ÿš… ๐Ÿš† ๐Ÿš‡ ๐Ÿšˆ ๐Ÿš‰ ๐ŸšŠ ๐Ÿš ๐Ÿšž ๐ŸšŸ ๐Ÿš  ๐Ÿšก ๐Ÿ›ฐ๏ธ - **Objetos y herramientas**: ๐Ÿ“ฑ ๐Ÿ’ป ๐Ÿ–ฅ๏ธ โŒจ๏ธ ๐Ÿ–ฑ๏ธ ๐Ÿ–ฒ๏ธ ๐Ÿ’ฝ ๐Ÿ’พ ๐Ÿ’ฟ ๐Ÿ“€ ๐Ÿงฎ ๐ŸŽฅ ๐Ÿ“น ๐Ÿ“ท ๐Ÿ“ธ ๐Ÿ“ผ ๐Ÿ” ๐Ÿ”Ž ๐Ÿ•ฏ๏ธ ๐Ÿ’ก ๐Ÿ”ฆ ๐Ÿฎ ๐Ÿช” ๐Ÿ“” ๐Ÿ“• ๐Ÿ“– ๐Ÿ“— ๐Ÿ“˜ ๐Ÿ“™ ๐Ÿ“š ๐Ÿ““ ๐Ÿ“’ ๐Ÿ“ƒ ๐Ÿ“œ ๐Ÿ“„ ๐Ÿ“ฐ ๐Ÿ—ž๏ธ ๐Ÿ“‘ ๐Ÿ”– ๐Ÿท๏ธ ๐Ÿ’ฐ ๐Ÿช™ ๐Ÿ’ด ๐Ÿ’ต ๐Ÿ’ถ ๐Ÿ’ท ๐Ÿ’ธ ๐Ÿ’ณ ๐Ÿงพ ๐Ÿ’Ž โš–๏ธ ๐Ÿชœ ๐Ÿงฐ ๐Ÿ”ง ๐Ÿ”จ โš’๏ธ ๐Ÿ› ๏ธ โ›๏ธ ๐Ÿช“ ๐Ÿชš ๐Ÿ”ฉ โš™๏ธ ๐Ÿชค ๐Ÿงฒ ๐Ÿชฃ ๐Ÿงฝ ๐Ÿงด ๐Ÿงท ๐Ÿงน ๐Ÿงบ ๐Ÿช‘ ๐Ÿšช ๐ŸชŸ ๐Ÿ›๏ธ ๐Ÿ›‹๏ธ ๐Ÿšฟ ๐Ÿ› ๐Ÿšฝ ๐Ÿช  ๐Ÿงป ๐Ÿชฅ ๐Ÿงผ ๐Ÿช’ ๐Ÿงฏ ๐Ÿ›’ - **Mรบsica y entretenimiento**: ๐ŸŽต ๐ŸŽถ ๐ŸŽผ ๐ŸŽน ๐Ÿฅ ๐ŸŽท ๐ŸŽบ ๐ŸŽธ ๐Ÿช• ๐ŸŽป ๐ŸŽค ๐ŸŽง ๐Ÿ“ป ๐ŸŽฌ ๐ŸŽญ ๐ŸŽช ๐ŸŽจ ๐ŸŽฏ ๐ŸŽฒ ๐ŸŽฎ ๐Ÿ•น๏ธ ๐ŸŽฐ ๐ŸŽณ - **Magia y fantasรญa**: ๐Ÿช„ ๐Ÿ”ฎ ๐Ÿงฟ ๐Ÿชฌ ๐ŸŽƒ ๐Ÿ‘ป ๐Ÿ’€ โ˜ ๏ธ ๐Ÿ‘ฝ ๐Ÿ‘พ ๐Ÿค– ๐ŸŽ… ๐Ÿคถ ๐Ÿง™ ๐Ÿงš ๐Ÿง› ๐Ÿงœ ๐Ÿง ๐Ÿงž ๐ŸงŸ ๐Ÿฆธ ๐Ÿฆน Los emojis no reconocidos se convierten automรกticamente en "emoji" genรฉrico. ## License MIT [Fixtergeek.com](https://www.fixtergeek.com)