diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml new file mode 100644 index 0000000..3470663 --- /dev/null +++ b/.github/workflows/node.yml @@ -0,0 +1,22 @@ +name: Node.js CI + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + - name: Install dependencies + run: npm ci + - name: Run Build + run: npm run build + - name: Run tests + run: npm test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b69df58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +node_modules/ +script/web-speech-recommended-voices/ +.DS_Store diff --git a/README.md b/README.md index 0b68adf..7bbb4b5 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,122 @@ Readium Speech was spun out as a separate project in order to facilitate its int For our initial work on this project, we're focusing on voice selection based on [recommended voices](https://github.com/HadrienGardeur/web-speech-recommended-voices). -The outline of this work has been explored in a [GitHub discussion](https://github.com/HadrienGardeur/web-speech-recommended-voices/discussions/9) and through a [best practices document](https://github.com/HadrienGardeur/read-aloud-best-practices/blob/main/voice-selection.md). \ No newline at end of file +The outline of this work has been explored in a [GitHub discussion](https://github.com/HadrienGardeur/web-speech-recommended-voices/discussions/9) and through a [best practices document](https://github.com/HadrienGardeur/read-aloud-best-practices/blob/main/voice-selection.md). + + +## QuickStart + +`npm install readium-speech` + +#### node : +``` +import { getVoices } from "readium-speech/build/cjs/voices"; + +const voices = await getVoices(); +console.log(voices); +``` + +#### web : +``` +import { getVoices } from "readium-speech/build/mjs/voices"; + +const voices = await getVoices(); +console.log(voices); +``` + +## API + +### Interface + +``` +export interface IVoices { + label: string; + voiceURI: string; + name: string; + language: string; + gender?: TGender | undefined; + age?: string | undefined; + offlineAvailability: boolean; + quality?: TQuality | undefined; + pitchControl: boolean; + recommendedPitch?: number | undefined; + recommendedRate?: number | undefined; +} + +export interface ILanguages { + label: string; + code: string; + count: number; +} +``` + + +#### Parse and Extract IVoices from speechSynthesis WebAPI +``` +function getVoices(preferredLanguage?: string[] | string, localization?: string): Promise +``` + +#### List languages from IVoices +``` +function getLanguages(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): ILanguages[] +``` + +#### helpers + +``` +function listLanguages(voices: IVoices[], localization?: string): ILanguages[] + +function ListRegions(voices: IVoices[], localization?: string): ILanguages[] + +function parseSpeechSynthesisVoices(speechSynthesisVoices: SpeechSynthesisVoice[]): IVoices[] + +function getSpeechSynthesisVoices(): Promise +``` + +#### groupBy + +``` +function groupByKindOfVoices(allVoices: IVoices[]): TGroupVoices + +function groupByRegions(voices: IVoices[], language: string, preferredRegions?: string[] | string, localization?: string): TGroupVoices + +function groupByLanguage(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string): TGroupVoices +``` + +#### sortBy + +``` +function sortByLanguage(voices: IVoices[], preferredLanguage?: string[] | string): IVoices[] + +function sortByRegion(voices: IVoices[], preferredRegions?: string[] | string, localization?: string | undefined): IVoices[] + +function sortByGender(voices: IVoices[], genderFirst: TGender): IVoices[] + +function sortByName(voices: IVoices[]): IVoices[] + +function sortByQuality(voices: IVoices[]): IVoices[] +``` + +#### filterOn + +``` +function filterOnRecommended(voices: IVoices[], _recommended?: IRecommended[]): TReturnFilterOnRecommended + +function filterOnVeryLowQuality(voices: IVoices[]): IVoices[] + +function filterOnNovelty(voices: IVoices[]): IVoices[] + +function filterOnQuality(voices: IVoices[], quality: TQuality | TQuality[]): IVoices[] + +function filterOnLanguage(voices: IVoices[], language: string | string[]): IVoices[] + +function filterOnGender(voices: IVoices[], gender: TGender): IVoices[] + +function filterOnGender(voices: IVoices[], gender: TGender): IVoices[] +``` + +## ressources + +https://www.sensedeep.com/blog/posts/2021/how-to-create-single-source-npm-module.html + + diff --git a/build/cjs/data.d.ts b/build/cjs/data.d.ts new file mode 100644 index 0000000..6d32589 --- /dev/null +++ b/build/cjs/data.d.ts @@ -0,0 +1,148 @@ +export declare const novelty: string[]; +export declare const veryLowQuality: string[]; +export type TGender = "female" | "male" | "nonbinary"; +export type TQuality = "veryLow" | "low" | "normal" | "high" | "veryHigh"; +export interface IRecommended { + label: string; + name: string; + altNames?: string[]; + language: string; + gender?: TGender | undefined; + age?: string | undefined; + quality: TQuality[]; + recommendedPitch?: number | undefined; + recommendedRate?: number | undefined; + localizedName: string; +} +export declare const recommended: Array; +export declare const quality: { + ca: { + normal: string; + high: string; + }; + cs: { + normal: string; + high: string; + }; + da: { + normal: string; + high: string; + }; + de: { + normal: string; + high: string; + }; + el: { + normal: string; + high: string; + }; + en: { + normal: string; + high: string; + }; + es: { + normal: string; + high: string; + }; + fi: { + normal: string; + high: string; + }; + fr: { + normal: string; + high: string; + }; + hu: { + normal: string; + high: string; + }; + hr: { + normal: string; + high: string; + }; + it: { + normal: string; + high: string; + }; + ja: { + normal: string; + high: string; + }; + ko: { + normal: string; + high: string; + }; + nb: { + normal: string; + high: string; + }; + nl: { + normal: string; + high: string; + }; + pl: { + normal: string; + high: string; + }; + pt: { + normal: string; + high: string; + }; + ro: { + normal: string; + high: string; + }; + ru: { + normal: string; + high: string; + }; + sk: { + normal: string; + high: string; + }; + sl: { + normal: string; + high: string; + }; + sv: { + normal: string; + high: string; + }; + tr: { + normal: string; + high: string; + }; + uk: { + normal: string; + high: string; + }; +}; +export declare const defaultRegion: { + bg: string; + ca: string; + cs: string; + da: string; + de: string; + en: string; + es: string; + eu: string; + fi: string; + fr: string; + gl: string; + hr: string; + hu: string; + it: string; + ja: string; + ko: string; + nb: string; + nl: string; + pl: string; + pt: string; + ro: string; + ru: string; + sk: string; + sl: string; + sv: string; + tr: string; + uk: string; +}; diff --git a/build/cjs/data.js b/build/cjs/data.js new file mode 100644 index 0000000..1a1a6a8 --- /dev/null +++ b/build/cjs/data.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.defaultRegion = exports.quality = exports.recommended = exports.veryLowQuality = exports.novelty = void 0; +exports.novelty = ["Albert", "Bad News", "Mauvaises nouvelles", "Malas noticias", "Brutte notizie", "Bahh", "Bells", "Cloches", "Campanas", "Campane", "Boing", "Bubbles", "Bulles", "Burbujas", "Bollicine", "Cellos", "Violoncelles", "Violonchelos", "Violoncelli", "Good News", "Bonnes nouvelles", "Buenas noticias", "Buone notizie", "Jester", "Bouffon", "Bufón", "Giullare", "Organ", "Orgue", "Órgano", "Organo", "Superstar", "Superestrella", "Trinoids", "Trinoïdes", "Trinoid", "Whisper", "Murmure", "Susurro", "Sussurro", "Wobble", "Zarvox"]; +exports.veryLowQuality = ["Eddy", "Flo", "Grandma", "Grandpa", "Jacques", "Reed", "Rocko", "Sandy", "Shelley", "Fred", "Junior", "Kathy", "Ralph", "eSpeak Arabic", "eSpeak Bulgarian", "eSpeak Bengali", "eSpeak Catalan", "eSpeak Chinese (Mandarin, latin as English)", "eSpeak Czech", "eSpeak Danish", "eSpeak German", "eSpeak Greek", "eSpeak Spanish (Spain)", "eSpeak Estonian", "eSpeak Finnish", "eSpeak Gujarati", "eSpeak Croatian", "eSpeak Hungarian", "eSpeak Indonesian", "eSpeak Italian", "eSpeak Kannada", "eSpeak Korean", "eSpeak Lithuanian", "eSpeak Latvian", "eSpeak Malayalm", "eSpeak Marathi", "eSpeak Malay", "eSpeak Norwegian Bokmål", "eSpeak Polish", "eSpeak Portuguese (Brazil)", "eSpeak Romanian", "eSpeak Russian", "eSpeak Slovak", "eSpeak Slovenian", "eSpeak Serbian", "eSpeak Swedish", "eSpeak Swahili", "eSpeak Tamil", "eSpeak Telugu", "eSpeak Turkish", "eSpeak Vietnamese (Northern)"]; +; +exports.recommended = [{ "label": "Kalina", "name": "Microsoft Kalina Online (Natural) - Bulgarian (Bulgaria)", "language": "bg-BG", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Borislav", "name": "Microsoft Borislav Online (Natural) - Bulgarian (Bulgaria)", "language": "bg-BG", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Daria", "name": "Daria", "language": "bg-BG", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Ivan", "name": "Microsoft Ivan - Bulgarian (Bulgaria)", "language": "bg-BG", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Женски глас", "name": "Android Speech Recognition and Synthesis from Google bg-bg-x-ifk-network", "altNames": ["Android Speech Recognition and Synthesis from Google bg-bg-x-ifk-local", "Android Speech Recognition and Synthesis from Google bg-bg-language"], "language": "bg-BG", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "български глас", "name": "български България", "language": "bg-BG", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Joana (Català)", "name": "Microsoft Joana Online (Natural) - Catalan (Spain)", "language": "ca-ES", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Enric (Català)", "name": "Microsoft Enric Online (Natural) - Catalan (Spain)", "language": "ca-ES", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Montse (Català)", "name": "Montse", "language": "ca-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Pau (Valencià)", "name": "Pau", "language": "ca-ES-u-sd-esvc", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jordi (Català)", "name": "Jordi", "language": "ca-ES", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Herena (Català)", "name": "Microsoft Herena - Catalan (Spain)", "language": "ca-ES", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Veu femenina catalana", "name": "Android Speech Recognition and Synthesis from Google ca-es-x-caf-network", "altNames": ["Android Speech Recognition and Synthesis from Google ca-es-x-caf-local", "Android Speech Recognition and Synthesis from Google ca-ES-language"], "language": "ca-ES", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Veu catalana", "name": "català Espanya", "language": "ca-ES", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Vlasta", "name": "Microsoft Vlasta Online (Natural) - Czech (Czech)", "language": "cs-CZ", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Antonin", "name": "Microsoft Antonin Online (Natural) - Czech (Czech)", "language": "cs-CZ", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Zuzana", "name": "Zuzana", "language": "cs-CZ", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Iveta", "name": "Iveta", "language": "cs-CZ", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jakub", "name": "Microsoft Jakub - Czech (Czech)", "language": "cs-CZ", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ženský hlas", "name": "Google čeština (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google cs-cz-x-jfs-network", "Chrome OS čeština", "Android Speech Recognition and Synthesis from Google cs-cz-x-jfs-local", "Android Speech Recognition and Synthesis from Google cs-CZ-language"], "language": "cs-CZ", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Český hlas", "name": "čeština Česko", "language": "cs-CZ", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Christel", "name": "Microsoft Christel Online (Natural) - Danish (Denmark)", "language": "da-DK", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jeppe", "name": "Microsoft Jeppe Online (Natural) - Danish (Denmark)", "language": "da-DK", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sara", "name": "Sara", "language": "da-DK", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Magnus", "name": "Magnus", "language": "da-DK", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Helle", "name": "Microsoft Helle - Danish (Denmark)", "language": "da-DK", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvindestemme 1", "name": "Google Dansk 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google da-dk-x-kfm-network", "Chrome OS Dansk 1", "Android Speech Recognition and Synthesis from Google da-dk-x-kfm-local", "Android Speech Recognition and Synthesis from Google da-DK-language"], "language": "da-DK", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvindestemme 2", "name": "Google Dansk 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google da-dk-x-sfp-network", "Chrome OS Dansk 3", "Android Speech Recognition and Synthesis from Google da-dk-x-sfp-local"], "language": "da-DK", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvindestemme 3", "name": "Google Dansk 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google da-dk-x-vfb-network", "Chrome OS Dansk 4", "Android Speech Recognition and Synthesis from Google da-dk-x-vfb-local"], "language": "da-DK", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mandsstemme", "name": "Google Dansk 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google da-dk-x-nmm-network", "Chrome OS Dansk 2", "Android Speech Recognition and Synthesis from Google da-dk-x-nmm-local"], "language": "da-DK", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Dansk stemme", "name": "dansk Danmark", "language": "da-DK", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Seraphina (Deutschland)", "name": "Microsoft SeraphinaMultilingual Online (Natural) - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Amala (Deutschland)", "name": "Microsoft Amala Online (Natural) - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Katja (Deutschland)", "name": "Microsoft Katja Online (Natural) - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Florian (Deutschland)", "name": "Microsoft FlorianMultilingual Online (Natural) - German (Germany)", "language": "de-DE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Conrad (Deutschland)", "name": "Microsoft Conrad Online (Natural) - German (Germany)", "language": "de-DE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Killian (Deutschland)", "name": "Microsoft Killian Online (Natural) - German (Germany)", "language": "de-DE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ingrid (Österreich)", "name": "Microsoft Ingrid Online (Natural) - German (Austria)", "language": "de-AT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jonas (Österreich)", "name": "Microsoft Jonas Online (Natural) - German (Austria)", "language": "de-AT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Leni (Schweiz)", "name": "Microsoft Leni Online (Natural) - German (Switzerland)", "language": "de-CH", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jan (Schweiz)", "name": "Microsoft Jan Online (Natural) - German (Switzerland)", "language": "de-CH", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Petra (Deutschland)", "name": "Petra", "language": "de-DE", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Anna (Deutschland)", "name": "Anna", "language": "de-DE", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Helena (Deutschland)", "name": "Helena", "language": "de-DE", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Markus (Deutschland)", "name": "Markus", "language": "de-DE", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Viktor (Deutschland)", "name": "Viktor", "language": "de-DE", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Yannick (Deutschland)", "name": "Yannick", "language": "de-DE", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Martin (Deutschland)", "name": "Martin", "language": "de-DE", "gender": "male", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Weibliche Google-Stimme (Deutschland)", "name": "Google Deutsch", "language": "de-DE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Hedda (Deutschland)", "name": "Microsoft Hedda - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Katja (Deutschland)", "name": "Microsoft Katja - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Stefan (Deutschland)", "name": "Microsoft Stefan - German (Germany)", "language": "de-DE", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Michael (Österreich)", "name": "Microsoft Michael - German (Austria)", "language": "de-AT", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Karsten (Schweiz)", "name": "Microsoft Karsten - German (Switzerland)", "language": "de-CH", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Weibliche Stimme 1 (Deutschland)", "name": "Google Deutsch 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google de-de-x-dea-network", "Chrome OS Deutsch 2", "Android Speech Recognition and Synthesis from Google de-de-x-dea-local", "Android Speech Recognition and Synthesis from Google de-DE-language"], "language": "de-DE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Weibliche Stimme 2 (Deutschland)", "name": "Google Deutsch 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google de-de-x-nfh-network", "Chrome OS Deutsch 1", "Android Speech Recognition and Synthesis from Google de-de-x-nfh-local"], "language": "de-DE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Männliche Stimme 1 (Deutschland)", "name": "Google Deutsch 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google de-de-x-deb-network", "Chrome OS Deutsch 3", "Android Speech Recognition and Synthesis from Google de-de-x-deb-local"], "language": "de-DE", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Männliche Stimme 2 (Deutschland)", "name": "Google Deutsch 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google de-de-x-deg-network", "Chrome OS Deutsch 4", "Android Speech Recognition and Synthesis from Google de-de-x-deg-local"], "language": "de-DE", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Deutsche Stimme", "name": "Deutsch Deutschland", "language": "de-DE", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Emma (US)", "name": "Microsoft EmmaMultilingual Online (Natural) - English (United States)", "altNames": ["Microsoft Emma Online (Natural) - English (United States)"], "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Microsoft Ava (US)", "name": "Microsoft AvaMultilingual Online (Natural) - English (United States)", "altNames": ["Microsoft Ava Online (Natural) - English (United States)"], "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jenny (US)", "name": "Microsoft Jenny Online (Natural) - English (United States)", "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Aria (US)", "name": "Microsoft Aria Online (Natural) - English (United States)", "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Michelle (US)", "name": "Microsoft Michelle Online (Natural) - English (United States)", "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ana (US)", "name": "Microsoft Ana Online (Natural) - English (United States)", "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Andrew (US)", "name": "Microsoft AndrewMultilingual Online (Natural) - English (United States)", "altNames": ["Microsoft Andrew Online (Natural) - English (United States)"], "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Brian (US)", "name": "Microsoft BrianMultilingual Online (Natural) - English (United States)", "altNames": ["Microsoft Brian Online (Natural) - English (United States)"], "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Guy (US)", "name": "Microsoft Guy Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Eric (US)", "name": "Microsoft Eric Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Steffan (US)", "name": "Microsoft Steffan Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Christopher (US)", "name": "Microsoft Christopher Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Roger (US)", "name": "Microsoft Roger Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sonia (UK)", "name": "Microsoft Sonia Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Libby (UK)", "name": "Microsoft Libby Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Maisie (UK)", "name": "Microsoft Maisie Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ryan (UK)", "name": "Microsoft Ryan Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Thomas (UK)", "name": "Microsoft Thomas Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Natasha (Australia)", "name": "Microsoft Natasha Online (Natural) - English (Australia)", "language": "en-AU", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "William (Australia)", "name": "Microsoft William Online (Natural) - English (Australia)", "language": "en-AU", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Clara (Canada)", "name": "Microsoft Clara Online (Natural) - English (Canada)", "language": "en-CA", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Liam (Canada)", "name": "Microsoft Liam Online (Natural) - English (Canada)", "language": "en-CA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Apple Ava (US)", "name": "Ava", "language": "en-US", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Zoe (US)", "name": "Zoe", "language": "en-US", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Allison (US)", "name": "Allison", "language": "en-US", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Nicky (US)", "name": "Nicky", "language": "en-US", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Samantha (US)", "name": "Samantha", "language": "en-US", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Joelle (US)", "name": "Joelle", "language": "en-US", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Evan (US)", "name": "Evan", "language": "en-US", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Nathan (US)", "name": "Nathan", "language": "en-US", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Tom (US)", "name": "Tom", "language": "en-US", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Alex (US)", "name": "Alex", "language": "en-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Aaron (US)", "name": "Aaron", "language": "en-US", "gender": "male", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Kate (UK)", "name": "Kate", "language": "en-GB", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Stephanie (UK)", "name": "Stephanie", "language": "en-GB", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Serena (UK)", "name": "Serena", "language": "en-GB", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Martha (UK)", "name": "Martha", "language": "en-GB", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jamie (UK)", "name": "Jamie", "language": "en-GB", "gender": "male", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Oliver (UK)", "name": "Oliver", "language": "en-GB", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Daniel (UK)", "name": "Daniel", "language": "en-GB", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Arthur (UK)", "name": "Arthur", "language": "en-GB", "gender": "male", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Matilda (Australia)", "name": "Matilda", "language": "en-AU", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Karen (Australia)", "name": "Karen", "language": "en-AU", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Catherine (Australia)", "name": "Catherine", "language": "en-AU", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Lee (Australia)", "name": "Lee", "language": "en-AU", "gender": "male", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Gordon (Australia)", "name": "Gordon", "language": "en-AU", "gender": "male", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Female Google voice (US)", "name": "Google US English", "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female Google voice (UK)", "name": "Google UK English Female", "language": "en-GB", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male Google voice (UK)", "name": "Google UK English Male", "language": "en-GB", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Zira (US)", "name": "Microsoft Zira - English (United States)", "language": "en-US", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "David (US)", "name": "Microsoft David - English (United States)", "language": "en-US", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mark (US)", "name": "Microsoft Mark - English (United States)", "language": "en-US", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Hazel (UK)", "name": "Microsoft Hazel - English (Great Britain)", "language": "en-GB", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Susan (UK)", "name": "Microsoft Susan - English (Great Britain)", "language": "en-GB", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "George (UK)", "name": "Microsoft George - English (Great Britain)", "language": "en-GB", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Catherine (Australia)", "name": "Microsoft Catherine - English (Austalia)", "language": "en-AU", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "James (Australia)", "name": "Microsoft Richard - English (Australia)", "language": "en-AU", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Linda (Canada)", "name": "Microsoft Linda - English (Canada)", "language": "en-CA", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Richard (Canada)", "name": "Microsoft Richard - English (Canada)", "language": "en-CA", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 1 (US)", "name": "Google US English 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-tpc-network", "Chrome OS US English 5", "Android Speech Recognition and Synthesis from Google en-us-x-tpc-local", "Android Speech Recognition and Synthesis from Google en-US-language"], "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 2 (US)", "name": "Google US English 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-iob-network", "Chrome OS US English 1", "Android Speech Recognition and Synthesis from Google en-us-x-iob-local"], "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 3 (US)", "name": "Google US English 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-iog-network", "Chrome OS US English 2", "Android Speech Recognition and Synthesis from Google en-us-x-iog-local"], "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 4 (US)", "name": "Google US English 7 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-tpf-network", "Chrome OS US English 7", "Android Speech Recognition and Synthesis from Google en-us-x-tpf-local"], "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 5 (US)", "name": "Android Speech Recognition and Synthesis from Google en-us-x-sfg-network", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-sfg-local"], "language": "en-US", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 6 (US)", "name": "Chrome OS US English 8", "language": "en-US", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 1 (US)", "name": "Google US English 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-iom-network", "Chrome OS US English 4", "Android Speech Recognition and Synthesis from Google en-us-x-iom-local"], "language": "en-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 2 (US)", "name": "Google US English 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-iol-network", "Chrome OS US English 3", "Android Speech Recognition and Synthesis from Google en-us-x-iol-local"], "language": "en-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 3 (US)", "name": "Google US English 6 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-tpd-network", "Chrome OS US English 6", "Android Speech Recognition and Synthesis from Google en-us-x-tpd-local"], "language": "en-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 1 (UK)", "name": "Google UK English 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gba-network", "Chrome OS UK English 2", "Android Speech Recognition and Synthesis from Google en-gb-x-gba-local", "Android Speech Recognition and Synthesis from Google en-GB-language"], "language": "en-GB", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 2 (UK)", "name": "Google UK English 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gbc-network", "Chrome OS UK English 4", "Android Speech Recognition and Synthesis from Google en-gb-x-gbc-local"], "language": "en-GB", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 3 (UK)", "name": "Google UK English 6 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gbg-network", "Chrome OS UK English 6", "Android Speech Recognition and Synthesis from Google en-gb-x-gbg-local"], "language": "en-GB", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 4 (UK)", "name": "Chrome OS UK English 7", "language": "en-GB", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 1 (UK)", "name": "Google UK English 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-rjs-network", "Chrome OS UK English 1", "Android Speech Recognition and Synthesis from Google en-gb-x-rjs-local"], "language": "en-GB", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 2 (UK)", "name": "Google UK English 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gbb-network", "Chrome OS UK English 3", "Android Speech Recognition and Synthesis from Google en-gb-x-gbb-local"], "language": "en-GB", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 3 (UK)", "name": "Google UK English 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gbd-network", "Chrome OS UK English 5", "Android Speech Recognition and Synthesis from Google en-gb-x-gbd-local"], "language": "en-GB", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 1 (Australia)", "name": "Google Australian English 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-au-x-aua-network", "Chrome OS Australian English 1", "Android Speech Recognition and Synthesis from Google en-au-x-aua-local", "Android Speech Recognition and Synthesis from Google en-AU-language"], "language": "en-AU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 2 (Australia)", "name": "Google Australian English 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-au-x-auc-network", "Chrome OS Australian English 3", "Android Speech Recognition and Synthesis from Google en-au-x-auc-local"], "language": "en-AU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 1 (Australia)", "name": "Google Australian English 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-au-x-aub-network", "Chrome OS Australian English 2", "Android Speech Recognition and Synthesis from Google en-au-x-aub-local"], "language": "en-AU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 2 (Australia)", "name": "Google Australian English 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-au-x-aud-network", "Chrome OS Australian English 4", "Android Speech Recognition and Synthesis from Google en-au-x-aud-local"], "language": "en-AU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 3 (Australia)", "name": "Chrome OS Australian English 5", "language": "en-AU", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "US voice", "name": "English United States", "language": "en-US", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "UK voice", "name": "English United Kingdom", "language": "en-GB", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Australian voice", "name": "English Australia", "language": "en-AU", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Elvira (España)", "name": "Microsoft Elvira Online (Natural) - Spanish (Spain)", "language": "es-ES", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Alvaro (España)", "name": "Microsoft Alvaro Online (Natural) - Spanish (Spain)", "language": "es-ES", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Dalia (México)", "name": "Microsoft Dalia Online (Natural) - Spanish (Mexico)", "language": "es-MX", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Microsoft Jorge (México)", "name": "Microsoft Jorge Online (Natural) - Spanish (Mexico)", "language": "es-MX", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Elena (Argentina)", "name": "Microsoft Elena Online (Natural) - Spanish (Argentina)", "language": "es-AR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tomas (Argentina)", "name": "Microsoft Tomas Online (Natural) - Spanish (Argentina)", "language": "es-AR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sofia (Bolivia)", "name": "Microsoft Sofia Online (Natural) - Spanish (Bolivia)", "language": "es-BO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Marcelo (Bolivia)", "name": "Microsoft Marcelo Online (Natural) - Spanish (Bolivia)", "language": "es-BO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Catalina (Chile)", "name": "Microsoft Catalina Online (Natural) - Spanish (Chile)", "language": "es-CL", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lorenzo (Chile)", "name": "Microsoft Lorenzo Online (Natural) - Spanish (Chile)", "language": "es-CL", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ximena (Colombia)", "name": "Microsoft Ximena Online (Natural) - Spanish (Colombia)", "language": "es-CO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Salome (Colombia)", "name": "Microsoft Salome Online (Natural) - Spanish (Colombia)", "language": "es-CO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Gonzalo (Colombia)", "name": "Microsoft Gonzalo Online (Natural) - Spanish (Colombia)", "language": "es-CO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Maria (Costa Rica)", "name": "Microsoft Maria Online (Natural) - Spanish (Costa Rica)", "language": "es-CR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Juan (Costa Rica)", "name": "Microsoft Juan Online (Natural) - Spanish (Costa Rica)", "language": "es-CR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Belkys (Cuba)", "name": "Microsoft Belkys Online (Natural) - Spanish (Cuba)", "language": "es-CU", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Manuel (Cuba)", "name": "Microsoft Manuel Online (Natural) - Spanish (Cuba)", "language": "es-CU", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Andrea (Ecuador)", "name": "Microsoft Andrea Online (Natural) - Spanish (Ecuador)", "language": "es-EC", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Luis (Ecuador)", "name": "Microsoft Luis Online (Natural) - Spanish (Ecuador)", "language": "es-EC", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lorena (El Salvador)", "name": "Microsoft Lorena Online (Natural) - Spanish (El Salvador)", "language": "es-SV", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Rodrigo (El Salvador)", "name": "Microsoft Rodrigo Online (Natural) - Spanish (El Salvador)", "language": "es-SV", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Paloma (Estados Unidos)", "name": "Microsoft Paloma Online (Natural) - Spanish (United States)", "language": "es-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Alonso (Estados Unidos)", "name": "Microsoft Alonso Online (Natural) - Spanish (United States)", "language": "es-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Marta (Guatemala)", "name": "Microsoft Marta Online (Natural) - Spanish (Guatemala)", "language": "es-GT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Andres (Guatemala)", "name": "Microsoft Andres Online (Natural) - Spanish (Guatemala)", "language": "es-GT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Teresa (Guinea Ecuatorial)", "name": "Microsoft Teresa Online (Natural) - Spanish (Equatorial Guinea)", "language": "es-GQ", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Javier (Guinea Ecuatorial)", "name": "Microsoft Javier Online (Natural) - Spanish (Equatorial Guinea)", "language": "es-GQ", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Karla (Honduras)", "name": "Microsoft Karla Online (Natural) - Spanish (Honduras)", "language": "es-HN", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Carlos (Honduras)", "name": "Microsoft Carlos Online (Natural) - Spanish (Honduras)", "language": "es-HN", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Yolanda (Nicaragua)", "name": "Microsoft Yolanda Online (Natural) - Spanish (Nicaragua)", "language": "es-NI", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Federico (Nicaragua)", "name": "Microsoft Federico Online (Natural) - Spanish (Nicaragua)", "language": "es-NI", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Margarita (Panamá)", "name": "Microsoft Margarita Online (Natural) - Spanish (Panama)", "language": "es-PA", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Roberto (Panamá)", "name": "Microsoft Roberto Online (Natural) - Spanish (Panama)", "language": "es-PA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tania (Paraguay)", "name": "Microsoft Tania Online (Natural) - Spanish (Paraguay)", "language": "es-PY", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Mario (Paraguay)", "name": "Microsoft Mario Online (Natural) - Spanish (Paraguay)", "language": "es-PY", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Camila (Perú)", "name": "Microsoft Camila Online (Natural) - Spanish (Peru)", "language": "es-PE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Alex (Perú)", "name": "Microsoft Alex Online (Natural) - Spanish (Peru)", "language": "es-PE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Karina (Puerto Rico)", "name": "Microsoft Karina Online (Natural) - Spanish (Puerto Rico)", "language": "es-PR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Victor (Puerto Rico)", "name": "Microsoft Victor Online (Natural) - Spanish (Puerto Rico)", "language": "es-PR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ramona (República Dominicana)", "name": "Microsoft Ramona Online (Natural) - Spanish (Dominican Republic)", "language": "es-DO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Emilio (República Dominicana)", "name": "Microsoft Emilio Online (Natural) - Spanish (Dominican Republic)", "language": "es-DO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Valentina (Uruguay)", "name": "Microsoft Valentina Online (Natural) - Spanish (Uruguay)", "language": "es-UY", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Mateo (Uruguay)", "name": "Microsoft Mateo Online (Natural) - Spanish (Uruguay)", "language": "es-UY", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Paola (Venezuela)", "name": "Microsoft Paola Online (Natural) - Spanish (Venezuela)", "language": "es-VE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sebastian (Venezuela)", "name": "Microsoft Sebastian Online (Natural) - Spanish (Venezuela)", "language": "es-VE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Marisol (España)", "name": "Marisol", "language": "es-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Mónica (España)", "name": "Mónica", "language": "es-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Apple Jorge (España)", "name": "Jorge", "language": "es-ES", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Angelica (México)", "name": "Angelica", "language": "es-MX", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Paulina (México)", "name": "Paulina", "language": "es-MX", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Juan (México)", "name": "Juan", "language": "es-MX", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Isabela (Argentina)", "name": "Isabela", "language": "es-AR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Diego (Argentina)", "name": "Diego", "language": "es-AR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Francesca (Chile)", "name": "Francesca", "language": "es-CL", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Soledad (Colombia)", "name": "Soledad", "language": "es-CO", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jimena (Colombia)", "name": "Jimena", "language": "es-CO", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Carlos (Colombia)", "name": "Carlos", "language": "es-CO", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Voz Google masculina (España)", "name": "Google español", "language": "es-ES", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz Google femenina (Estados Unidos)", "name": "Google español de Estados Unidos", "language": "es-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Helena (España)", "name": "Microsoft Helena - Spanish (Spain)", "language": "es-ES", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Laura (España)", "name": "Microsoft Laura - Spanish (Spain)", "language": "es-ES", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Pablo (España)", "name": "Microsoft Pablo - Spanish (Spain)", "language": "es-ES", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Sabina (México)", "name": "Microsoft Sabina - Spanish (Mexico)", "language": "es-MX", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Raul (México)", "name": "Microsoft Raul - Spanish (Mexico)", "language": "es-MX", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 1 (España)", "name": "Google español 4 (Natural)", "altNames": ["Chrome OS español 4", "Android Speech Recognition and Synthesis from Google es-es-x-eee-local", "Android Speech Recognition and Synthesis from Google es-ES-language"], "language": "es-ES", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 2 (España)", "name": "Google español 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-es-x-eea-network", "Chrome OS español 1", "Android Speech Recognition and Synthesis from Google es-es-x-eea-local"], "language": "es-ES", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 3 (España)", "name": "Google español 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-es-x-eec-network", "Chrome OS español 2", "Android Speech Recognition and Synthesis from Google es-es-x-eec-local"], "language": "es-ES", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 1 (España)", "name": "Google español 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-es-x-eed-network", "Chrome OS español 3", "Android Speech Recognition and Synthesis from Google es-es-x-eed-local"], "language": "es-ES", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 2 (España)", "name": "Google español 5 (Natural)", "altNames": ["Chrome OS español 5", "Android Speech Recognition and Synthesis from Google es-es-x-eef-local"], "language": "es-ES", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 1 (Estados Unidos)", "name": "Google español de Estados Unidos 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-us-x-esc-network", "Chrome OS español de Estados Unidos", "Android Speech Recognition and Synthesis from Google es-us-x-esc-local", "Android Speech Recognition and Synthesis from Google es-US-language"], "language": "es-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 2 (Estados Unidos)", "name": "Google español de Estados Unidos 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-us-x-sfb-network", "Android Speech Recognition and Synthesis from Google es-us-x-sfb-local"], "language": "es-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 1 (Estados Unidos)", "name": "Google español de Estados Unidos 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-us-x-esd-network", "Android Speech Recognition and Synthesis from Google es-us-x-esd-local"], "language": "es-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 2 (Estados Unidos)", "name": "Google español de Estados Unidos 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-us-x-esf-network", "Android Speech Recognition and Synthesis from Google es-us-x-esf-local"], "language": "es-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz de España", "name": "español España", "language": "es-ES", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Voz de Estados Unidos", "name": "español Estados Unidos", "language": "es-US", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Miren", "name": "Miren", "language": "eu-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Noora", "name": "Microsoft Noora Online (Natural) - Finnish (Finland)", "language": "fi-FI", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Harri", "name": "Microsoft Harri Online (Natural) - Finnish (Finland)", "language": "fi-FI", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Satu", "name": "Satu", "language": "fi-FI", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Onni", "name": "Onni", "language": "fi-FI", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Heidi", "name": "Microsoft Heidi - Finnish (Finland)", "language": "fi-FI", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Suomalainen naisääni", "name": "Google Suomi (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fi-fi-x-afi-network", "Chrome OS Suomi", "Android Speech Recognition and Synthesis from Google fi-fi-x-afi-local", "Android Speech Recognition and Synthesis from Google fi-FI-language"], "language": "fi-FI", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Suomalainen ääni", "name": "suomi Suomi", "language": "fi-FI", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Vivienne (France)", "name": "Microsoft VivienneMultilingual Online (Natural) - French (France)", "language": "fr-FR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Denise (France)", "name": "Microsoft Denise Online (Natural) - French (France)", "language": "fr-FR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Charline (Belgique)", "name": "Microsoft Charline Online (Natural) - French (Belgium)", "language": "fr-BE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ariane (Suisse)", "name": "Microsoft Ariane Online (Natural) - French (Switzerland)", "language": "fr-CH", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Eloise (France)", "name": "Microsoft Eloise Online (Natural) - French (France)", "language": "fr-FR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Remy (France)", "name": "Microsoft RemyMultilingual Online (Natural) - French (France)", "language": "fr-FR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Henri (France)", "name": "Microsoft Henri Online (Natural) - French (France)", "language": "fr-FR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Gerard (Belgique)", "name": "Microsoft Gerard Online (Natural) - French (Belgium)", "language": "fr-BE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Fabrice (Suisse)", "name": "Microsoft Fabrice Online (Natural) - French (Switzerland)", "language": "fr-CH", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sylvie (Canada)", "name": "Microsoft Sylvie Online (Natural) - French (Canada)", "language": "fr-CA", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Antoine (Canada)", "name": "Microsoft Antoine Online (Natural) - French (Canada)", "language": "fr-CA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jean (Canada)", "name": "Microsoft Jean Online (Natural) - French (Canada)", "language": "fr-CA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Thierry (Canada)", "name": "Microsoft Thierry Online (Natural) - French (Canada)", "language": "fr-CA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Audrey (France)", "name": "Audrey", "language": "fr-FR", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Aurélie (France)", "name": "Aurélie", "language": "fr-FR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 0.9, "localizedName": "apple" }, { "label": "Marie (France)", "name": "Marie", "language": "fr-FR", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Thomas (France)", "name": "Thomas", "language": "fr-FR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Aude (Belgique)", "name": "Aude", "language": "fr-BE", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Chantal (Canada)", "name": "Chantal", "language": "fr-CA", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Amélie (Canada)", "name": "Amélie", "language": "fr-CA", "gender": "female", "quality": ["low", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Nicolas (Canada)", "name": "Nicolas", "language": "fr-CA", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Voix Google féminine (France)", "name": "Google français", "language": "fr-FR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Julie (France)", "name": "Microsoft Julie - French (France)", "language": "fr-FR", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Hortence (France)", "name": "Microsoft Hortence - French (France)", "language": "fr-FR", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Paul (France)", "name": "Microsoft Paul - French (France)", "language": "fr-FR", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Caroline (Canada)", "name": "Microsoft Caroline - French (Canada)", "language": "fr-CA", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Claude (Canada)", "name": "Microsoft Claude - French (Canada)", "language": "fr-CA", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Guillaume (Suisse)", "name": "Microsoft Claude - French (Switzerland)", "language": "fr-CH", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 1 (France)", "name": "Google français 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-frc-network", "Chrome OS français 4", "Android Speech Recognition and Synthesis from Google fr-fr-x-frc-local", "Android Speech Recognition and Synthesis from Google fr-FR-language"], "language": "fr-FR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 2 (France)", "name": "Google français 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-fra-network", "Chrome OS français 2", "Android Speech Recognition and Synthesis from Google fr-fr-x-fra-local"], "language": "fr-FR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 3 (France)", "name": "Google français 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-vlf-network", "Chrome OS français 1", "Android Speech Recognition and Synthesis from Google fr-fr-x-vlf-local"], "language": "fr-FR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix masculine 1 (France)", "name": "Google français 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-frd-network", "Chrome OS français 5", "Android Speech Recognition and Synthesis from Google fr-fr-x-frd-local"], "language": "fr-FR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix masculine 2 (France)", "name": "Google français 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-frb-network", "Chrome OS français 3", "Android Speech Recognition and Synthesis from Google fr-fr-x-frb-local"], "language": "fr-FR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 1 (Canada)", "name": "Android Speech Recognition and Synthesis from Google fr-ca-x-caa-network", "altNames": ["Android Speech Recognition and Synthesis from Google fr-ca-x-caa-local", "Android Speech Recognition and Synthesis from Google fr-CA-language"], "language": "fr-CA", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 2 (Canada)", "name": "Android Speech Recognition and Synthesis from Google fr-ca-x-cac-network", "altNames": ["Android Speech Recognition and Synthesis from Google fr-ca-x-cac-local"], "language": "fr-CA", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix masculine 1 (Canada)", "name": "Android Speech Recognition and Synthesis from Google fr-ca-x-cab-network", "altNames": ["Android Speech Recognition and Synthesis from Google fr-ca-x-cab-local"], "language": "fr-CA", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix masculine 2 (Canada)", "name": "Android Speech Recognition and Synthesis from Google fr-ca-x-cad-network", "altNames": ["Android Speech Recognition and Synthesis from Google fr-ca-x-cad-local"], "language": "fr-CA", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix française", "name": "français France", "language": "fr-FR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Voix canadienne", "name": "français Canada", "language": "fr-CA", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Sabela", "name": "Microsoft Sabela Online (Natural) - Galician (Spain)", "language": "gl-ES", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Roi", "name": "Microsoft Roi Online (Natural) - Galician (Spain)", "language": "gl-ES", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Carmela", "name": "Carmela", "language": "gl-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Gabrijela", "name": "Microsoft Gabrijela Online (Natural) - Croatian (Croatia)", "language": "hr-HR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Srecko", "name": "Microsoft Srecko Online (Natural) - Croatian (Croatia)", "language": "hr-HR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lana", "name": "Lana", "altNames": ["Lana (poboljšani)", "Lana (hrvatski (Hrvatska))"], "language": "hr-HR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Matej", "name": "Microsoft Matej - Croatian (Croatia)", "language": "hr-HR", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ženski glas", "name": "Android Speech Recognition and Synthesis from Google hr-hr-x-hra-network", "altNames": ["Android Speech Recognition and Synthesis from Google hr-hr-x-hra-local"], "language": "hr-HR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Muški glas", "name": "Android Speech Recognition and Synthesis from Google hr-hr-x-hrb-network", "altNames": ["Android Speech Recognition and Synthesis from Google hr-hr-x-hrb-local", "Android Speech Recognition and Synthesis from Google hr-HR-language"], "language": "hr-HR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Hrvatski glas", "name": "hrvatski Hrvatska", "language": "hr-HR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Noemi", "name": "Microsoft Noemi Online (Natural) - Hungarian (Hungary)", "language": "hu-HU", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tamas", "name": "Microsoft Tamas Online (Natural) - Hungarian (Hungary)", "language": "hu-HU", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tünde", "name": "Tünde", "language": "hu-HU", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Szabolcs", "name": "Microsoft Szabolcs - Hungarian (Hungary)", "language": "hu-HU", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Női hang", "name": "Google Magyar (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google hu-hu-x-kfl-network", "Chrome OS Magyar", "Android Speech Recognition and Synthesis from Google hu-hu-x-kfl-local", "Android Speech Recognition and Synthesis from Google hu-HU-language"], "language": "hu-HU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Magyar hang", "name": "magyar Magyarország", "language": "hu-HU", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Elsa (Alta qualita)", "name": "Microsoft Elsa Online (Natural) - Italian (Italy)", "language": "it-IT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Isabella", "name": "Microsoft Isabella Online (Natural) - Italian (Italy)", "language": "it-IT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Giuseppe", "name": "Microsoft Giuseppe Online (Natural) - Italian (Italy)", "language": "it-IT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Diego", "name": "Microsoft Diego Online (Natural) - Italian (Italy)", "language": "it-IT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Federica", "name": "Federica", "language": "it-IT", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Emma", "name": "Emma", "language": "it-IT", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Alice", "name": "Alice", "language": "it-IT", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Paola", "name": "Paola", "language": "it-IT", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Luca", "name": "Luca", "language": "it-IT", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Voce Google femminile", "name": "Google italiano", "language": "it-IT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Elsa", "name": "Microsoft Elsa - Italian (Italy)", "language": "it-IT", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Cosimo", "name": "Microsoft Cosimo - Italian (Italy)", "language": "it-IT", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce femminile 1", "name": "Google italiano 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google it-it-x-itb-network", "Chrome OS italiano 2", "Android Speech Recognition and Synthesis from Google it-it-x-itb-local", "Android Speech Recognition and Synthesis from Google it-IT-language"], "language": "it-IT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce femminile 2", "name": "Google italiano 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google it-it-x-kda-network", "Chrome OS italiano 1", "Android Speech Recognition and Synthesis from Google it-it-x-kda-local"], "language": "it-IT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce maschile 1", "name": "Google italiano 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google it-it-x-itc-network", "Chrome OS italiano 3", "Android Speech Recognition and Synthesis from Google it-it-x-itc-local"], "language": "it-IT", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce maschile 2", "name": "Google italiano 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google it-it-x-itd-network", "Chrome OS italiano 4", "Android Speech Recognition and Synthesis from Google it-it-x-itd-local"], "language": "it-IT", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce italiana", "name": "italiano Italia", "language": "it-IT", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Nanami", "name": "Microsoft Nanami Online (Natural) - Japanese (Japan)", "language": "ja-JP", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Keita", "name": "Microsoft Keita Online (Natural) - Japanese (Japan)", "language": "ja-JP", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "O-Ren", "name": "O-Ren", "language": "ja-JP", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Kyoko", "name": "Kyoko", "language": "ja-JP", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Otoya", "name": "Otoya", "language": "ja-JP", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Hattori", "name": "Hattori", "language": "ja-JP", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Google の女性の声", "name": "Google 日本語", "language": "ja-JP", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ayumi", "name": "Microsoft Ayumi - Japanese (Japan)", "language": "ja-JP", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Haruka", "name": "Microsoft Haruka - Japanese (Japan)", "language": "ja-JP", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ichiro", "name": "Microsoft Ichiro - Japanese (Japan)", "language": "ja-JP", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "女性の声1", "name": "Google 日本語 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ja-jp-x-htm-network", "Chrome OS 日本語 1", "Android Speech Recognition and Synthesis from Google ja-jp-x-htm-local", "Android Speech Recognition and Synthesis from Google ja-JP-language"], "language": "ja-JP", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "女性の声2", "name": "Chrome OS 日本語 2", "altNames": ["Android Speech Recognition and Synthesis from Google ja-jp-x-jab-network", "Android Speech Recognition and Synthesis from Google ja-jp-x-jab-local"], "language": "ja-JP", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "男性の声1", "name": "Google 日本語 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ja-jp-x-jac-network", "Chrome OS 日本語 3", "Android Speech Recognition and Synthesis from Google ja-jp-x-jac-local"], "language": "ja-JP", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "男性の声2", "name": "Google 日本語 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ja-jp-x-jad-network", "Chrome OS 日本語 4", "Android Speech Recognition and Synthesis from Google ja-jp-x-jad-local"], "language": "ja-JP", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "日本の音声", "name": "日本語 日本", "language": "ja-JP", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "SunHi", "name": "Microsoft SunHi Online (Natural) - Korean (Korea)", "language": "ko-KR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Hyunsu", "name": "Microsoft Hyunsu Online (Natural) - Korean (Korea)", "language": "ko-KR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "InJoon", "name": "Microsoft InJoon Online (Natural) - Korean (Korea)", "language": "ko-KR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Yuna", "name": "Yuna", "language": "ko-KR", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jian", "name": "Jian", "language": "ko-KR", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Suhyun", "name": "Suhyun", "language": "ko-KR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Sora", "name": "Sora", "language": "ko-KR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Minsu", "name": "Minsu", "language": "ko-KR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Google 여성 음성", "name": "Google 한국의", "language": "ko-KR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Heami", "name": "Microsoft Heami - Korean (Korea)", "language": "ko-KR", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "여성 목소리 1", "name": "Google 한국어 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ko-kr-x-kob-network", "Chrome OS 한국어 2", "Android Speech Recognition and Synthesis from Google ko-kr-x-kob-local", "Android Speech Recognition and Synthesis from Google ko-KR-language"], "language": "ko-KR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "여성 목소리 2", "name": "Google 한국어 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ko-kr-x-ism-network", "Chrome OS 한국어 1", "Android Speech Recognition and Synthesis from Google ko-kr-x-ism-local"], "language": "ko-KR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "남성 1", "name": "Google 한국어 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ko-kr-x-koc-network", "Chrome OS 한국어 3", "Android Speech Recognition and Synthesis from Google ko-kr-x-koc-local"], "language": "ko-KR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "남성 2", "name": "Google 한국어 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ko-kr-x-kod-network", "Chrome OS 한국어 4", "Android Speech Recognition and Synthesis from Google ko-kr-x-kod-local"], "language": "ko-KR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "한국어 음성", "name": "한국어 대한민국", "language": "ko-KR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Pernille", "name": "Microsoft Pernille Online (Natural) - Norwegian (Bokmål, Norway)", "language": "nb-NO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Finn", "name": "Microsoft Finn Online (Natural) - Norwegian (Bokmål Norway)", "language": "nb-NO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Nora", "name": "Nora", "language": "nb-NO", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Henrik", "name": "Henrik", "language": "nb-NO", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jon", "name": "Microsoft Jon - Norwegian (Bokmål Norway)", "language": "nb-NO", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnestemme 1", "name": "Google Norsk Bokmål 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-cfl-network", "Chrome OS Norsk Bokmål 2", "Android Speech Recognition and Synthesis from Google nb-no-x-cfl-local", "Android Speech Recognition and Synthesis from Google nb-NO-language"], "language": "nb-NO", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnestemme 2", "name": "Google Norsk Bokmål 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-rfj-network", "Chrome OS Norsk Bokmål 1", "Android Speech Recognition and Synthesis from Google nb-no-x-rfj-local"], "language": "nb-NO", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnestemme 3", "name": "Google Norsk Bokmål 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-tfs-network", "Chrome OS Norsk Bokmål 4", "Android Speech Recognition and Synthesis from Google nb-no-x-tfs-local"], "language": "nb-NO", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannsstemme 1", "name": "Google Norsk Bokmål 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-cmj-network", "Chrome OS Norsk Bokmål 3", "Android Speech Recognition and Synthesis from Google nb-no-x-cmj-local"], "language": "nb-NO", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannsstemme 2", "name": "Google Norsk Bokmål 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-tmg-network", "Chrome OS Norsk Bokmål 5", "Android Speech Recognition and Synthesis from Google nb-no-x-tmg-local"], "language": "nb-NO", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Norsk stemme", "name": "norsk bokmål Norge", "language": "nb-NO", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Colette (Nederland)", "name": "Microsoft Colette Online (Natural) - Dutch (Netherlands)", "language": "nl-NL", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Fenna (Nederland)", "name": "Microsoft Fenna Online (Natural) - Dutch (Netherlands)", "language": "nl-NL", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Maarten (Nederland)", "name": "Microsoft Maarten Online (Natural) - Dutch (Netherlands)", "language": "nl-NL", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Dena (België)", "name": "Microsoft Dena Online (Natural) - Dutch (Belgium)", "language": "nl-BE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Arnaud (België)", "name": "Microsoft Arnaud Online (Natural) - Dutch (Belgium)", "language": "nl-BE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Claire (Nederland)", "name": "Claire", "language": "nl-NL", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Xander (Nederland)", "name": "Xander", "language": "nl-NL", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Ellen (België)", "name": "Ellen", "language": "nl-BE", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Google mannelijke stem (Nederland)", "name": "Google Nederlands", "language": "nl-NL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Frank (Nederland)", "name": "Microsoft Frank - Dutch (Netherlands)", "language": "nl-NL", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Vrouwelijke stem 1 (Nederland)", "name": "Google Nederlands 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-lfc-network", "Chrome OS Nederlands 4", "Android Speech Recognition and Synthesis from Google nl-nl-x-lfc-local", "Android Speech Recognition and Synthesis from Google nl-NL-language"], "language": "nl-NL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Vrouwelijke stem 2 (Nederland)", "name": "Google Nederlands 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-tfb-network", "Chrome OS Nederlands 1", "Android Speech Recognition and Synthesis from Google nl-nl-x-tfb-local"], "language": "nl-NL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Vrouwelijke stem 3 (Nederland)", "name": "Google Nederlands 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-yfr-network", "Chrome OS Nederlands 5", "Android Speech Recognition and Synthesis from Google nl-nl-x-yfr-local"], "language": "nl-NL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannelijke stem 1 (Nederland)", "name": "Google Nederlands 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-bmh-network", "Chrome OS Nederlands 2", "Android Speech Recognition and Synthesis from Google nl-nl-x-bmh-local"], "language": "nl-NL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannelijke stem 2 (Nederland)", "name": "Google Nederlands 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-dma-network", "Chrome OS Nederlands 3", "Android Speech Recognition and Synthesis from Google nl-nl-x-dma-local"], "language": "nl-NL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Vrouwelijke stem (België)", "name": "Android Speech Recognition and Synthesis from Google nl-be-x-bec-network", "altNames": ["Android Speech Recognition and Synthesis from Google nl-be-x-bec-local", "Android Speech Recognition and Synthesis from Google nl-BE-language"], "language": "nl-NL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannelijke stem (België)", "name": "Android Speech Recognition and Synthesis from Google nl-be-x-bed-network", "altNames": ["Android Speech Recognition and Synthesis from Google nl-be-x-bed-local"], "language": "nl-NL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Nederlandse stem (Nederland)", "name": "Nederlands Nederland", "language": "nl-NL", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Nederlandse stem (België)", "name": "Nederlands België", "language": "nl-BE", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Zofia", "name": "Microsoft Zofia Online (Natural) - Polish (Poland)", "language": "pl-PL", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Marek", "name": "Microsoft Marek Online (Natural) - Polish (Poland)", "language": "pl-PL", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ewa", "name": "Ewa", "language": "pl-PL", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Zosia", "name": "Zosia", "language": "pl-PL", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Krzysztof", "name": "Krzysztof", "language": "pl-PL", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Żeński głos Google’a", "name": "Google polski", "language": "pl-PL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Paulina", "name": "Microsoft Paulina - Polish (Poland)", "language": "pl-PL", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Adam", "name": "Microsoft Adam - Polish (Poland)", "language": "pl-PL", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos żeński 1", "name": "Google Polski 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-afb-network", "Chrome OS Polski 2", "Android Speech Recognition and Synthesis from Google pl-pl-x-afb-local", "Android Speech Recognition and Synthesis from Google pl-PL-language"], "language": "pl-PL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos żeński 2", "name": "Google Polski 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-oda-network", "Chrome OS Polski 1", "Android Speech Recognition and Synthesis from Google pl-pl-x-oda-local"], "language": "pl-PL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos żeński 3", "name": "Google Polski 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-zfg-network", "Chrome OS Polski 5", "Android Speech Recognition and Synthesis from Google pl-pl-x-zfg-local"], "language": "pl-PL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos męski 1", "name": "Google Polski 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-bmg-network", "Chrome OS Polski 3", "Android Speech Recognition and Synthesis from Google pl-pl-x-bmg-local"], "language": "pl-PL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos męski 2", "name": "Google Polski 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-jmk-network", "Chrome OS Polski 4", "Android Speech Recognition and Synthesis from Google pl-pl-x-jmk-local"], "language": "pl-PL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos polski", "name": "polski Polska", "language": "pl-PL", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Raquel (Portugal)", "name": "Microsoft Raquel Online (Natural) - Portuguese (Portugal)", "language": "pt-PT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Duarte (Portugal)", "name": "Microsoft Duarte Online (Natural) - Portuguese (Portugal)", "language": "pt-PT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Francisca (Brasil)", "name": "Microsoft Francisca Online (Natural) - Portuguese (Brazil)", "language": "pt-BR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Thalita (Brasil)", "name": "Microsoft Thalita Online (Natural) - Portuguese (Brazil)", "language": "pt-BR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Antonio (Brasil)", "name": "Microsoft Antonio Online (Natural) - Portuguese (Brazil)", "language": "pt-BR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Catarina (Portugal)", "name": "Catarina", "language": "pt-PT", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Joana (Portugal)", "name": "Joana", "language": "pt-PT", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Joaquim (Portugal)", "name": "Joaquim", "language": "pt-PT", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Fernanda (Brasil)", "name": "Fernanda", "language": "pt-BR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Luciana (Brasil)", "name": "Luciana", "language": "pt-BR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Felipe (Brasil)", "name": "Felipe", "language": "pt-BR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Voz Google (Brasil)", "name": "Google português do Brasil", "language": "pt-BR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Helia (Portugal)", "name": "Microsoft Helia - Portuguese (Portugal)", "language": "pt-PT", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Maria (Brasil)", "name": "Microsoft Maria - Portuguese (Brazil)", "language": "pt-BR", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Daniel (Brasil)", "name": "Microsoft Daniel - Portuguese (Brazil)", "language": "pt-BR", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz feminina 1 (Portugal)", "name": "Google português de Portugal 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-pt-x-jfb-network", "Android Speech Recognition and Synthesis from Google pt-pt-x-jfb-local", "Android Speech Recognition and Synthesis from Google pt-PT-language"], "language": "pt-PT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz feminina 2 (Portugal)", "name": "Google português de Portugal 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-pt-x-sfs-network", "Chrome OS português de Portugal", "Android Speech Recognition and Synthesis from Google pt-pt-x-sfs-local"], "language": "pt-PT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 1 (Portugal)", "name": "Google português de Portugal 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-pt-x-jmn-network", "Android Speech Recognition and Synthesis from Google pt-pt-x-jmn-local"], "language": "pt-PT", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 2 (Portugal)", "name": "Google português de Portugal 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-pt-x-pmj-network", "Android Speech Recognition and Synthesis from Google pt-pt-x-pmj-local"], "language": "pt-PT", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz feminina 1 (Brasil)", "name": "Google português do Brasil 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-br-x-afs-network", "Chrome OS português do Brasil", "Android Speech Recognition and Synthesis from Google pt-br-x-afs-local", "Android Speech Recognition and Synthesis from Google pt-BR-language"], "language": "pt-BR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz feminina 2 (Brasil)", "name": "Google português do Brasil 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-br-x-pte-network", "Android Speech Recognition and Synthesis from Google pt-br-x-pte-local"], "language": "pt-BR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina (Brasil)", "name": "Google português do Brasil 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-br-x-ptd-network", "Android Speech Recognition and Synthesis from Google pt-br-x-ptd-local"], "language": "pt-BR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz do Portugal", "name": "português Portugal", "language": "pt-PT", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Voz do Brasil", "name": "português Brasil", "language": "pt-BR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Alina", "name": "Microsoft Alina Online (Natural) - Romanian (Romania)", "language": "ro-RO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Emil", "name": "Microsoft Emil Online (Natural) - Romanian (Romania)", "language": "ro-RO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Iona", "name": "Iona", "language": "ro-RO", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Andrei", "name": "Microsoft Andrei - Romanian (Romania)", "language": "ro-RO", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce feminină", "name": "Android Speech Recognition and Synthesis from Google ro-ro-x-vfv-network", "altNames": ["Android Speech Recognition and Synthesis from Google ro-ro-x-vfv-local", "Android Speech Recognition and Synthesis from Google ro-RO-language"], "language": "ro-RO", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce românească", "name": "română România", "language": "ro-RO", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Svetlana", "name": "Microsoft Svetlana Online (Natural) - Russian (Russia)", "language": "ru-RU", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Dmitry", "name": "Microsoft Dmitry Online (Natural) - Russian (Russia)", "language": "ru-RU", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Katya", "name": "Katya", "language": "ru-RU", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Milena", "name": "Milena", "language": "ru-RU", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Yuri", "name": "Yuri", "language": "ru-RU", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Google женский голос", "name": "Google русский", "language": "ru-RU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Irina", "name": "Microsoft Irina - Russian (Russian)", "language": "ru-RU", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Pavel", "name": "Microsoft Pavel - Russian (Russian)", "language": "ru-RU", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Женский голос 1", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-dfc-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-dfc-local"], "language": "ru-RU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Женский голос 2", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-ruc-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-ruc-local"], "language": "ru-RU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Женский голос 3", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-rue-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-rue-local"], "language": "ru-RU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Мужской голос 1", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-rud-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-rud-local"], "language": "ru-RU", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Мужской голос 2", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-ruf-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-ruf-local"], "language": "ru-RU", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Русский голос", "name": "русский Россия", "language": "ru-RU", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Viktoria", "name": "Microsoft Viktoria Online (Natural) - Slovak (Slovakia)", "language": "sk-SK", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lukas", "name": "Microsoft Lukas Online (Natural) - Slovak (Slovakia)", "language": "sk-SK", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Laura", "name": "Laura", "language": "sk-SK", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Filip", "name": "Microsoft Filip - Slovak (Slovakia)", "language": "sk-SK", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ženský hlas", "name": "Google Slovenčina (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sk-sk-x-sfk-network", "Android Speech Recognition and Synthesis from Google sk-sk-x-sfk-local", "Chrome OS Slovenčina", "Android Speech Recognition and Synthesis from Google sk-SK-language"], "language": "sk-SK", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Slovenský hlas", "name": "slovenčina Slovensko", "language": "sk-SK", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Petra", "name": "Microsoft Petra Online (Natural) - Slovenian (Slovenia)", "language": "sl-SI", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Rok", "name": "Microsoft Rok Online (Natural) - Slovenian (Slovenia)", "language": "sl-SI", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tina", "name": "Tina", "language": "sl-SI", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Lado", "name": "Microsoft Lado - Slovenian (Slovenia)", "language": "sl-SI", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ženski glas", "name": "Android Speech Recognition and Synthesis from Google sl-si-x-frm-local", "altNames": ["Android Speech Recognition and Synthesis from Google sl-SI-language"], "language": "sl-SI", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Sofie", "name": "Microsoft Sofie Online (Natural) - Swedish (Sweden)", "language": "sv-SE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Mattias", "name": "Microsoft Mattias Online (Natural) - Swedish (Sweden)", "language": "sv-SE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Klara", "name": "Klara", "language": "sv-SE", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Alva", "name": "Alva", "language": "sv-SE", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Oskar", "name": "Oskar", "language": "sv-SE", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Bengt", "name": "Microsoft Bengt - Swedish (Sweden)", "language": "sv-SE", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnlig röst 1", "name": "Google Svenska 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-lfs-network", "Chrome OS Svenska", "Android Speech Recognition and Synthesis from Google sv-se-x-lfs-local", "Android Speech Recognition and Synthesis from Google sv-SE-language"], "language": "sv-SE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnlig röst 2", "name": "Google Svenska 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-afp-network", "Android Speech Recognition and Synthesis from Google sv-se-x-afp-local"], "language": "sv-SE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnlig röst 3", "name": "Google Svenska 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-cfg-network", "Android Speech Recognition and Synthesis from Google sv-se-x-cfg-local"], "language": "sv-SE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mansröst 1", "name": "Google Svenska 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-cmh-network", "Android Speech Recognition and Synthesis from Google sv-se-x-cmh-local"], "language": "sv-SE", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mansröst 2", "name": "Google Svenska 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-dmc-network", "Android Speech Recognition and Synthesis from Google sv-se-x-dmc-local"], "language": "sv-SE", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Svensk röst", "name": "svenska Sverige", "language": "sv-SE", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Emel", "name": "Microsoft Emel Online (Natural) - Turkish (Turkey)", "language": "tr-TR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ahmet", "name": "Microsoft Ahmet Online (Natural) - Turkish (Turkey)", "language": "tr-TR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Yelda", "name": "Yelda", "altNames": ["Yelda (Geliştirilmiş)", "Yelda (Türkçe (Türkiye))"], "language": "tr-TR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Cem", "name": "Cem", "language": "tr-TR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Tolga", "name": "Microsoft Tolga - Turkish (Turkey)", "language": "tr-TR", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kadın sesi 1", "name": "Google Türkçe 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-cfs-network", "Chrome OS Türkçe 3", "Android Speech Recognition and Synthesis from Google tr-tr-x-cfs-local", "Android Speech Recognition and Synthesis from Google tr-TR-language"], "language": "tr-TR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kadın sesi 2", "name": "Google Türkçe 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-efu-network", "Chrome OS Türkçe 4", "Android Speech Recognition and Synthesis from Google tr-tr-x-efu-local"], "language": "tr-TR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kadın sesi 3", "name": "Google Türkçe 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-mfm-network", "Chrome OS Türkçe 1", "Android Speech Recognition and Synthesis from Google tr-tr-x-mfm-local"], "language": "tr-TR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Erkek sesi 1", "name": "Google Türkçe 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-ama-network", "Chrome OS Türkçe 2", "Android Speech Recognition and Synthesis from Google tr-tr-x-ama-local"], "language": "tr-TR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Erkek sesi 2", "name": "Google Türkçe 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-tmc-network", "Chrome OS Türkçe 5", "Android Speech Recognition and Synthesis from Google tr-tr-x-tmc-local"], "language": "tr-TR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Türkçe ses", "name": "Türkçe Türkiye", "language": "tr-TR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Polina", "name": "Microsoft Polina Online (Natural) - Ukrainian (Ukraine)", "language": "uk-UA", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ostap", "name": "Microsoft Ostap Online (Natural) - Ukrainian (Ukraine)", "language": "uk-UA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lesya", "name": "Lesya", "language": "uk-UA", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Жіночий голос", "name": "Google українська (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google uk-ua-x-hfd-network", "Chrome OS українська", "Android Speech Recognition and Synthesis from Google uk-ua-x-hfd-local", "Android Speech Recognition and Synthesis from Google uk-UA-language"], "language": "uk-UA", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Український голос", "name": "українська Україна", "language": "uk-UA", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }]; +exports.quality = { "ca": { "normal": "millorada", "high": "prèmium" }, "cs": { "normal": "vylepšená verze", "high": "prémiový" }, "da": { "normal": "forbedret", "high": "høj kvalitet" }, "de": { "normal": "erweitert", "high": "premium" }, "el": { "normal": "βελτιωμένη", "high": "υψηλής ποιότητας" }, "en": { "normal": "Enhanced", "high": "Premium" }, "es": { "normal": "mejorada", "high": "premium" }, "fi": { "normal": "parannettu", "high": "korkealaatuinen" }, "fr": { "normal": "premium", "high": "de qualité" }, "hu": { "normal": "továbbfejlesztett", "high": "prémium" }, "hr": { "normal": "poboljšani", "high": "vrhunski" }, "it": { "normal": "ottimizzata", "high": "premium" }, "ja": { "normal": "拡張", "high": "プレミアム" }, "ko": { "normal": "고품질", "high": "프리미엄" }, "nb": { "normal": "forbedret", "high": "premium" }, "nl": { "normal": "verbeterd", "high": "premium" }, "pl": { "normal": "rozszerzony", "high": "premium" }, "pt": { "normal": "melhorada", "high": "premium" }, "ro": { "normal": "îmbunătățită", "high": "premium" }, "ru": { "normal": "улучшенный", "high": "высшее качество" }, "sk": { "normal": "vylepšený", "high": "prémiový" }, "sl": { "normal": "izboljšano", "high": "prvovrsten" }, "sv": { "normal": "förbättrad", "high": "premium" }, "tr": { "normal": "Geliştirilmiş", "high": "Yüksek Kaliteli" }, "uk": { "normal": "вдосконалений", "high": "високої якості" } }; +exports.defaultRegion = { "bg": "bg-BG", "ca": "ca-ES", "cs": "cs-CZ", "da": "da-DK", "de": "de-DE", "en": "en-US", "es": "es-ES", "eu": "eu-ES", "fi": "fi-FI", "fr": "fr-FR", "gl": "gl-ES", "hr": "hr-HR", "hu": "hu-HU", "it": "it-IT", "ja": "ja-JP", "ko": "ko-KR", "nb": "nb-NO", "nl": "nl-NL", "pl": "pl-PL", "pt": "pt-BR", "ro": "ro-RO", "ru": "ru-RU", "sk": "sk-SK", "sl": "sl-SI", "sv": "sv-SE", "tr": "tr-TR", "uk": "uk-UA" }; diff --git a/build/cjs/index.d.ts b/build/cjs/index.d.ts new file mode 100644 index 0000000..95a6baf --- /dev/null +++ b/build/cjs/index.d.ts @@ -0,0 +1 @@ +export * as voicesSelection from "./voices.js"; diff --git a/build/cjs/index.js b/build/cjs/index.js new file mode 100644 index 0000000..899a986 --- /dev/null +++ b/build/cjs/index.js @@ -0,0 +1,27 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.voicesSelection = void 0; +exports.voicesSelection = __importStar(require("./voices.js")); diff --git a/build/cjs/package.json b/build/cjs/package.json new file mode 100644 index 0000000..1cd945a --- /dev/null +++ b/build/cjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/build/cjs/voices.d.ts b/build/cjs/voices.d.ts new file mode 100644 index 0000000..3d766f4 --- /dev/null +++ b/build/cjs/voices.d.ts @@ -0,0 +1,46 @@ +import { TGender, TQuality, IRecommended } from "./data.js"; +export interface IVoices { + label: string; + voiceURI: string; + name: string; + language: string; + gender?: TGender | undefined; + age?: string | undefined; + offlineAvailability: boolean; + quality?: TQuality | undefined; + pitchControl: boolean; + recommendedPitch?: number | undefined; + recommendedRate?: number | undefined; +} +export declare function getSpeechSynthesisVoices(): Promise; +export declare function parseSpeechSynthesisVoices(speechSynthesisVoices: SpeechSynthesisVoice[]): IVoices[]; +export declare function filterOnOfflineAvailability(voices: IVoices[], offline?: boolean): IVoices[]; +export declare function filterOnGender(voices: IVoices[], gender: TGender): IVoices[]; +export declare function filterOnLanguage(voices: IVoices[], language: string | string[]): IVoices[]; +export declare function filterOnQuality(voices: IVoices[], quality: TQuality | TQuality[]): IVoices[]; +export declare function filterOnNovelty(voices: IVoices[]): IVoices[]; +export declare function filterOnVeryLowQuality(voices: IVoices[]): IVoices[]; +export type TReturnFilterOnRecommended = [voicesRecommended: IVoices[], voicesLowerQuality: IVoices[]]; +export declare function filterOnRecommended(voices: IVoices[], _recommended?: IRecommended[]): TReturnFilterOnRecommended; +export declare function sortByQuality(voices: IVoices[]): IVoices[]; +export declare function sortByName(voices: IVoices[]): IVoices[]; +export declare function sortByGender(voices: IVoices[], genderFirst: TGender): IVoices[]; +export declare function sortByLanguage(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): IVoices[]; +export declare function sortByRegion(voices: IVoices[], preferredRegions?: string[] | string, localization?: string | undefined): IVoices[]; +export interface ILanguages { + label: string; + code: string; + count: number; +} +export declare function listLanguages(voices: IVoices[], localization?: string | undefined): ILanguages[]; +export declare function listRegions(voices: IVoices[], localization?: string | undefined): ILanguages[]; +export type TGroupVoices = Map; +export declare function groupByLanguages(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): TGroupVoices; +export declare function groupByRegions(voices: IVoices[], preferredRegions?: string[] | string, localization?: string | undefined): TGroupVoices; +export declare function groupByKindOfVoices(allVoices: IVoices[]): TGroupVoices; +export declare function getLanguages(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): ILanguages[]; +/** + * Parse and extract SpeechSynthesisVoices, + * @returns IVoices[] + */ +export declare function getVoices(preferredLanguage?: string[] | string, localization?: string): Promise; diff --git a/build/cjs/voices.js b/build/cjs/voices.js new file mode 100644 index 0000000..8348a0e --- /dev/null +++ b/build/cjs/voices.js @@ -0,0 +1,449 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getSpeechSynthesisVoices = getSpeechSynthesisVoices; +exports.parseSpeechSynthesisVoices = parseSpeechSynthesisVoices; +exports.filterOnOfflineAvailability = filterOnOfflineAvailability; +exports.filterOnGender = filterOnGender; +exports.filterOnLanguage = filterOnLanguage; +exports.filterOnQuality = filterOnQuality; +exports.filterOnNovelty = filterOnNovelty; +exports.filterOnVeryLowQuality = filterOnVeryLowQuality; +exports.filterOnRecommended = filterOnRecommended; +exports.sortByQuality = sortByQuality; +exports.sortByName = sortByName; +exports.sortByGender = sortByGender; +exports.sortByLanguage = sortByLanguage; +exports.sortByRegion = sortByRegion; +exports.listLanguages = listLanguages; +exports.listRegions = listRegions; +exports.groupByLanguages = groupByLanguages; +exports.groupByRegions = groupByRegions; +exports.groupByKindOfVoices = groupByKindOfVoices; +exports.getLanguages = getLanguages; +exports.getVoices = getVoices; +const data_js_1 = require("./data.js"); +// export type TOS = 'Android' | 'ChromeOS' | 'iOS' | 'iPadOS' | 'macOS' | 'Windows'; +// export type TBrowser = 'ChromeDesktop' | 'Edge' | 'Firefox' | 'Safari'; +const navigatorLanguages = () => { var _a; return ((_a = window === null || window === void 0 ? void 0 : window.navigator) === null || _a === void 0 ? void 0 : _a.languages) || []; }; +const navigatorLang = () => ((navigator === null || navigator === void 0 ? void 0 : navigator.language) || "").split("-")[0].toLowerCase(); +const normalQuality = Object.values(data_js_1.quality).map(({ normal }) => normal); +const highQuality = Object.values(data_js_1.quality).map(({ high }) => high); +function compareQuality(a, b) { + const qualityToNumber = (quality) => { + switch (quality) { + case "veryLow": { + return 0; + } + case "low": { + return 1; + } + case "normal": { + return 2; + } + case "high": { + return 3; + } + case "veryHigh": { + return 4; + } + default: + { + return -1; + } + ; + } + }; + return qualityToNumber(b || "low") - qualityToNumber(a || "low"); +} +; +function getSpeechSynthesisVoices() { + return __awaiter(this, void 0, void 0, function* () { + const a = () => speechSynthesis.getVoices(); + const voices = a(); + if (Array.isArray(voices) && voices.length) + return voices; + return new Promise((resolve, _reject) => { + let counter = 1000; + const tick = () => { + if (counter < 1) + return resolve([]); + // console.log(counter); + --counter; + const voices = a(); + if (Array.isArray(voices) && voices.length) + return resolve(voices); + setTimeout(tick, 10); + }; + setTimeout(tick, 10); + }); + }); +} +function parseSpeechSynthesisVoices(speechSynthesisVoices) { + const parseAndFormatBCP47 = (lang) => { + const speechVoiceLang = lang.replace("_", "-"); + if (/\w{2,3}-\w{2,3}/.test(speechVoiceLang)) { + return `${speechVoiceLang.split("-")[0].toLowerCase()}-${speechVoiceLang.split("-")[1].toUpperCase()}`; + } + // bad formated !? + return lang; + }; + return speechSynthesisVoices.map((speechVoice) => ({ + label: speechVoice.name, + voiceURI: speechVoice.voiceURI, + name: speechVoice.name, + language: parseAndFormatBCP47(speechVoice.lang), + gender: undefined, + age: undefined, + offlineAvailability: speechVoice.localService, + quality: undefined, + pitchControl: true, + recommendedPitch: undefined, + recomendedRate: undefined, + })); +} +function filterOnOfflineAvailability(voices, offline = true) { + return voices.filter(({ offlineAvailability }) => { + return offlineAvailability === offline; + }); +} +function filterOnGender(voices, gender) { + return voices.filter(({ gender: voiceGender }) => { + return voiceGender === gender; + }); +} +function filterOnLanguage(voices, language) { + language = Array.isArray(language) ? language : [language]; + language = language.map((l) => extractLangRegionFromBCP47(l)[0]); + return voices.filter(({ language: voiceLanguage }) => { + const [lang] = extractLangRegionFromBCP47(voiceLanguage); + return language.includes(lang); + }); +} +function filterOnQuality(voices, quality) { + quality = Array.isArray(quality) ? quality : [quality]; + return voices.filter(({ quality: voiceQuality }) => { + return quality.some((qual) => qual === voiceQuality); + }); +} +function filterOnNovelty(voices) { + return voices.filter(({ name }) => { + return !data_js_1.novelty.includes(name); + }); +} +function filterOnVeryLowQuality(voices) { + return voices.filter(({ name }) => { + return !data_js_1.veryLowQuality.find((v) => name.startsWith(v)); + }); +} +function updateVoiceInfo(recommendedVoice, voice) { + voice.label = recommendedVoice.label; + voice.gender = recommendedVoice.gender; + voice.recommendedPitch = recommendedVoice.recommendedPitch; + voice.recommendedRate = recommendedVoice.recommendedRate; + return voice; +} +function filterOnRecommended(voices, _recommended = data_js_1.recommended) { + const voicesRecommended = []; + const voicesLowerQuality = []; + recommendedVoiceLoop: for (const recommendedVoice of _recommended) { + if (Array.isArray(recommendedVoice.quality) && recommendedVoice.quality.length > 1) { + const voicesFound = voices.filter(({ name }) => name.startsWith(recommendedVoice.name)); + if (voicesFound.length) { + for (const qualityTested of ["high", "normal"]) { + for (let i = 0; i < voicesFound.length; i++) { + const voice = voicesFound[i]; + const rxp = /^.*\((.*)\)$/; + if (rxp.test(voice.name)) { + const res = rxp.exec(voice.name); + const maybeQualityString = res ? res[1] || "" : ""; + const qualityDataArray = qualityTested === "high" ? highQuality : normalQuality; + if (recommendedVoice.quality.includes(qualityTested) && qualityDataArray.includes(maybeQualityString)) { + voice.quality = qualityTested; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + voicesFound.splice(i, 1); + voicesLowerQuality.push(...(voicesFound.map((v) => { + v.quality = "low"; // Todo need to be more precise for 'normal' quality voices + return updateVoiceInfo(recommendedVoice, v); + }))); + continue recommendedVoiceLoop; + } + } + } + } + const voice = voicesFound[0]; + for (let i = 1; i < voicesFound.length; i++) { + voicesLowerQuality.push(voicesFound[i]); + } + voice.quality = voicesFound.length > 3 ? "veryHigh" : voicesFound.length > 2 ? "high" : "normal"; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + } + } + else if (Array.isArray(recommendedVoice.altNames) && recommendedVoice.altNames.length) { + const voiceFound = voices.find(({ name }) => name === recommendedVoice.name); + if (voiceFound) { + const voice = voiceFound; + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + // voice Name found so altNames array must be filter and push to voicesLowerQuality + const altNamesVoicesFound = voices.filter(({ name }) => recommendedVoice.altNames.includes(name)); + // TODO: Typescript bug type assertion doesn't work, need to force the compiler with the Non-null Assertion Operator + voicesLowerQuality.push(...(altNamesVoicesFound.map((v) => { + v.quality = recommendedVoice.quality[0]; + return updateVoiceInfo(recommendedVoice, v); + }))); + } + else { + // filter voices on altNames, keep the first and push the remaining to voicesLowerQuality + const altNamesVoicesFound = voices.filter(({ name }) => recommendedVoice.altNames.includes(name)); + if (altNamesVoicesFound.length) { + const voice = altNamesVoicesFound.shift(); + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + voicesLowerQuality.push(...(altNamesVoicesFound.map((v) => { + v.quality = recommendedVoice.quality[0]; + return updateVoiceInfo(recommendedVoice, v); + }))); + } + } + } + else { + const voiceFound = voices.find(({ name }) => name === recommendedVoice.name); + if (voiceFound) { + const voice = voiceFound; + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + } + } + } + return [voicesRecommended, voicesLowerQuality]; +} +const extractLangRegionFromBCP47 = (l) => { var _a; return [l.split("-")[0].toLowerCase(), (_a = l.split("-")[1]) === null || _a === void 0 ? void 0 : _a.toUpperCase()]; }; +function sortByQuality(voices) { + return voices.sort(({ quality: qa }, { quality: qb }) => { + return compareQuality(qa, qb); + }); +} +function sortByName(voices) { + return voices.sort(({ name: na }, { name: nb }) => { + return na.localeCompare(nb); + }); +} +function sortByGender(voices, genderFirst) { + return voices.sort(({ gender: ga }, { gender: gb }) => { + return ga === gb ? 0 : ga === genderFirst ? -1 : gb === genderFirst ? -1 : 1; + }); +} +function orderByPreferredLanguage(preferredLanguage) { + preferredLanguage = Array.isArray(preferredLanguage) ? preferredLanguage : + preferredLanguage ? [preferredLanguage] : []; + return [...(new Set([...preferredLanguage, ...navigatorLanguages()]))]; +} +function orderByPreferredRegion(preferredLanguage) { + preferredLanguage = Array.isArray(preferredLanguage) ? preferredLanguage : + preferredLanguage ? [preferredLanguage] : []; + const regionByDefaultArray = Object.values(data_js_1.defaultRegion); + return [...(new Set([...preferredLanguage, ...navigatorLanguages(), ...regionByDefaultArray]))]; +} +const getLangFromBCP47Array = (a) => { + return [...(new Set(a.map((v) => extractLangRegionFromBCP47(v)[0]).filter((v) => !!v)))]; +}; +const getRegionFromBCP47Array = (a) => { + return [...(new Set(a.map((v) => (extractLangRegionFromBCP47(v)[1] || "").toUpperCase()).filter((v) => !!v)))]; +}; +function sortByLanguage(voices, preferredLanguage = [], localization = navigatorLang()) { + const languages = getLangFromBCP47Array(orderByPreferredLanguage(preferredLanguage)); + const voicesSorted = []; + for (const lang of languages) { + voicesSorted.push(...voices.filter(({ language: voiceLanguage }) => lang === extractLangRegionFromBCP47(voiceLanguage)[0])); + } + let langueName = undefined; + if (localization) { + try { + langueName = new Intl.DisplayNames([localization], { type: 'language' }); + } + catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + const remainingVoices = voices.filter((v) => !voicesSorted.includes(v)); + remainingVoices.sort(({ language: a }, { language: b }) => { + let nameA = a, nameB = b; + try { + if (langueName) { + nameA = langueName.of(extractLangRegionFromBCP47(a)[0]) || a; + nameB = langueName.of(extractLangRegionFromBCP47(b)[0]) || b; + } + } + catch (e) { + // ignore + } + return nameA.localeCompare(nameB); + }); + return [...voicesSorted, ...remainingVoices]; +} +function sortByRegion(voices, preferredRegions = [], localization = navigatorLang()) { + const regions = getRegionFromBCP47Array(orderByPreferredRegion(preferredRegions)); + const voicesSorted = []; + for (const reg of regions) { + voicesSorted.push(...voices.filter(({ language: voiceLanguage }) => reg === extractLangRegionFromBCP47(voiceLanguage)[1])); + } + let regionName = undefined; + if (localization) { + try { + regionName = new Intl.DisplayNames([localization], { type: 'region' }); + } + catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + const remainingVoices = voices.filter((v) => !voicesSorted.includes(v)); + remainingVoices.sort(({ language: a }, { language: b }) => { + let nameA = a, nameB = b; + try { + if (regionName) { + nameA = regionName.of(extractLangRegionFromBCP47(a)[1]) || a; + nameB = regionName.of(extractLangRegionFromBCP47(b)[1]) || b; + } + } + catch (e) { + // ignore + } + return nameA.localeCompare(nameB); + }); + return [...voicesSorted, ...remainingVoices]; +} +function listLanguages(voices, localization = navigatorLang()) { + let langueName = undefined; + if (localization) { + try { + langueName = new Intl.DisplayNames([localization], { type: 'language' }); + } + catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + return voices.reduce((acc, cv) => { + const [lang] = extractLangRegionFromBCP47(cv.language); + let name = lang; + try { + if (langueName) { + name = langueName.of(lang) || lang; + } + } + catch (e) { + console.error("langueName.of throw an error with ", lang, e); + } + const found = acc.find(({ code }) => code === lang); + if (found) { + found.count++; + } + else { + acc.push({ code: lang, count: 1, label: name }); + } + return acc; + }, []); +} +function listRegions(voices, localization = navigatorLang()) { + let regionName = undefined; + if (localization) { + try { + regionName = new Intl.DisplayNames([localization], { type: 'region' }); + } + catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + return voices.reduce((acc, cv) => { + const [, region] = extractLangRegionFromBCP47(cv.language); + let name = region; + try { + if (regionName) { + name = regionName.of(region) || region; + } + } + catch (e) { + console.error("regionName.of throw an error with ", region, e); + } + const found = acc.find(({ code }) => code === region); + if (found) { + found.count++; + } + else { + acc.push({ code: region, count: 1, label: name }); + } + return acc; + }, []); +} +function groupByLanguages(voices, preferredLanguage = [], localization = navigatorLang()) { + const voicesSorted = sortByLanguage(voices, preferredLanguage, localization); + const languagesStructure = listLanguages(voicesSorted, localization); + const res = new Map(); + for (const { code, label } of languagesStructure) { + res.set(label, voicesSorted + .filter(({ language: voiceLang }) => { + const [l] = extractLangRegionFromBCP47(voiceLang); + return l === code; + })); + } + return res; +} +function groupByRegions(voices, preferredRegions = [], localization = navigatorLang()) { + const voicesSorted = sortByRegion(voices, preferredRegions, localization); + const languagesStructure = listRegions(voicesSorted, localization); + const res = new Map(); + for (const { code, label } of languagesStructure) { + res.set(label, voicesSorted + .filter(({ language: voiceLang }) => { + const [, r] = extractLangRegionFromBCP47(voiceLang); + return r === code; + })); + } + return res; +} +function groupByKindOfVoices(allVoices) { + const [recommendedVoices, lowQualityVoices] = filterOnRecommended(allVoices); + const remainingVoice = allVoices.filter((v) => !recommendedVoices.includes(v) && !lowQualityVoices.includes(v)); + const noveltyFiltered = filterOnNovelty(remainingVoice); + const noveltyVoices = remainingVoice.filter((v) => !noveltyFiltered.includes(v)); + const veryLowQualityFiltered = filterOnVeryLowQuality(remainingVoice); + const veryLowQualityVoices = remainingVoice.filter((v) => !veryLowQualityFiltered.includes(v)); + const remainingVoiceFiltered = filterOnNovelty(filterOnVeryLowQuality(remainingVoice)); + const res = new Map(); + res.set("recommendedVoices", recommendedVoices); + res.set("lowerQuality", lowQualityVoices); + res.set("novelty", noveltyVoices); + res.set("veryLowQuality", veryLowQualityVoices); + res.set("remaining", remainingVoiceFiltered); + return res; +} +function getLanguages(voices, preferredLanguage = [], localization = navigatorLang()) { + const group = groupByLanguages(voices, preferredLanguage, localization); + return Array.from(group.entries()).map(([label, _voices]) => { + var _a; + return { label, count: _voices.length, code: extractLangRegionFromBCP47(((_a = _voices[0]) === null || _a === void 0 ? void 0 : _a.language) || "")[0] }; + }); +} +/** + * Parse and extract SpeechSynthesisVoices, + * @returns IVoices[] + */ +function getVoices(preferredLanguage, localization) { + return __awaiter(this, void 0, void 0, function* () { + const allVoices = parseSpeechSynthesisVoices(yield getSpeechSynthesisVoices()); + const [recommendedVoices, lowQualityVoices] = filterOnRecommended(allVoices); + const remainingVoice = allVoices.filter((v) => !recommendedVoices.includes(v) && !lowQualityVoices.includes(v)); + const remainingVoiceFiltered = filterOnNovelty(filterOnVeryLowQuality(remainingVoice)); + const voices = [recommendedVoices, remainingVoiceFiltered].flat(); + const voicesSorted = sortByLanguage(sortByQuality(voices), preferredLanguage, localization || navigatorLang()); + return voicesSorted; + }); +} diff --git a/build/mjs/package.json b/build/mjs/package.json new file mode 100644 index 0000000..4720025 --- /dev/null +++ b/build/mjs/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/build/mjs/src/data.d.ts b/build/mjs/src/data.d.ts new file mode 100644 index 0000000..6d32589 --- /dev/null +++ b/build/mjs/src/data.d.ts @@ -0,0 +1,148 @@ +export declare const novelty: string[]; +export declare const veryLowQuality: string[]; +export type TGender = "female" | "male" | "nonbinary"; +export type TQuality = "veryLow" | "low" | "normal" | "high" | "veryHigh"; +export interface IRecommended { + label: string; + name: string; + altNames?: string[]; + language: string; + gender?: TGender | undefined; + age?: string | undefined; + quality: TQuality[]; + recommendedPitch?: number | undefined; + recommendedRate?: number | undefined; + localizedName: string; +} +export declare const recommended: Array; +export declare const quality: { + ca: { + normal: string; + high: string; + }; + cs: { + normal: string; + high: string; + }; + da: { + normal: string; + high: string; + }; + de: { + normal: string; + high: string; + }; + el: { + normal: string; + high: string; + }; + en: { + normal: string; + high: string; + }; + es: { + normal: string; + high: string; + }; + fi: { + normal: string; + high: string; + }; + fr: { + normal: string; + high: string; + }; + hu: { + normal: string; + high: string; + }; + hr: { + normal: string; + high: string; + }; + it: { + normal: string; + high: string; + }; + ja: { + normal: string; + high: string; + }; + ko: { + normal: string; + high: string; + }; + nb: { + normal: string; + high: string; + }; + nl: { + normal: string; + high: string; + }; + pl: { + normal: string; + high: string; + }; + pt: { + normal: string; + high: string; + }; + ro: { + normal: string; + high: string; + }; + ru: { + normal: string; + high: string; + }; + sk: { + normal: string; + high: string; + }; + sl: { + normal: string; + high: string; + }; + sv: { + normal: string; + high: string; + }; + tr: { + normal: string; + high: string; + }; + uk: { + normal: string; + high: string; + }; +}; +export declare const defaultRegion: { + bg: string; + ca: string; + cs: string; + da: string; + de: string; + en: string; + es: string; + eu: string; + fi: string; + fr: string; + gl: string; + hr: string; + hu: string; + it: string; + ja: string; + ko: string; + nb: string; + nl: string; + pl: string; + pt: string; + ro: string; + ru: string; + sk: string; + sl: string; + sv: string; + tr: string; + uk: string; +}; diff --git a/build/mjs/src/data.js b/build/mjs/src/data.js new file mode 100644 index 0000000..8afe406 --- /dev/null +++ b/build/mjs/src/data.js @@ -0,0 +1,6 @@ +export const novelty = ["Albert", "Bad News", "Mauvaises nouvelles", "Malas noticias", "Brutte notizie", "Bahh", "Bells", "Cloches", "Campanas", "Campane", "Boing", "Bubbles", "Bulles", "Burbujas", "Bollicine", "Cellos", "Violoncelles", "Violonchelos", "Violoncelli", "Good News", "Bonnes nouvelles", "Buenas noticias", "Buone notizie", "Jester", "Bouffon", "Bufón", "Giullare", "Organ", "Orgue", "Órgano", "Organo", "Superstar", "Superestrella", "Trinoids", "Trinoïdes", "Trinoid", "Whisper", "Murmure", "Susurro", "Sussurro", "Wobble", "Zarvox"]; +export const veryLowQuality = ["Eddy", "Flo", "Grandma", "Grandpa", "Jacques", "Reed", "Rocko", "Sandy", "Shelley", "Fred", "Junior", "Kathy", "Ralph", "eSpeak Arabic", "eSpeak Bulgarian", "eSpeak Bengali", "eSpeak Catalan", "eSpeak Chinese (Mandarin, latin as English)", "eSpeak Czech", "eSpeak Danish", "eSpeak German", "eSpeak Greek", "eSpeak Spanish (Spain)", "eSpeak Estonian", "eSpeak Finnish", "eSpeak Gujarati", "eSpeak Croatian", "eSpeak Hungarian", "eSpeak Indonesian", "eSpeak Italian", "eSpeak Kannada", "eSpeak Korean", "eSpeak Lithuanian", "eSpeak Latvian", "eSpeak Malayalm", "eSpeak Marathi", "eSpeak Malay", "eSpeak Norwegian Bokmål", "eSpeak Polish", "eSpeak Portuguese (Brazil)", "eSpeak Romanian", "eSpeak Russian", "eSpeak Slovak", "eSpeak Slovenian", "eSpeak Serbian", "eSpeak Swedish", "eSpeak Swahili", "eSpeak Tamil", "eSpeak Telugu", "eSpeak Turkish", "eSpeak Vietnamese (Northern)"]; +; +export const recommended = [{ "label": "Kalina", "name": "Microsoft Kalina Online (Natural) - Bulgarian (Bulgaria)", "language": "bg-BG", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Borislav", "name": "Microsoft Borislav Online (Natural) - Bulgarian (Bulgaria)", "language": "bg-BG", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Daria", "name": "Daria", "language": "bg-BG", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Ivan", "name": "Microsoft Ivan - Bulgarian (Bulgaria)", "language": "bg-BG", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Женски глас", "name": "Android Speech Recognition and Synthesis from Google bg-bg-x-ifk-network", "altNames": ["Android Speech Recognition and Synthesis from Google bg-bg-x-ifk-local", "Android Speech Recognition and Synthesis from Google bg-bg-language"], "language": "bg-BG", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "български глас", "name": "български България", "language": "bg-BG", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Joana (Català)", "name": "Microsoft Joana Online (Natural) - Catalan (Spain)", "language": "ca-ES", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Enric (Català)", "name": "Microsoft Enric Online (Natural) - Catalan (Spain)", "language": "ca-ES", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Montse (Català)", "name": "Montse", "language": "ca-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Pau (Valencià)", "name": "Pau", "language": "ca-ES-u-sd-esvc", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jordi (Català)", "name": "Jordi", "language": "ca-ES", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Herena (Català)", "name": "Microsoft Herena - Catalan (Spain)", "language": "ca-ES", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Veu femenina catalana", "name": "Android Speech Recognition and Synthesis from Google ca-es-x-caf-network", "altNames": ["Android Speech Recognition and Synthesis from Google ca-es-x-caf-local", "Android Speech Recognition and Synthesis from Google ca-ES-language"], "language": "ca-ES", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Veu catalana", "name": "català Espanya", "language": "ca-ES", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Vlasta", "name": "Microsoft Vlasta Online (Natural) - Czech (Czech)", "language": "cs-CZ", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Antonin", "name": "Microsoft Antonin Online (Natural) - Czech (Czech)", "language": "cs-CZ", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Zuzana", "name": "Zuzana", "language": "cs-CZ", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Iveta", "name": "Iveta", "language": "cs-CZ", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jakub", "name": "Microsoft Jakub - Czech (Czech)", "language": "cs-CZ", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ženský hlas", "name": "Google čeština (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google cs-cz-x-jfs-network", "Chrome OS čeština", "Android Speech Recognition and Synthesis from Google cs-cz-x-jfs-local", "Android Speech Recognition and Synthesis from Google cs-CZ-language"], "language": "cs-CZ", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Český hlas", "name": "čeština Česko", "language": "cs-CZ", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Christel", "name": "Microsoft Christel Online (Natural) - Danish (Denmark)", "language": "da-DK", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jeppe", "name": "Microsoft Jeppe Online (Natural) - Danish (Denmark)", "language": "da-DK", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sara", "name": "Sara", "language": "da-DK", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Magnus", "name": "Magnus", "language": "da-DK", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Helle", "name": "Microsoft Helle - Danish (Denmark)", "language": "da-DK", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvindestemme 1", "name": "Google Dansk 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google da-dk-x-kfm-network", "Chrome OS Dansk 1", "Android Speech Recognition and Synthesis from Google da-dk-x-kfm-local", "Android Speech Recognition and Synthesis from Google da-DK-language"], "language": "da-DK", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvindestemme 2", "name": "Google Dansk 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google da-dk-x-sfp-network", "Chrome OS Dansk 3", "Android Speech Recognition and Synthesis from Google da-dk-x-sfp-local"], "language": "da-DK", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvindestemme 3", "name": "Google Dansk 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google da-dk-x-vfb-network", "Chrome OS Dansk 4", "Android Speech Recognition and Synthesis from Google da-dk-x-vfb-local"], "language": "da-DK", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mandsstemme", "name": "Google Dansk 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google da-dk-x-nmm-network", "Chrome OS Dansk 2", "Android Speech Recognition and Synthesis from Google da-dk-x-nmm-local"], "language": "da-DK", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Dansk stemme", "name": "dansk Danmark", "language": "da-DK", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Seraphina (Deutschland)", "name": "Microsoft SeraphinaMultilingual Online (Natural) - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Amala (Deutschland)", "name": "Microsoft Amala Online (Natural) - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Katja (Deutschland)", "name": "Microsoft Katja Online (Natural) - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Florian (Deutschland)", "name": "Microsoft FlorianMultilingual Online (Natural) - German (Germany)", "language": "de-DE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Conrad (Deutschland)", "name": "Microsoft Conrad Online (Natural) - German (Germany)", "language": "de-DE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Killian (Deutschland)", "name": "Microsoft Killian Online (Natural) - German (Germany)", "language": "de-DE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ingrid (Österreich)", "name": "Microsoft Ingrid Online (Natural) - German (Austria)", "language": "de-AT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jonas (Österreich)", "name": "Microsoft Jonas Online (Natural) - German (Austria)", "language": "de-AT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Leni (Schweiz)", "name": "Microsoft Leni Online (Natural) - German (Switzerland)", "language": "de-CH", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jan (Schweiz)", "name": "Microsoft Jan Online (Natural) - German (Switzerland)", "language": "de-CH", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Petra (Deutschland)", "name": "Petra", "language": "de-DE", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Anna (Deutschland)", "name": "Anna", "language": "de-DE", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Helena (Deutschland)", "name": "Helena", "language": "de-DE", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Markus (Deutschland)", "name": "Markus", "language": "de-DE", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Viktor (Deutschland)", "name": "Viktor", "language": "de-DE", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Yannick (Deutschland)", "name": "Yannick", "language": "de-DE", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Martin (Deutschland)", "name": "Martin", "language": "de-DE", "gender": "male", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Weibliche Google-Stimme (Deutschland)", "name": "Google Deutsch", "language": "de-DE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Hedda (Deutschland)", "name": "Microsoft Hedda - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Katja (Deutschland)", "name": "Microsoft Katja - German (Germany)", "language": "de-DE", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Stefan (Deutschland)", "name": "Microsoft Stefan - German (Germany)", "language": "de-DE", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Michael (Österreich)", "name": "Microsoft Michael - German (Austria)", "language": "de-AT", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Karsten (Schweiz)", "name": "Microsoft Karsten - German (Switzerland)", "language": "de-CH", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Weibliche Stimme 1 (Deutschland)", "name": "Google Deutsch 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google de-de-x-dea-network", "Chrome OS Deutsch 2", "Android Speech Recognition and Synthesis from Google de-de-x-dea-local", "Android Speech Recognition and Synthesis from Google de-DE-language"], "language": "de-DE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Weibliche Stimme 2 (Deutschland)", "name": "Google Deutsch 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google de-de-x-nfh-network", "Chrome OS Deutsch 1", "Android Speech Recognition and Synthesis from Google de-de-x-nfh-local"], "language": "de-DE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Männliche Stimme 1 (Deutschland)", "name": "Google Deutsch 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google de-de-x-deb-network", "Chrome OS Deutsch 3", "Android Speech Recognition and Synthesis from Google de-de-x-deb-local"], "language": "de-DE", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Männliche Stimme 2 (Deutschland)", "name": "Google Deutsch 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google de-de-x-deg-network", "Chrome OS Deutsch 4", "Android Speech Recognition and Synthesis from Google de-de-x-deg-local"], "language": "de-DE", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Deutsche Stimme", "name": "Deutsch Deutschland", "language": "de-DE", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Emma (US)", "name": "Microsoft EmmaMultilingual Online (Natural) - English (United States)", "altNames": ["Microsoft Emma Online (Natural) - English (United States)"], "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Microsoft Ava (US)", "name": "Microsoft AvaMultilingual Online (Natural) - English (United States)", "altNames": ["Microsoft Ava Online (Natural) - English (United States)"], "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jenny (US)", "name": "Microsoft Jenny Online (Natural) - English (United States)", "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Aria (US)", "name": "Microsoft Aria Online (Natural) - English (United States)", "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Michelle (US)", "name": "Microsoft Michelle Online (Natural) - English (United States)", "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ana (US)", "name": "Microsoft Ana Online (Natural) - English (United States)", "language": "en-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Andrew (US)", "name": "Microsoft AndrewMultilingual Online (Natural) - English (United States)", "altNames": ["Microsoft Andrew Online (Natural) - English (United States)"], "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Brian (US)", "name": "Microsoft BrianMultilingual Online (Natural) - English (United States)", "altNames": ["Microsoft Brian Online (Natural) - English (United States)"], "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Guy (US)", "name": "Microsoft Guy Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Eric (US)", "name": "Microsoft Eric Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Steffan (US)", "name": "Microsoft Steffan Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Christopher (US)", "name": "Microsoft Christopher Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Roger (US)", "name": "Microsoft Roger Online (Natural) - English (United States)", "language": "en-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sonia (UK)", "name": "Microsoft Sonia Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Libby (UK)", "name": "Microsoft Libby Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Maisie (UK)", "name": "Microsoft Maisie Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ryan (UK)", "name": "Microsoft Ryan Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Thomas (UK)", "name": "Microsoft Thomas Online (Natural) - English (United Kingdom)", "language": "en-GB", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Natasha (Australia)", "name": "Microsoft Natasha Online (Natural) - English (Australia)", "language": "en-AU", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "William (Australia)", "name": "Microsoft William Online (Natural) - English (Australia)", "language": "en-AU", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Clara (Canada)", "name": "Microsoft Clara Online (Natural) - English (Canada)", "language": "en-CA", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Liam (Canada)", "name": "Microsoft Liam Online (Natural) - English (Canada)", "language": "en-CA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Apple Ava (US)", "name": "Ava", "language": "en-US", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Zoe (US)", "name": "Zoe", "language": "en-US", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Allison (US)", "name": "Allison", "language": "en-US", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Nicky (US)", "name": "Nicky", "language": "en-US", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Samantha (US)", "name": "Samantha", "language": "en-US", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Joelle (US)", "name": "Joelle", "language": "en-US", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Evan (US)", "name": "Evan", "language": "en-US", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Nathan (US)", "name": "Nathan", "language": "en-US", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Tom (US)", "name": "Tom", "language": "en-US", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Alex (US)", "name": "Alex", "language": "en-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Aaron (US)", "name": "Aaron", "language": "en-US", "gender": "male", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Kate (UK)", "name": "Kate", "language": "en-GB", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Stephanie (UK)", "name": "Stephanie", "language": "en-GB", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Serena (UK)", "name": "Serena", "language": "en-GB", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Martha (UK)", "name": "Martha", "language": "en-GB", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jamie (UK)", "name": "Jamie", "language": "en-GB", "gender": "male", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Oliver (UK)", "name": "Oliver", "language": "en-GB", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Daniel (UK)", "name": "Daniel", "language": "en-GB", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Arthur (UK)", "name": "Arthur", "language": "en-GB", "gender": "male", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Matilda (Australia)", "name": "Matilda", "language": "en-AU", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Karen (Australia)", "name": "Karen", "language": "en-AU", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Catherine (Australia)", "name": "Catherine", "language": "en-AU", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Lee (Australia)", "name": "Lee", "language": "en-AU", "gender": "male", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Gordon (Australia)", "name": "Gordon", "language": "en-AU", "gender": "male", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Female Google voice (US)", "name": "Google US English", "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female Google voice (UK)", "name": "Google UK English Female", "language": "en-GB", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male Google voice (UK)", "name": "Google UK English Male", "language": "en-GB", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Zira (US)", "name": "Microsoft Zira - English (United States)", "language": "en-US", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "David (US)", "name": "Microsoft David - English (United States)", "language": "en-US", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mark (US)", "name": "Microsoft Mark - English (United States)", "language": "en-US", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Hazel (UK)", "name": "Microsoft Hazel - English (Great Britain)", "language": "en-GB", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Susan (UK)", "name": "Microsoft Susan - English (Great Britain)", "language": "en-GB", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "George (UK)", "name": "Microsoft George - English (Great Britain)", "language": "en-GB", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Catherine (Australia)", "name": "Microsoft Catherine - English (Austalia)", "language": "en-AU", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "James (Australia)", "name": "Microsoft Richard - English (Australia)", "language": "en-AU", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Linda (Canada)", "name": "Microsoft Linda - English (Canada)", "language": "en-CA", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Richard (Canada)", "name": "Microsoft Richard - English (Canada)", "language": "en-CA", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 1 (US)", "name": "Google US English 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-tpc-network", "Chrome OS US English 5", "Android Speech Recognition and Synthesis from Google en-us-x-tpc-local", "Android Speech Recognition and Synthesis from Google en-US-language"], "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 2 (US)", "name": "Google US English 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-iob-network", "Chrome OS US English 1", "Android Speech Recognition and Synthesis from Google en-us-x-iob-local"], "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 3 (US)", "name": "Google US English 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-iog-network", "Chrome OS US English 2", "Android Speech Recognition and Synthesis from Google en-us-x-iog-local"], "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 4 (US)", "name": "Google US English 7 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-tpf-network", "Chrome OS US English 7", "Android Speech Recognition and Synthesis from Google en-us-x-tpf-local"], "language": "en-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 5 (US)", "name": "Android Speech Recognition and Synthesis from Google en-us-x-sfg-network", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-sfg-local"], "language": "en-US", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 6 (US)", "name": "Chrome OS US English 8", "language": "en-US", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 1 (US)", "name": "Google US English 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-iom-network", "Chrome OS US English 4", "Android Speech Recognition and Synthesis from Google en-us-x-iom-local"], "language": "en-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 2 (US)", "name": "Google US English 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-iol-network", "Chrome OS US English 3", "Android Speech Recognition and Synthesis from Google en-us-x-iol-local"], "language": "en-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 3 (US)", "name": "Google US English 6 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-us-x-tpd-network", "Chrome OS US English 6", "Android Speech Recognition and Synthesis from Google en-us-x-tpd-local"], "language": "en-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 1 (UK)", "name": "Google UK English 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gba-network", "Chrome OS UK English 2", "Android Speech Recognition and Synthesis from Google en-gb-x-gba-local", "Android Speech Recognition and Synthesis from Google en-GB-language"], "language": "en-GB", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 2 (UK)", "name": "Google UK English 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gbc-network", "Chrome OS UK English 4", "Android Speech Recognition and Synthesis from Google en-gb-x-gbc-local"], "language": "en-GB", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 3 (UK)", "name": "Google UK English 6 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gbg-network", "Chrome OS UK English 6", "Android Speech Recognition and Synthesis from Google en-gb-x-gbg-local"], "language": "en-GB", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 4 (UK)", "name": "Chrome OS UK English 7", "language": "en-GB", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 1 (UK)", "name": "Google UK English 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-rjs-network", "Chrome OS UK English 1", "Android Speech Recognition and Synthesis from Google en-gb-x-rjs-local"], "language": "en-GB", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 2 (UK)", "name": "Google UK English 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gbb-network", "Chrome OS UK English 3", "Android Speech Recognition and Synthesis from Google en-gb-x-gbb-local"], "language": "en-GB", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 3 (UK)", "name": "Google UK English 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-gb-x-gbd-network", "Chrome OS UK English 5", "Android Speech Recognition and Synthesis from Google en-gb-x-gbd-local"], "language": "en-GB", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 1 (Australia)", "name": "Google Australian English 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-au-x-aua-network", "Chrome OS Australian English 1", "Android Speech Recognition and Synthesis from Google en-au-x-aua-local", "Android Speech Recognition and Synthesis from Google en-AU-language"], "language": "en-AU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Female voice 2 (Australia)", "name": "Google Australian English 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-au-x-auc-network", "Chrome OS Australian English 3", "Android Speech Recognition and Synthesis from Google en-au-x-auc-local"], "language": "en-AU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 1 (Australia)", "name": "Google Australian English 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-au-x-aub-network", "Chrome OS Australian English 2", "Android Speech Recognition and Synthesis from Google en-au-x-aub-local"], "language": "en-AU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 2 (Australia)", "name": "Google Australian English 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google en-au-x-aud-network", "Chrome OS Australian English 4", "Android Speech Recognition and Synthesis from Google en-au-x-aud-local"], "language": "en-AU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Male voice 3 (Australia)", "name": "Chrome OS Australian English 5", "language": "en-AU", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "US voice", "name": "English United States", "language": "en-US", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "UK voice", "name": "English United Kingdom", "language": "en-GB", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Australian voice", "name": "English Australia", "language": "en-AU", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Elvira (España)", "name": "Microsoft Elvira Online (Natural) - Spanish (Spain)", "language": "es-ES", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Alvaro (España)", "name": "Microsoft Alvaro Online (Natural) - Spanish (Spain)", "language": "es-ES", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Dalia (México)", "name": "Microsoft Dalia Online (Natural) - Spanish (Mexico)", "language": "es-MX", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Microsoft Jorge (México)", "name": "Microsoft Jorge Online (Natural) - Spanish (Mexico)", "language": "es-MX", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Elena (Argentina)", "name": "Microsoft Elena Online (Natural) - Spanish (Argentina)", "language": "es-AR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tomas (Argentina)", "name": "Microsoft Tomas Online (Natural) - Spanish (Argentina)", "language": "es-AR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sofia (Bolivia)", "name": "Microsoft Sofia Online (Natural) - Spanish (Bolivia)", "language": "es-BO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Marcelo (Bolivia)", "name": "Microsoft Marcelo Online (Natural) - Spanish (Bolivia)", "language": "es-BO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Catalina (Chile)", "name": "Microsoft Catalina Online (Natural) - Spanish (Chile)", "language": "es-CL", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lorenzo (Chile)", "name": "Microsoft Lorenzo Online (Natural) - Spanish (Chile)", "language": "es-CL", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ximena (Colombia)", "name": "Microsoft Ximena Online (Natural) - Spanish (Colombia)", "language": "es-CO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Salome (Colombia)", "name": "Microsoft Salome Online (Natural) - Spanish (Colombia)", "language": "es-CO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Gonzalo (Colombia)", "name": "Microsoft Gonzalo Online (Natural) - Spanish (Colombia)", "language": "es-CO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Maria (Costa Rica)", "name": "Microsoft Maria Online (Natural) - Spanish (Costa Rica)", "language": "es-CR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Juan (Costa Rica)", "name": "Microsoft Juan Online (Natural) - Spanish (Costa Rica)", "language": "es-CR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Belkys (Cuba)", "name": "Microsoft Belkys Online (Natural) - Spanish (Cuba)", "language": "es-CU", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Manuel (Cuba)", "name": "Microsoft Manuel Online (Natural) - Spanish (Cuba)", "language": "es-CU", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Andrea (Ecuador)", "name": "Microsoft Andrea Online (Natural) - Spanish (Ecuador)", "language": "es-EC", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Luis (Ecuador)", "name": "Microsoft Luis Online (Natural) - Spanish (Ecuador)", "language": "es-EC", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lorena (El Salvador)", "name": "Microsoft Lorena Online (Natural) - Spanish (El Salvador)", "language": "es-SV", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Rodrigo (El Salvador)", "name": "Microsoft Rodrigo Online (Natural) - Spanish (El Salvador)", "language": "es-SV", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Paloma (Estados Unidos)", "name": "Microsoft Paloma Online (Natural) - Spanish (United States)", "language": "es-US", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Alonso (Estados Unidos)", "name": "Microsoft Alonso Online (Natural) - Spanish (United States)", "language": "es-US", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Marta (Guatemala)", "name": "Microsoft Marta Online (Natural) - Spanish (Guatemala)", "language": "es-GT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Andres (Guatemala)", "name": "Microsoft Andres Online (Natural) - Spanish (Guatemala)", "language": "es-GT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Teresa (Guinea Ecuatorial)", "name": "Microsoft Teresa Online (Natural) - Spanish (Equatorial Guinea)", "language": "es-GQ", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Javier (Guinea Ecuatorial)", "name": "Microsoft Javier Online (Natural) - Spanish (Equatorial Guinea)", "language": "es-GQ", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Karla (Honduras)", "name": "Microsoft Karla Online (Natural) - Spanish (Honduras)", "language": "es-HN", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Carlos (Honduras)", "name": "Microsoft Carlos Online (Natural) - Spanish (Honduras)", "language": "es-HN", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Yolanda (Nicaragua)", "name": "Microsoft Yolanda Online (Natural) - Spanish (Nicaragua)", "language": "es-NI", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Federico (Nicaragua)", "name": "Microsoft Federico Online (Natural) - Spanish (Nicaragua)", "language": "es-NI", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Margarita (Panamá)", "name": "Microsoft Margarita Online (Natural) - Spanish (Panama)", "language": "es-PA", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Roberto (Panamá)", "name": "Microsoft Roberto Online (Natural) - Spanish (Panama)", "language": "es-PA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tania (Paraguay)", "name": "Microsoft Tania Online (Natural) - Spanish (Paraguay)", "language": "es-PY", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Mario (Paraguay)", "name": "Microsoft Mario Online (Natural) - Spanish (Paraguay)", "language": "es-PY", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Camila (Perú)", "name": "Microsoft Camila Online (Natural) - Spanish (Peru)", "language": "es-PE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Alex (Perú)", "name": "Microsoft Alex Online (Natural) - Spanish (Peru)", "language": "es-PE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Karina (Puerto Rico)", "name": "Microsoft Karina Online (Natural) - Spanish (Puerto Rico)", "language": "es-PR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Victor (Puerto Rico)", "name": "Microsoft Victor Online (Natural) - Spanish (Puerto Rico)", "language": "es-PR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ramona (República Dominicana)", "name": "Microsoft Ramona Online (Natural) - Spanish (Dominican Republic)", "language": "es-DO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Emilio (República Dominicana)", "name": "Microsoft Emilio Online (Natural) - Spanish (Dominican Republic)", "language": "es-DO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Valentina (Uruguay)", "name": "Microsoft Valentina Online (Natural) - Spanish (Uruguay)", "language": "es-UY", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Mateo (Uruguay)", "name": "Microsoft Mateo Online (Natural) - Spanish (Uruguay)", "language": "es-UY", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Paola (Venezuela)", "name": "Microsoft Paola Online (Natural) - Spanish (Venezuela)", "language": "es-VE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sebastian (Venezuela)", "name": "Microsoft Sebastian Online (Natural) - Spanish (Venezuela)", "language": "es-VE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Marisol (España)", "name": "Marisol", "language": "es-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Mónica (España)", "name": "Mónica", "language": "es-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Apple Jorge (España)", "name": "Jorge", "language": "es-ES", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Angelica (México)", "name": "Angelica", "language": "es-MX", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Paulina (México)", "name": "Paulina", "language": "es-MX", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Juan (México)", "name": "Juan", "language": "es-MX", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Isabela (Argentina)", "name": "Isabela", "language": "es-AR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Diego (Argentina)", "name": "Diego", "language": "es-AR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Francesca (Chile)", "name": "Francesca", "language": "es-CL", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Soledad (Colombia)", "name": "Soledad", "language": "es-CO", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jimena (Colombia)", "name": "Jimena", "language": "es-CO", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Carlos (Colombia)", "name": "Carlos", "language": "es-CO", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Voz Google masculina (España)", "name": "Google español", "language": "es-ES", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz Google femenina (Estados Unidos)", "name": "Google español de Estados Unidos", "language": "es-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Helena (España)", "name": "Microsoft Helena - Spanish (Spain)", "language": "es-ES", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Laura (España)", "name": "Microsoft Laura - Spanish (Spain)", "language": "es-ES", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Pablo (España)", "name": "Microsoft Pablo - Spanish (Spain)", "language": "es-ES", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Sabina (México)", "name": "Microsoft Sabina - Spanish (Mexico)", "language": "es-MX", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Raul (México)", "name": "Microsoft Raul - Spanish (Mexico)", "language": "es-MX", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 1 (España)", "name": "Google español 4 (Natural)", "altNames": ["Chrome OS español 4", "Android Speech Recognition and Synthesis from Google es-es-x-eee-local", "Android Speech Recognition and Synthesis from Google es-ES-language"], "language": "es-ES", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 2 (España)", "name": "Google español 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-es-x-eea-network", "Chrome OS español 1", "Android Speech Recognition and Synthesis from Google es-es-x-eea-local"], "language": "es-ES", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 3 (España)", "name": "Google español 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-es-x-eec-network", "Chrome OS español 2", "Android Speech Recognition and Synthesis from Google es-es-x-eec-local"], "language": "es-ES", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 1 (España)", "name": "Google español 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-es-x-eed-network", "Chrome OS español 3", "Android Speech Recognition and Synthesis from Google es-es-x-eed-local"], "language": "es-ES", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 2 (España)", "name": "Google español 5 (Natural)", "altNames": ["Chrome OS español 5", "Android Speech Recognition and Synthesis from Google es-es-x-eef-local"], "language": "es-ES", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 1 (Estados Unidos)", "name": "Google español de Estados Unidos 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-us-x-esc-network", "Chrome OS español de Estados Unidos", "Android Speech Recognition and Synthesis from Google es-us-x-esc-local", "Android Speech Recognition and Synthesis from Google es-US-language"], "language": "es-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz femenina 2 (Estados Unidos)", "name": "Google español de Estados Unidos 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-us-x-sfb-network", "Android Speech Recognition and Synthesis from Google es-us-x-sfb-local"], "language": "es-US", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 1 (Estados Unidos)", "name": "Google español de Estados Unidos 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-us-x-esd-network", "Android Speech Recognition and Synthesis from Google es-us-x-esd-local"], "language": "es-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 2 (Estados Unidos)", "name": "Google español de Estados Unidos 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google es-us-x-esf-network", "Android Speech Recognition and Synthesis from Google es-us-x-esf-local"], "language": "es-US", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz de España", "name": "español España", "language": "es-ES", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Voz de Estados Unidos", "name": "español Estados Unidos", "language": "es-US", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Miren", "name": "Miren", "language": "eu-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Noora", "name": "Microsoft Noora Online (Natural) - Finnish (Finland)", "language": "fi-FI", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Harri", "name": "Microsoft Harri Online (Natural) - Finnish (Finland)", "language": "fi-FI", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Satu", "name": "Satu", "language": "fi-FI", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Onni", "name": "Onni", "language": "fi-FI", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Heidi", "name": "Microsoft Heidi - Finnish (Finland)", "language": "fi-FI", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Suomalainen naisääni", "name": "Google Suomi (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fi-fi-x-afi-network", "Chrome OS Suomi", "Android Speech Recognition and Synthesis from Google fi-fi-x-afi-local", "Android Speech Recognition and Synthesis from Google fi-FI-language"], "language": "fi-FI", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Suomalainen ääni", "name": "suomi Suomi", "language": "fi-FI", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Vivienne (France)", "name": "Microsoft VivienneMultilingual Online (Natural) - French (France)", "language": "fr-FR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Denise (France)", "name": "Microsoft Denise Online (Natural) - French (France)", "language": "fr-FR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Charline (Belgique)", "name": "Microsoft Charline Online (Natural) - French (Belgium)", "language": "fr-BE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ariane (Suisse)", "name": "Microsoft Ariane Online (Natural) - French (Switzerland)", "language": "fr-CH", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Eloise (France)", "name": "Microsoft Eloise Online (Natural) - French (France)", "language": "fr-FR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Remy (France)", "name": "Microsoft RemyMultilingual Online (Natural) - French (France)", "language": "fr-FR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Henri (France)", "name": "Microsoft Henri Online (Natural) - French (France)", "language": "fr-FR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Gerard (Belgique)", "name": "Microsoft Gerard Online (Natural) - French (Belgium)", "language": "fr-BE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Fabrice (Suisse)", "name": "Microsoft Fabrice Online (Natural) - French (Switzerland)", "language": "fr-CH", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Sylvie (Canada)", "name": "Microsoft Sylvie Online (Natural) - French (Canada)", "language": "fr-CA", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Antoine (Canada)", "name": "Microsoft Antoine Online (Natural) - French (Canada)", "language": "fr-CA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Jean (Canada)", "name": "Microsoft Jean Online (Natural) - French (Canada)", "language": "fr-CA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Thierry (Canada)", "name": "Microsoft Thierry Online (Natural) - French (Canada)", "language": "fr-CA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Audrey (France)", "name": "Audrey", "language": "fr-FR", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Aurélie (France)", "name": "Aurélie", "language": "fr-FR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 0.9, "localizedName": "apple" }, { "label": "Marie (France)", "name": "Marie", "language": "fr-FR", "gender": "female", "quality": ["low"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Thomas (France)", "name": "Thomas", "language": "fr-FR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Aude (Belgique)", "name": "Aude", "language": "fr-BE", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Chantal (Canada)", "name": "Chantal", "language": "fr-CA", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Amélie (Canada)", "name": "Amélie", "language": "fr-CA", "gender": "female", "quality": ["low", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Nicolas (Canada)", "name": "Nicolas", "language": "fr-CA", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Voix Google féminine (France)", "name": "Google français", "language": "fr-FR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Julie (France)", "name": "Microsoft Julie - French (France)", "language": "fr-FR", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Hortence (France)", "name": "Microsoft Hortence - French (France)", "language": "fr-FR", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Paul (France)", "name": "Microsoft Paul - French (France)", "language": "fr-FR", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Caroline (Canada)", "name": "Microsoft Caroline - French (Canada)", "language": "fr-CA", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Claude (Canada)", "name": "Microsoft Claude - French (Canada)", "language": "fr-CA", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Guillaume (Suisse)", "name": "Microsoft Claude - French (Switzerland)", "language": "fr-CH", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 1 (France)", "name": "Google français 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-frc-network", "Chrome OS français 4", "Android Speech Recognition and Synthesis from Google fr-fr-x-frc-local", "Android Speech Recognition and Synthesis from Google fr-FR-language"], "language": "fr-FR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 2 (France)", "name": "Google français 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-fra-network", "Chrome OS français 2", "Android Speech Recognition and Synthesis from Google fr-fr-x-fra-local"], "language": "fr-FR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 3 (France)", "name": "Google français 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-vlf-network", "Chrome OS français 1", "Android Speech Recognition and Synthesis from Google fr-fr-x-vlf-local"], "language": "fr-FR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix masculine 1 (France)", "name": "Google français 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-frd-network", "Chrome OS français 5", "Android Speech Recognition and Synthesis from Google fr-fr-x-frd-local"], "language": "fr-FR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix masculine 2 (France)", "name": "Google français 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google fr-fr-x-frb-network", "Chrome OS français 3", "Android Speech Recognition and Synthesis from Google fr-fr-x-frb-local"], "language": "fr-FR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 1 (Canada)", "name": "Android Speech Recognition and Synthesis from Google fr-ca-x-caa-network", "altNames": ["Android Speech Recognition and Synthesis from Google fr-ca-x-caa-local", "Android Speech Recognition and Synthesis from Google fr-CA-language"], "language": "fr-CA", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix féminine 2 (Canada)", "name": "Android Speech Recognition and Synthesis from Google fr-ca-x-cac-network", "altNames": ["Android Speech Recognition and Synthesis from Google fr-ca-x-cac-local"], "language": "fr-CA", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix masculine 1 (Canada)", "name": "Android Speech Recognition and Synthesis from Google fr-ca-x-cab-network", "altNames": ["Android Speech Recognition and Synthesis from Google fr-ca-x-cab-local"], "language": "fr-CA", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix masculine 2 (Canada)", "name": "Android Speech Recognition and Synthesis from Google fr-ca-x-cad-network", "altNames": ["Android Speech Recognition and Synthesis from Google fr-ca-x-cad-local"], "language": "fr-CA", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voix française", "name": "français France", "language": "fr-FR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Voix canadienne", "name": "français Canada", "language": "fr-CA", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Sabela", "name": "Microsoft Sabela Online (Natural) - Galician (Spain)", "language": "gl-ES", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Roi", "name": "Microsoft Roi Online (Natural) - Galician (Spain)", "language": "gl-ES", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Carmela", "name": "Carmela", "language": "gl-ES", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Gabrijela", "name": "Microsoft Gabrijela Online (Natural) - Croatian (Croatia)", "language": "hr-HR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Srecko", "name": "Microsoft Srecko Online (Natural) - Croatian (Croatia)", "language": "hr-HR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lana", "name": "Lana", "altNames": ["Lana (poboljšani)", "Lana (hrvatski (Hrvatska))"], "language": "hr-HR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Matej", "name": "Microsoft Matej - Croatian (Croatia)", "language": "hr-HR", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ženski glas", "name": "Android Speech Recognition and Synthesis from Google hr-hr-x-hra-network", "altNames": ["Android Speech Recognition and Synthesis from Google hr-hr-x-hra-local"], "language": "hr-HR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Muški glas", "name": "Android Speech Recognition and Synthesis from Google hr-hr-x-hrb-network", "altNames": ["Android Speech Recognition and Synthesis from Google hr-hr-x-hrb-local", "Android Speech Recognition and Synthesis from Google hr-HR-language"], "language": "hr-HR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Hrvatski glas", "name": "hrvatski Hrvatska", "language": "hr-HR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Noemi", "name": "Microsoft Noemi Online (Natural) - Hungarian (Hungary)", "language": "hu-HU", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tamas", "name": "Microsoft Tamas Online (Natural) - Hungarian (Hungary)", "language": "hu-HU", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tünde", "name": "Tünde", "language": "hu-HU", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Szabolcs", "name": "Microsoft Szabolcs - Hungarian (Hungary)", "language": "hu-HU", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Női hang", "name": "Google Magyar (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google hu-hu-x-kfl-network", "Chrome OS Magyar", "Android Speech Recognition and Synthesis from Google hu-hu-x-kfl-local", "Android Speech Recognition and Synthesis from Google hu-HU-language"], "language": "hu-HU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Magyar hang", "name": "magyar Magyarország", "language": "hu-HU", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Elsa (Alta qualita)", "name": "Microsoft Elsa Online (Natural) - Italian (Italy)", "language": "it-IT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Isabella", "name": "Microsoft Isabella Online (Natural) - Italian (Italy)", "language": "it-IT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Giuseppe", "name": "Microsoft Giuseppe Online (Natural) - Italian (Italy)", "language": "it-IT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Diego", "name": "Microsoft Diego Online (Natural) - Italian (Italy)", "language": "it-IT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Federica", "name": "Federica", "language": "it-IT", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Emma", "name": "Emma", "language": "it-IT", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Alice", "name": "Alice", "language": "it-IT", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Paola", "name": "Paola", "language": "it-IT", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Luca", "name": "Luca", "language": "it-IT", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Voce Google femminile", "name": "Google italiano", "language": "it-IT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Elsa", "name": "Microsoft Elsa - Italian (Italy)", "language": "it-IT", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Cosimo", "name": "Microsoft Cosimo - Italian (Italy)", "language": "it-IT", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce femminile 1", "name": "Google italiano 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google it-it-x-itb-network", "Chrome OS italiano 2", "Android Speech Recognition and Synthesis from Google it-it-x-itb-local", "Android Speech Recognition and Synthesis from Google it-IT-language"], "language": "it-IT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce femminile 2", "name": "Google italiano 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google it-it-x-kda-network", "Chrome OS italiano 1", "Android Speech Recognition and Synthesis from Google it-it-x-kda-local"], "language": "it-IT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce maschile 1", "name": "Google italiano 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google it-it-x-itc-network", "Chrome OS italiano 3", "Android Speech Recognition and Synthesis from Google it-it-x-itc-local"], "language": "it-IT", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce maschile 2", "name": "Google italiano 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google it-it-x-itd-network", "Chrome OS italiano 4", "Android Speech Recognition and Synthesis from Google it-it-x-itd-local"], "language": "it-IT", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce italiana", "name": "italiano Italia", "language": "it-IT", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Nanami", "name": "Microsoft Nanami Online (Natural) - Japanese (Japan)", "language": "ja-JP", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Keita", "name": "Microsoft Keita Online (Natural) - Japanese (Japan)", "language": "ja-JP", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "O-Ren", "name": "O-Ren", "language": "ja-JP", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Kyoko", "name": "Kyoko", "language": "ja-JP", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Otoya", "name": "Otoya", "language": "ja-JP", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Hattori", "name": "Hattori", "language": "ja-JP", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Google の女性の声", "name": "Google 日本語", "language": "ja-JP", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ayumi", "name": "Microsoft Ayumi - Japanese (Japan)", "language": "ja-JP", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Haruka", "name": "Microsoft Haruka - Japanese (Japan)", "language": "ja-JP", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ichiro", "name": "Microsoft Ichiro - Japanese (Japan)", "language": "ja-JP", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "女性の声1", "name": "Google 日本語 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ja-jp-x-htm-network", "Chrome OS 日本語 1", "Android Speech Recognition and Synthesis from Google ja-jp-x-htm-local", "Android Speech Recognition and Synthesis from Google ja-JP-language"], "language": "ja-JP", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "女性の声2", "name": "Chrome OS 日本語 2", "altNames": ["Android Speech Recognition and Synthesis from Google ja-jp-x-jab-network", "Android Speech Recognition and Synthesis from Google ja-jp-x-jab-local"], "language": "ja-JP", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "男性の声1", "name": "Google 日本語 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ja-jp-x-jac-network", "Chrome OS 日本語 3", "Android Speech Recognition and Synthesis from Google ja-jp-x-jac-local"], "language": "ja-JP", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "男性の声2", "name": "Google 日本語 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ja-jp-x-jad-network", "Chrome OS 日本語 4", "Android Speech Recognition and Synthesis from Google ja-jp-x-jad-local"], "language": "ja-JP", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "日本の音声", "name": "日本語 日本", "language": "ja-JP", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "SunHi", "name": "Microsoft SunHi Online (Natural) - Korean (Korea)", "language": "ko-KR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Hyunsu", "name": "Microsoft Hyunsu Online (Natural) - Korean (Korea)", "language": "ko-KR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "InJoon", "name": "Microsoft InJoon Online (Natural) - Korean (Korea)", "language": "ko-KR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Yuna", "name": "Yuna", "language": "ko-KR", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jian", "name": "Jian", "language": "ko-KR", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Suhyun", "name": "Suhyun", "language": "ko-KR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Sora", "name": "Sora", "language": "ko-KR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Minsu", "name": "Minsu", "language": "ko-KR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Google 여성 음성", "name": "Google 한국의", "language": "ko-KR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Heami", "name": "Microsoft Heami - Korean (Korea)", "language": "ko-KR", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "여성 목소리 1", "name": "Google 한국어 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ko-kr-x-kob-network", "Chrome OS 한국어 2", "Android Speech Recognition and Synthesis from Google ko-kr-x-kob-local", "Android Speech Recognition and Synthesis from Google ko-KR-language"], "language": "ko-KR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "여성 목소리 2", "name": "Google 한국어 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ko-kr-x-ism-network", "Chrome OS 한국어 1", "Android Speech Recognition and Synthesis from Google ko-kr-x-ism-local"], "language": "ko-KR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "남성 1", "name": "Google 한국어 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ko-kr-x-koc-network", "Chrome OS 한국어 3", "Android Speech Recognition and Synthesis from Google ko-kr-x-koc-local"], "language": "ko-KR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "남성 2", "name": "Google 한국어 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google ko-kr-x-kod-network", "Chrome OS 한국어 4", "Android Speech Recognition and Synthesis from Google ko-kr-x-kod-local"], "language": "ko-KR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "한국어 음성", "name": "한국어 대한민국", "language": "ko-KR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Pernille", "name": "Microsoft Pernille Online (Natural) - Norwegian (Bokmål, Norway)", "language": "nb-NO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Finn", "name": "Microsoft Finn Online (Natural) - Norwegian (Bokmål Norway)", "language": "nb-NO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Nora", "name": "Nora", "language": "nb-NO", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Henrik", "name": "Henrik", "language": "nb-NO", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Jon", "name": "Microsoft Jon - Norwegian (Bokmål Norway)", "language": "nb-NO", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnestemme 1", "name": "Google Norsk Bokmål 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-cfl-network", "Chrome OS Norsk Bokmål 2", "Android Speech Recognition and Synthesis from Google nb-no-x-cfl-local", "Android Speech Recognition and Synthesis from Google nb-NO-language"], "language": "nb-NO", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnestemme 2", "name": "Google Norsk Bokmål 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-rfj-network", "Chrome OS Norsk Bokmål 1", "Android Speech Recognition and Synthesis from Google nb-no-x-rfj-local"], "language": "nb-NO", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnestemme 3", "name": "Google Norsk Bokmål 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-tfs-network", "Chrome OS Norsk Bokmål 4", "Android Speech Recognition and Synthesis from Google nb-no-x-tfs-local"], "language": "nb-NO", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannsstemme 1", "name": "Google Norsk Bokmål 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-cmj-network", "Chrome OS Norsk Bokmål 3", "Android Speech Recognition and Synthesis from Google nb-no-x-cmj-local"], "language": "nb-NO", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannsstemme 2", "name": "Google Norsk Bokmål 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nb-no-x-tmg-network", "Chrome OS Norsk Bokmål 5", "Android Speech Recognition and Synthesis from Google nb-no-x-tmg-local"], "language": "nb-NO", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Norsk stemme", "name": "norsk bokmål Norge", "language": "nb-NO", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Colette (Nederland)", "name": "Microsoft Colette Online (Natural) - Dutch (Netherlands)", "language": "nl-NL", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Fenna (Nederland)", "name": "Microsoft Fenna Online (Natural) - Dutch (Netherlands)", "language": "nl-NL", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Maarten (Nederland)", "name": "Microsoft Maarten Online (Natural) - Dutch (Netherlands)", "language": "nl-NL", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Dena (België)", "name": "Microsoft Dena Online (Natural) - Dutch (Belgium)", "language": "nl-BE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Arnaud (België)", "name": "Microsoft Arnaud Online (Natural) - Dutch (Belgium)", "language": "nl-BE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Claire (Nederland)", "name": "Claire", "language": "nl-NL", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Xander (Nederland)", "name": "Xander", "language": "nl-NL", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Ellen (België)", "name": "Ellen", "language": "nl-BE", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Google mannelijke stem (Nederland)", "name": "Google Nederlands", "language": "nl-NL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Frank (Nederland)", "name": "Microsoft Frank - Dutch (Netherlands)", "language": "nl-NL", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Vrouwelijke stem 1 (Nederland)", "name": "Google Nederlands 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-lfc-network", "Chrome OS Nederlands 4", "Android Speech Recognition and Synthesis from Google nl-nl-x-lfc-local", "Android Speech Recognition and Synthesis from Google nl-NL-language"], "language": "nl-NL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Vrouwelijke stem 2 (Nederland)", "name": "Google Nederlands 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-tfb-network", "Chrome OS Nederlands 1", "Android Speech Recognition and Synthesis from Google nl-nl-x-tfb-local"], "language": "nl-NL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Vrouwelijke stem 3 (Nederland)", "name": "Google Nederlands 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-yfr-network", "Chrome OS Nederlands 5", "Android Speech Recognition and Synthesis from Google nl-nl-x-yfr-local"], "language": "nl-NL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannelijke stem 1 (Nederland)", "name": "Google Nederlands 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-bmh-network", "Chrome OS Nederlands 2", "Android Speech Recognition and Synthesis from Google nl-nl-x-bmh-local"], "language": "nl-NL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannelijke stem 2 (Nederland)", "name": "Google Nederlands 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google nl-nl-x-dma-network", "Chrome OS Nederlands 3", "Android Speech Recognition and Synthesis from Google nl-nl-x-dma-local"], "language": "nl-NL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Vrouwelijke stem (België)", "name": "Android Speech Recognition and Synthesis from Google nl-be-x-bec-network", "altNames": ["Android Speech Recognition and Synthesis from Google nl-be-x-bec-local", "Android Speech Recognition and Synthesis from Google nl-BE-language"], "language": "nl-NL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mannelijke stem (België)", "name": "Android Speech Recognition and Synthesis from Google nl-be-x-bed-network", "altNames": ["Android Speech Recognition and Synthesis from Google nl-be-x-bed-local"], "language": "nl-NL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Nederlandse stem (Nederland)", "name": "Nederlands Nederland", "language": "nl-NL", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Nederlandse stem (België)", "name": "Nederlands België", "language": "nl-BE", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Zofia", "name": "Microsoft Zofia Online (Natural) - Polish (Poland)", "language": "pl-PL", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Marek", "name": "Microsoft Marek Online (Natural) - Polish (Poland)", "language": "pl-PL", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ewa", "name": "Ewa", "language": "pl-PL", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Zosia", "name": "Zosia", "language": "pl-PL", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Krzysztof", "name": "Krzysztof", "language": "pl-PL", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Żeński głos Google’a", "name": "Google polski", "language": "pl-PL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Paulina", "name": "Microsoft Paulina - Polish (Poland)", "language": "pl-PL", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Adam", "name": "Microsoft Adam - Polish (Poland)", "language": "pl-PL", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos żeński 1", "name": "Google Polski 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-afb-network", "Chrome OS Polski 2", "Android Speech Recognition and Synthesis from Google pl-pl-x-afb-local", "Android Speech Recognition and Synthesis from Google pl-PL-language"], "language": "pl-PL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos żeński 2", "name": "Google Polski 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-oda-network", "Chrome OS Polski 1", "Android Speech Recognition and Synthesis from Google pl-pl-x-oda-local"], "language": "pl-PL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos żeński 3", "name": "Google Polski 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-zfg-network", "Chrome OS Polski 5", "Android Speech Recognition and Synthesis from Google pl-pl-x-zfg-local"], "language": "pl-PL", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos męski 1", "name": "Google Polski 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-bmg-network", "Chrome OS Polski 3", "Android Speech Recognition and Synthesis from Google pl-pl-x-bmg-local"], "language": "pl-PL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos męski 2", "name": "Google Polski 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pl-pl-x-jmk-network", "Chrome OS Polski 4", "Android Speech Recognition and Synthesis from Google pl-pl-x-jmk-local"], "language": "pl-PL", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Głos polski", "name": "polski Polska", "language": "pl-PL", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Raquel (Portugal)", "name": "Microsoft Raquel Online (Natural) - Portuguese (Portugal)", "language": "pt-PT", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Duarte (Portugal)", "name": "Microsoft Duarte Online (Natural) - Portuguese (Portugal)", "language": "pt-PT", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Francisca (Brasil)", "name": "Microsoft Francisca Online (Natural) - Portuguese (Brazil)", "language": "pt-BR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Thalita (Brasil)", "name": "Microsoft Thalita Online (Natural) - Portuguese (Brazil)", "language": "pt-BR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Antonio (Brasil)", "name": "Microsoft Antonio Online (Natural) - Portuguese (Brazil)", "language": "pt-BR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Catarina (Portugal)", "name": "Catarina", "language": "pt-PT", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Joana (Portugal)", "name": "Joana", "language": "pt-PT", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Joaquim (Portugal)", "name": "Joaquim", "language": "pt-PT", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Fernanda (Brasil)", "name": "Fernanda", "language": "pt-BR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Luciana (Brasil)", "name": "Luciana", "language": "pt-BR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Felipe (Brasil)", "name": "Felipe", "language": "pt-BR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Voz Google (Brasil)", "name": "Google português do Brasil", "language": "pt-BR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Helia (Portugal)", "name": "Microsoft Helia - Portuguese (Portugal)", "language": "pt-PT", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Maria (Brasil)", "name": "Microsoft Maria - Portuguese (Brazil)", "language": "pt-BR", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Daniel (Brasil)", "name": "Microsoft Daniel - Portuguese (Brazil)", "language": "pt-BR", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz feminina 1 (Portugal)", "name": "Google português de Portugal 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-pt-x-jfb-network", "Android Speech Recognition and Synthesis from Google pt-pt-x-jfb-local", "Android Speech Recognition and Synthesis from Google pt-PT-language"], "language": "pt-PT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz feminina 2 (Portugal)", "name": "Google português de Portugal 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-pt-x-sfs-network", "Chrome OS português de Portugal", "Android Speech Recognition and Synthesis from Google pt-pt-x-sfs-local"], "language": "pt-PT", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 1 (Portugal)", "name": "Google português de Portugal 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-pt-x-jmn-network", "Android Speech Recognition and Synthesis from Google pt-pt-x-jmn-local"], "language": "pt-PT", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina 2 (Portugal)", "name": "Google português de Portugal 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-pt-x-pmj-network", "Android Speech Recognition and Synthesis from Google pt-pt-x-pmj-local"], "language": "pt-PT", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz feminina 1 (Brasil)", "name": "Google português do Brasil 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-br-x-afs-network", "Chrome OS português do Brasil", "Android Speech Recognition and Synthesis from Google pt-br-x-afs-local", "Android Speech Recognition and Synthesis from Google pt-BR-language"], "language": "pt-BR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz feminina 2 (Brasil)", "name": "Google português do Brasil 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-br-x-pte-network", "Android Speech Recognition and Synthesis from Google pt-br-x-pte-local"], "language": "pt-BR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz masculina (Brasil)", "name": "Google português do Brasil 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google pt-br-x-ptd-network", "Android Speech Recognition and Synthesis from Google pt-br-x-ptd-local"], "language": "pt-BR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voz do Portugal", "name": "português Portugal", "language": "pt-PT", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Voz do Brasil", "name": "português Brasil", "language": "pt-BR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Alina", "name": "Microsoft Alina Online (Natural) - Romanian (Romania)", "language": "ro-RO", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Emil", "name": "Microsoft Emil Online (Natural) - Romanian (Romania)", "language": "ro-RO", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Iona", "name": "Iona", "language": "ro-RO", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Andrei", "name": "Microsoft Andrei - Romanian (Romania)", "language": "ro-RO", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce feminină", "name": "Android Speech Recognition and Synthesis from Google ro-ro-x-vfv-network", "altNames": ["Android Speech Recognition and Synthesis from Google ro-ro-x-vfv-local", "Android Speech Recognition and Synthesis from Google ro-RO-language"], "language": "ro-RO", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Voce românească", "name": "română România", "language": "ro-RO", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Svetlana", "name": "Microsoft Svetlana Online (Natural) - Russian (Russia)", "language": "ru-RU", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Dmitry", "name": "Microsoft Dmitry Online (Natural) - Russian (Russia)", "language": "ru-RU", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Katya", "name": "Katya", "language": "ru-RU", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Milena", "name": "Milena", "language": "ru-RU", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Yuri", "name": "Yuri", "language": "ru-RU", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Google женский голос", "name": "Google русский", "language": "ru-RU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Irina", "name": "Microsoft Irina - Russian (Russian)", "language": "ru-RU", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Pavel", "name": "Microsoft Pavel - Russian (Russian)", "language": "ru-RU", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Женский голос 1", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-dfc-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-dfc-local"], "language": "ru-RU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Женский голос 2", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-ruc-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-ruc-local"], "language": "ru-RU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Женский голос 3", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-rue-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-rue-local"], "language": "ru-RU", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Мужской голос 1", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-rud-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-rud-local"], "language": "ru-RU", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Мужской голос 2", "name": "Android Speech Recognition and Synthesis from Google ru-ru-x-ruf-network", "altNames": ["Android Speech Recognition and Synthesis from Google ru-ru-x-ruf-local"], "language": "ru-RU", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Русский голос", "name": "русский Россия", "language": "ru-RU", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Viktoria", "name": "Microsoft Viktoria Online (Natural) - Slovak (Slovakia)", "language": "sk-SK", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lukas", "name": "Microsoft Lukas Online (Natural) - Slovak (Slovakia)", "language": "sk-SK", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Laura", "name": "Laura", "language": "sk-SK", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Filip", "name": "Microsoft Filip - Slovak (Slovakia)", "language": "sk-SK", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ženský hlas", "name": "Google Slovenčina (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sk-sk-x-sfk-network", "Android Speech Recognition and Synthesis from Google sk-sk-x-sfk-local", "Chrome OS Slovenčina", "Android Speech Recognition and Synthesis from Google sk-SK-language"], "language": "sk-SK", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Slovenský hlas", "name": "slovenčina Slovensko", "language": "sk-SK", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Petra", "name": "Microsoft Petra Online (Natural) - Slovenian (Slovenia)", "language": "sl-SI", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Rok", "name": "Microsoft Rok Online (Natural) - Slovenian (Slovenia)", "language": "sl-SI", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Tina", "name": "Tina", "language": "sl-SI", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Lado", "name": "Microsoft Lado - Slovenian (Slovenia)", "language": "sl-SI", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Ženski glas", "name": "Android Speech Recognition and Synthesis from Google sl-si-x-frm-local", "altNames": ["Android Speech Recognition and Synthesis from Google sl-SI-language"], "language": "sl-SI", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Sofie", "name": "Microsoft Sofie Online (Natural) - Swedish (Sweden)", "language": "sv-SE", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Mattias", "name": "Microsoft Mattias Online (Natural) - Swedish (Sweden)", "language": "sv-SE", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Klara", "name": "Klara", "language": "sv-SE", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Alva", "name": "Alva", "language": "sv-SE", "gender": "female", "quality": ["low", "normal", "high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Oskar", "name": "Oskar", "language": "sv-SE", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Bengt", "name": "Microsoft Bengt - Swedish (Sweden)", "language": "sv-SE", "gender": "female", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnlig röst 1", "name": "Google Svenska 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-lfs-network", "Chrome OS Svenska", "Android Speech Recognition and Synthesis from Google sv-se-x-lfs-local", "Android Speech Recognition and Synthesis from Google sv-SE-language"], "language": "sv-SE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnlig röst 2", "name": "Google Svenska 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-afp-network", "Android Speech Recognition and Synthesis from Google sv-se-x-afp-local"], "language": "sv-SE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kvinnlig röst 3", "name": "Google Svenska 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-cfg-network", "Android Speech Recognition and Synthesis from Google sv-se-x-cfg-local"], "language": "sv-SE", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mansröst 1", "name": "Google Svenska 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-cmh-network", "Android Speech Recognition and Synthesis from Google sv-se-x-cmh-local"], "language": "sv-SE", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Mansröst 2", "name": "Google Svenska 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google sv-se-x-dmc-network", "Android Speech Recognition and Synthesis from Google sv-se-x-dmc-local"], "language": "sv-SE", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Svensk röst", "name": "svenska Sverige", "language": "sv-SE", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Emel", "name": "Microsoft Emel Online (Natural) - Turkish (Turkey)", "language": "tr-TR", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ahmet", "name": "Microsoft Ahmet Online (Natural) - Turkish (Turkey)", "language": "tr-TR", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Yelda", "name": "Yelda", "altNames": ["Yelda (Geliştirilmiş)", "Yelda (Türkçe (Türkiye))"], "language": "tr-TR", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Cem", "name": "Cem", "language": "tr-TR", "gender": "male", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Tolga", "name": "Microsoft Tolga - Turkish (Turkey)", "language": "tr-TR", "gender": "male", "quality": ["normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kadın sesi 1", "name": "Google Türkçe 3 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-cfs-network", "Chrome OS Türkçe 3", "Android Speech Recognition and Synthesis from Google tr-tr-x-cfs-local", "Android Speech Recognition and Synthesis from Google tr-TR-language"], "language": "tr-TR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kadın sesi 2", "name": "Google Türkçe 4 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-efu-network", "Chrome OS Türkçe 4", "Android Speech Recognition and Synthesis from Google tr-tr-x-efu-local"], "language": "tr-TR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Kadın sesi 3", "name": "Google Türkçe 1 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-mfm-network", "Chrome OS Türkçe 1", "Android Speech Recognition and Synthesis from Google tr-tr-x-mfm-local"], "language": "tr-TR", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Erkek sesi 1", "name": "Google Türkçe 2 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-ama-network", "Chrome OS Türkçe 2", "Android Speech Recognition and Synthesis from Google tr-tr-x-ama-local"], "language": "tr-TR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Erkek sesi 2", "name": "Google Türkçe 5 (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google tr-tr-x-tmc-network", "Chrome OS Türkçe 5", "Android Speech Recognition and Synthesis from Google tr-tr-x-tmc-local"], "language": "tr-TR", "gender": "male", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Türkçe ses", "name": "Türkçe Türkiye", "language": "tr-TR", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }, { "label": "Polina", "name": "Microsoft Polina Online (Natural) - Ukrainian (Ukraine)", "language": "uk-UA", "gender": "female", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Ostap", "name": "Microsoft Ostap Online (Natural) - Ukrainian (Ukraine)", "language": "uk-UA", "gender": "male", "quality": ["veryHigh"], "localizedName": "" }, { "label": "Lesya", "name": "Lesya", "language": "uk-UA", "gender": "female", "quality": ["low", "normal"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "apple" }, { "label": "Жіночий голос", "name": "Google українська (Natural)", "altNames": ["Android Speech Recognition and Synthesis from Google uk-ua-x-hfd-network", "Chrome OS українська", "Android Speech Recognition and Synthesis from Google uk-ua-x-hfd-local", "Android Speech Recognition and Synthesis from Google uk-UA-language"], "language": "uk-UA", "gender": "female", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "" }, { "label": "Український голос", "name": "українська Україна", "language": "uk-UA", "quality": ["high"], "recommendedPitch": 1, "recommendedRate": 1, "localizedName": "android" }]; +export const quality = { "ca": { "normal": "millorada", "high": "prèmium" }, "cs": { "normal": "vylepšená verze", "high": "prémiový" }, "da": { "normal": "forbedret", "high": "høj kvalitet" }, "de": { "normal": "erweitert", "high": "premium" }, "el": { "normal": "βελτιωμένη", "high": "υψηλής ποιότητας" }, "en": { "normal": "Enhanced", "high": "Premium" }, "es": { "normal": "mejorada", "high": "premium" }, "fi": { "normal": "parannettu", "high": "korkealaatuinen" }, "fr": { "normal": "premium", "high": "de qualité" }, "hu": { "normal": "továbbfejlesztett", "high": "prémium" }, "hr": { "normal": "poboljšani", "high": "vrhunski" }, "it": { "normal": "ottimizzata", "high": "premium" }, "ja": { "normal": "拡張", "high": "プレミアム" }, "ko": { "normal": "고품질", "high": "프리미엄" }, "nb": { "normal": "forbedret", "high": "premium" }, "nl": { "normal": "verbeterd", "high": "premium" }, "pl": { "normal": "rozszerzony", "high": "premium" }, "pt": { "normal": "melhorada", "high": "premium" }, "ro": { "normal": "îmbunătățită", "high": "premium" }, "ru": { "normal": "улучшенный", "high": "высшее качество" }, "sk": { "normal": "vylepšený", "high": "prémiový" }, "sl": { "normal": "izboljšano", "high": "prvovrsten" }, "sv": { "normal": "förbättrad", "high": "premium" }, "tr": { "normal": "Geliştirilmiş", "high": "Yüksek Kaliteli" }, "uk": { "normal": "вдосконалений", "high": "високої якості" } }; +export const defaultRegion = { "bg": "bg-BG", "ca": "ca-ES", "cs": "cs-CZ", "da": "da-DK", "de": "de-DE", "en": "en-US", "es": "es-ES", "eu": "eu-ES", "fi": "fi-FI", "fr": "fr-FR", "gl": "gl-ES", "hr": "hr-HR", "hu": "hu-HU", "it": "it-IT", "ja": "ja-JP", "ko": "ko-KR", "nb": "nb-NO", "nl": "nl-NL", "pl": "pl-PL", "pt": "pt-BR", "ro": "ro-RO", "ru": "ru-RU", "sk": "sk-SK", "sl": "sl-SI", "sv": "sv-SE", "tr": "tr-TR", "uk": "uk-UA" }; diff --git a/build/mjs/src/index.d.ts b/build/mjs/src/index.d.ts new file mode 100644 index 0000000..95a6baf --- /dev/null +++ b/build/mjs/src/index.d.ts @@ -0,0 +1 @@ +export * as voicesSelection from "./voices.js"; diff --git a/build/mjs/src/index.js b/build/mjs/src/index.js new file mode 100644 index 0000000..95a6baf --- /dev/null +++ b/build/mjs/src/index.js @@ -0,0 +1 @@ +export * as voicesSelection from "./voices.js"; diff --git a/build/mjs/src/voices.d.ts b/build/mjs/src/voices.d.ts new file mode 100644 index 0000000..3d766f4 --- /dev/null +++ b/build/mjs/src/voices.d.ts @@ -0,0 +1,46 @@ +import { TGender, TQuality, IRecommended } from "./data.js"; +export interface IVoices { + label: string; + voiceURI: string; + name: string; + language: string; + gender?: TGender | undefined; + age?: string | undefined; + offlineAvailability: boolean; + quality?: TQuality | undefined; + pitchControl: boolean; + recommendedPitch?: number | undefined; + recommendedRate?: number | undefined; +} +export declare function getSpeechSynthesisVoices(): Promise; +export declare function parseSpeechSynthesisVoices(speechSynthesisVoices: SpeechSynthesisVoice[]): IVoices[]; +export declare function filterOnOfflineAvailability(voices: IVoices[], offline?: boolean): IVoices[]; +export declare function filterOnGender(voices: IVoices[], gender: TGender): IVoices[]; +export declare function filterOnLanguage(voices: IVoices[], language: string | string[]): IVoices[]; +export declare function filterOnQuality(voices: IVoices[], quality: TQuality | TQuality[]): IVoices[]; +export declare function filterOnNovelty(voices: IVoices[]): IVoices[]; +export declare function filterOnVeryLowQuality(voices: IVoices[]): IVoices[]; +export type TReturnFilterOnRecommended = [voicesRecommended: IVoices[], voicesLowerQuality: IVoices[]]; +export declare function filterOnRecommended(voices: IVoices[], _recommended?: IRecommended[]): TReturnFilterOnRecommended; +export declare function sortByQuality(voices: IVoices[]): IVoices[]; +export declare function sortByName(voices: IVoices[]): IVoices[]; +export declare function sortByGender(voices: IVoices[], genderFirst: TGender): IVoices[]; +export declare function sortByLanguage(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): IVoices[]; +export declare function sortByRegion(voices: IVoices[], preferredRegions?: string[] | string, localization?: string | undefined): IVoices[]; +export interface ILanguages { + label: string; + code: string; + count: number; +} +export declare function listLanguages(voices: IVoices[], localization?: string | undefined): ILanguages[]; +export declare function listRegions(voices: IVoices[], localization?: string | undefined): ILanguages[]; +export type TGroupVoices = Map; +export declare function groupByLanguages(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): TGroupVoices; +export declare function groupByRegions(voices: IVoices[], preferredRegions?: string[] | string, localization?: string | undefined): TGroupVoices; +export declare function groupByKindOfVoices(allVoices: IVoices[]): TGroupVoices; +export declare function getLanguages(voices: IVoices[], preferredLanguage?: string[] | string, localization?: string | undefined): ILanguages[]; +/** + * Parse and extract SpeechSynthesisVoices, + * @returns IVoices[] + */ +export declare function getVoices(preferredLanguage?: string[] | string, localization?: string): Promise; diff --git a/build/mjs/src/voices.js b/build/mjs/src/voices.js new file mode 100644 index 0000000..59fe4b0 --- /dev/null +++ b/build/mjs/src/voices.js @@ -0,0 +1,412 @@ +import { novelty, quality, recommended, veryLowQuality, defaultRegion } from "./data.js"; +// export type TOS = 'Android' | 'ChromeOS' | 'iOS' | 'iPadOS' | 'macOS' | 'Windows'; +// export type TBrowser = 'ChromeDesktop' | 'Edge' | 'Firefox' | 'Safari'; +const navigatorLanguages = () => window?.navigator?.languages || []; +const navigatorLang = () => (navigator?.language || "").split("-")[0].toLowerCase(); +const normalQuality = Object.values(quality).map(({ normal }) => normal); +const highQuality = Object.values(quality).map(({ high }) => high); +function compareQuality(a, b) { + const qualityToNumber = (quality) => { + switch (quality) { + case "veryLow": { + return 0; + } + case "low": { + return 1; + } + case "normal": { + return 2; + } + case "high": { + return 3; + } + case "veryHigh": { + return 4; + } + default: + { + return -1; + } + ; + } + }; + return qualityToNumber(b || "low") - qualityToNumber(a || "low"); +} +; +export async function getSpeechSynthesisVoices() { + const a = () => speechSynthesis.getVoices(); + const voices = a(); + if (Array.isArray(voices) && voices.length) + return voices; + return new Promise((resolve, _reject) => { + let counter = 1000; + const tick = () => { + if (counter < 1) + return resolve([]); + // console.log(counter); + --counter; + const voices = a(); + if (Array.isArray(voices) && voices.length) + return resolve(voices); + setTimeout(tick, 10); + }; + setTimeout(tick, 10); + }); +} +export function parseSpeechSynthesisVoices(speechSynthesisVoices) { + const parseAndFormatBCP47 = (lang) => { + const speechVoiceLang = lang.replace("_", "-"); + if (/\w{2,3}-\w{2,3}/.test(speechVoiceLang)) { + return `${speechVoiceLang.split("-")[0].toLowerCase()}-${speechVoiceLang.split("-")[1].toUpperCase()}`; + } + // bad formated !? + return lang; + }; + return speechSynthesisVoices.map((speechVoice) => ({ + label: speechVoice.name, + voiceURI: speechVoice.voiceURI, + name: speechVoice.name, + language: parseAndFormatBCP47(speechVoice.lang), + gender: undefined, + age: undefined, + offlineAvailability: speechVoice.localService, + quality: undefined, + pitchControl: true, + recommendedPitch: undefined, + recomendedRate: undefined, + })); +} +export function filterOnOfflineAvailability(voices, offline = true) { + return voices.filter(({ offlineAvailability }) => { + return offlineAvailability === offline; + }); +} +export function filterOnGender(voices, gender) { + return voices.filter(({ gender: voiceGender }) => { + return voiceGender === gender; + }); +} +export function filterOnLanguage(voices, language) { + language = Array.isArray(language) ? language : [language]; + language = language.map((l) => extractLangRegionFromBCP47(l)[0]); + return voices.filter(({ language: voiceLanguage }) => { + const [lang] = extractLangRegionFromBCP47(voiceLanguage); + return language.includes(lang); + }); +} +export function filterOnQuality(voices, quality) { + quality = Array.isArray(quality) ? quality : [quality]; + return voices.filter(({ quality: voiceQuality }) => { + return quality.some((qual) => qual === voiceQuality); + }); +} +export function filterOnNovelty(voices) { + return voices.filter(({ name }) => { + return !novelty.includes(name); + }); +} +export function filterOnVeryLowQuality(voices) { + return voices.filter(({ name }) => { + return !veryLowQuality.find((v) => name.startsWith(v)); + }); +} +function updateVoiceInfo(recommendedVoice, voice) { + voice.label = recommendedVoice.label; + voice.gender = recommendedVoice.gender; + voice.recommendedPitch = recommendedVoice.recommendedPitch; + voice.recommendedRate = recommendedVoice.recommendedRate; + return voice; +} +export function filterOnRecommended(voices, _recommended = recommended) { + const voicesRecommended = []; + const voicesLowerQuality = []; + recommendedVoiceLoop: for (const recommendedVoice of _recommended) { + if (Array.isArray(recommendedVoice.quality) && recommendedVoice.quality.length > 1) { + const voicesFound = voices.filter(({ name }) => name.startsWith(recommendedVoice.name)); + if (voicesFound.length) { + for (const qualityTested of ["high", "normal"]) { + for (let i = 0; i < voicesFound.length; i++) { + const voice = voicesFound[i]; + const rxp = /^.*\((.*)\)$/; + if (rxp.test(voice.name)) { + const res = rxp.exec(voice.name); + const maybeQualityString = res ? res[1] || "" : ""; + const qualityDataArray = qualityTested === "high" ? highQuality : normalQuality; + if (recommendedVoice.quality.includes(qualityTested) && qualityDataArray.includes(maybeQualityString)) { + voice.quality = qualityTested; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + voicesFound.splice(i, 1); + voicesLowerQuality.push(...(voicesFound.map((v) => { + v.quality = "low"; // Todo need to be more precise for 'normal' quality voices + return updateVoiceInfo(recommendedVoice, v); + }))); + continue recommendedVoiceLoop; + } + } + } + } + const voice = voicesFound[0]; + for (let i = 1; i < voicesFound.length; i++) { + voicesLowerQuality.push(voicesFound[i]); + } + voice.quality = voicesFound.length > 3 ? "veryHigh" : voicesFound.length > 2 ? "high" : "normal"; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + } + } + else if (Array.isArray(recommendedVoice.altNames) && recommendedVoice.altNames.length) { + const voiceFound = voices.find(({ name }) => name === recommendedVoice.name); + if (voiceFound) { + const voice = voiceFound; + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + // voice Name found so altNames array must be filter and push to voicesLowerQuality + const altNamesVoicesFound = voices.filter(({ name }) => recommendedVoice.altNames.includes(name)); + // TODO: Typescript bug type assertion doesn't work, need to force the compiler with the Non-null Assertion Operator + voicesLowerQuality.push(...(altNamesVoicesFound.map((v) => { + v.quality = recommendedVoice.quality[0]; + return updateVoiceInfo(recommendedVoice, v); + }))); + } + else { + // filter voices on altNames, keep the first and push the remaining to voicesLowerQuality + const altNamesVoicesFound = voices.filter(({ name }) => recommendedVoice.altNames.includes(name)); + if (altNamesVoicesFound.length) { + const voice = altNamesVoicesFound.shift(); + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + voicesLowerQuality.push(...(altNamesVoicesFound.map((v) => { + v.quality = recommendedVoice.quality[0]; + return updateVoiceInfo(recommendedVoice, v); + }))); + } + } + } + else { + const voiceFound = voices.find(({ name }) => name === recommendedVoice.name); + if (voiceFound) { + const voice = voiceFound; + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + } + } + } + return [voicesRecommended, voicesLowerQuality]; +} +const extractLangRegionFromBCP47 = (l) => [l.split("-")[0].toLowerCase(), l.split("-")[1]?.toUpperCase()]; +export function sortByQuality(voices) { + return voices.sort(({ quality: qa }, { quality: qb }) => { + return compareQuality(qa, qb); + }); +} +export function sortByName(voices) { + return voices.sort(({ name: na }, { name: nb }) => { + return na.localeCompare(nb); + }); +} +export function sortByGender(voices, genderFirst) { + return voices.sort(({ gender: ga }, { gender: gb }) => { + return ga === gb ? 0 : ga === genderFirst ? -1 : gb === genderFirst ? -1 : 1; + }); +} +function orderByPreferredLanguage(preferredLanguage) { + preferredLanguage = Array.isArray(preferredLanguage) ? preferredLanguage : + preferredLanguage ? [preferredLanguage] : []; + return [...(new Set([...preferredLanguage, ...navigatorLanguages()]))]; +} +function orderByPreferredRegion(preferredLanguage) { + preferredLanguage = Array.isArray(preferredLanguage) ? preferredLanguage : + preferredLanguage ? [preferredLanguage] : []; + const regionByDefaultArray = Object.values(defaultRegion); + return [...(new Set([...preferredLanguage, ...navigatorLanguages(), ...regionByDefaultArray]))]; +} +const getLangFromBCP47Array = (a) => { + return [...(new Set(a.map((v) => extractLangRegionFromBCP47(v)[0]).filter((v) => !!v)))]; +}; +const getRegionFromBCP47Array = (a) => { + return [...(new Set(a.map((v) => (extractLangRegionFromBCP47(v)[1] || "").toUpperCase()).filter((v) => !!v)))]; +}; +export function sortByLanguage(voices, preferredLanguage = [], localization = navigatorLang()) { + const languages = getLangFromBCP47Array(orderByPreferredLanguage(preferredLanguage)); + const voicesSorted = []; + for (const lang of languages) { + voicesSorted.push(...voices.filter(({ language: voiceLanguage }) => lang === extractLangRegionFromBCP47(voiceLanguage)[0])); + } + let langueName = undefined; + if (localization) { + try { + langueName = new Intl.DisplayNames([localization], { type: 'language' }); + } + catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + const remainingVoices = voices.filter((v) => !voicesSorted.includes(v)); + remainingVoices.sort(({ language: a }, { language: b }) => { + let nameA = a, nameB = b; + try { + if (langueName) { + nameA = langueName.of(extractLangRegionFromBCP47(a)[0]) || a; + nameB = langueName.of(extractLangRegionFromBCP47(b)[0]) || b; + } + } + catch (e) { + // ignore + } + return nameA.localeCompare(nameB); + }); + return [...voicesSorted, ...remainingVoices]; +} +export function sortByRegion(voices, preferredRegions = [], localization = navigatorLang()) { + const regions = getRegionFromBCP47Array(orderByPreferredRegion(preferredRegions)); + const voicesSorted = []; + for (const reg of regions) { + voicesSorted.push(...voices.filter(({ language: voiceLanguage }) => reg === extractLangRegionFromBCP47(voiceLanguage)[1])); + } + let regionName = undefined; + if (localization) { + try { + regionName = new Intl.DisplayNames([localization], { type: 'region' }); + } + catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + const remainingVoices = voices.filter((v) => !voicesSorted.includes(v)); + remainingVoices.sort(({ language: a }, { language: b }) => { + let nameA = a, nameB = b; + try { + if (regionName) { + nameA = regionName.of(extractLangRegionFromBCP47(a)[1]) || a; + nameB = regionName.of(extractLangRegionFromBCP47(b)[1]) || b; + } + } + catch (e) { + // ignore + } + return nameA.localeCompare(nameB); + }); + return [...voicesSorted, ...remainingVoices]; +} +export function listLanguages(voices, localization = navigatorLang()) { + let langueName = undefined; + if (localization) { + try { + langueName = new Intl.DisplayNames([localization], { type: 'language' }); + } + catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + return voices.reduce((acc, cv) => { + const [lang] = extractLangRegionFromBCP47(cv.language); + let name = lang; + try { + if (langueName) { + name = langueName.of(lang) || lang; + } + } + catch (e) { + console.error("langueName.of throw an error with ", lang, e); + } + const found = acc.find(({ code }) => code === lang); + if (found) { + found.count++; + } + else { + acc.push({ code: lang, count: 1, label: name }); + } + return acc; + }, []); +} +export function listRegions(voices, localization = navigatorLang()) { + let regionName = undefined; + if (localization) { + try { + regionName = new Intl.DisplayNames([localization], { type: 'region' }); + } + catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + return voices.reduce((acc, cv) => { + const [, region] = extractLangRegionFromBCP47(cv.language); + let name = region; + try { + if (regionName) { + name = regionName.of(region) || region; + } + } + catch (e) { + console.error("regionName.of throw an error with ", region, e); + } + const found = acc.find(({ code }) => code === region); + if (found) { + found.count++; + } + else { + acc.push({ code: region, count: 1, label: name }); + } + return acc; + }, []); +} +export function groupByLanguages(voices, preferredLanguage = [], localization = navigatorLang()) { + const voicesSorted = sortByLanguage(voices, preferredLanguage, localization); + const languagesStructure = listLanguages(voicesSorted, localization); + const res = new Map(); + for (const { code, label } of languagesStructure) { + res.set(label, voicesSorted + .filter(({ language: voiceLang }) => { + const [l] = extractLangRegionFromBCP47(voiceLang); + return l === code; + })); + } + return res; +} +export function groupByRegions(voices, preferredRegions = [], localization = navigatorLang()) { + const voicesSorted = sortByRegion(voices, preferredRegions, localization); + const languagesStructure = listRegions(voicesSorted, localization); + const res = new Map(); + for (const { code, label } of languagesStructure) { + res.set(label, voicesSorted + .filter(({ language: voiceLang }) => { + const [, r] = extractLangRegionFromBCP47(voiceLang); + return r === code; + })); + } + return res; +} +export function groupByKindOfVoices(allVoices) { + const [recommendedVoices, lowQualityVoices] = filterOnRecommended(allVoices); + const remainingVoice = allVoices.filter((v) => !recommendedVoices.includes(v) && !lowQualityVoices.includes(v)); + const noveltyFiltered = filterOnNovelty(remainingVoice); + const noveltyVoices = remainingVoice.filter((v) => !noveltyFiltered.includes(v)); + const veryLowQualityFiltered = filterOnVeryLowQuality(remainingVoice); + const veryLowQualityVoices = remainingVoice.filter((v) => !veryLowQualityFiltered.includes(v)); + const remainingVoiceFiltered = filterOnNovelty(filterOnVeryLowQuality(remainingVoice)); + const res = new Map(); + res.set("recommendedVoices", recommendedVoices); + res.set("lowerQuality", lowQualityVoices); + res.set("novelty", noveltyVoices); + res.set("veryLowQuality", veryLowQualityVoices); + res.set("remaining", remainingVoiceFiltered); + return res; +} +export function getLanguages(voices, preferredLanguage = [], localization = navigatorLang()) { + const group = groupByLanguages(voices, preferredLanguage, localization); + return Array.from(group.entries()).map(([label, _voices]) => { + return { label, count: _voices.length, code: extractLangRegionFromBCP47(_voices[0]?.language || "")[0] }; + }); +} +/** + * Parse and extract SpeechSynthesisVoices, + * @returns IVoices[] + */ +export async function getVoices(preferredLanguage, localization) { + const allVoices = parseSpeechSynthesisVoices(await getSpeechSynthesisVoices()); + const [recommendedVoices, lowQualityVoices] = filterOnRecommended(allVoices); + const remainingVoice = allVoices.filter((v) => !recommendedVoices.includes(v) && !lowQualityVoices.includes(v)); + const remainingVoiceFiltered = filterOnNovelty(filterOnVeryLowQuality(remainingVoice)); + const voices = [recommendedVoices, remainingVoiceFiltered].flat(); + const voicesSorted = sortByLanguage(sortByQuality(voices), preferredLanguage, localization || navigatorLang()); + return voicesSorted; +} diff --git a/build/mjs/test/voices.test.d.ts b/build/mjs/test/voices.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/build/mjs/test/voices.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/build/mjs/test/voices.test.js b/build/mjs/test/voices.test.js new file mode 100644 index 0000000..e24560d --- /dev/null +++ b/build/mjs/test/voices.test.js @@ -0,0 +1,524 @@ +import test from "ava"; +import { filterOnRecommended, groupByLanguages, sortByLanguage, groupByRegions } from "../src/voices.js"; +// import { getVoices } from "../src/voices.js"; +test('dumb test', t => { + t.deepEqual([], []); +}); +test.before(t => { + // This runs before all tests + globalThis.window = { navigator: { languages: [] } }; +}); +test('sortByLanguage: Empty preferred language list', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, [], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); +test('sortByLanguage: Preferred language list with one language', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, ['fr-FR'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'fr-FR'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'en-US'); +}); +test('sortByLanguage: Preferred language list with multiple languages', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, ['fr-FR', 'es-ES'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'fr-FR'); + t.true(result[1].language === 'es-ES'); + t.true(result[2].language === 'en-US'); + t.true(result[3].language === 'en-US'); +}); +test('sortByLanguage: No matching languages', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, ['de-DE'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); +test('sortByLanguage: Preferred language list is not an array', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, 'en-US', ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); +test('sortByLanguage: Preferred language undefined and navigator langua', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, 'en-US', ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); +test('sortByLanguage: Preferred language list with one language and navigator.languages', t => { + globalThis.window.navigator.languages = ['fr-FR', 'en-US']; + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, ['fr-FR'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'fr-FR'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'en-US'); +}); +test('sortByLanguage: Preferred language list with multiple languages and navigator.languages', t => { + globalThis.window.navigator.languages = ['fr-FR', 'en-US']; + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, ['fr-FR', 'es-ES'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'fr-FR'); + t.true(result[1].language === 'es-ES'); + t.true(result[2].language === 'en-US'); + t.true(result[3].language === 'en-US'); +}); +test('sortByLanguage: No matching languages and navigator.languages', t => { + globalThis.window.navigator.languages = ['de-DE', 'en-US']; + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, ['de-DE'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); +test('sortByLanguage: Preferred language list is not an array and navigator.languages', t => { + globalThis.window.navigator.languages = ['fr-FR', 'en-US']; + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const result = sortByLanguage(voices, 'en-US', ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); +test('filterOnRecommended: Empty input', t => { + const voices = []; + const result = filterOnRecommended(voices); + t.deepEqual(result, [[], []]); +}); +test('filterOnRecommended: No recommended voices', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + ]; + const result = filterOnRecommended(voices, []); + t.deepEqual(result, [[], []]); +}); +test('filterOnRecommended: Single recommended voice with single quality', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + ]; + const recommended = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], language: 'en-US', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [], + ]); +}); +test('filterOnRecommended: Single recommended voice with multiple qualities', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + ]; + const recommended = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high', 'normal'], language: 'en-US', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [], + ]); +}); +test('filterOnRecommended: Single recommended voice with multiple qualities and remaining lowQuality', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 1 (Premium)', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const recommended = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high', 'normal'], language: 'en-US', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri3', name: 'Name 1 (Premium)', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'low', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + ]); +}); +test('filterOnRecommended: Multiple recommended voices', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + ]; + const recommended = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], language: 'en-US', localizedName: "" }, + { name: 'Name 2', label: 'Voice 2', quality: ['normal'], language: 'es-ES', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [], + ]); +}); +test('filterOnRecommended: Recommended voices with altNames', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 1-1', voiceURI: 'uri1-1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + ]; + const recommended = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], altNames: ['Name 1 with an altNames'], language: 'en-US', localizedName: "" }, + { name: 'Name 2', label: 'Voice 2', quality: ['normal'], language: 'es-ES', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [ + { label: 'Voice 1', voiceURI: 'uri1-1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + ]); +}); +test('filterOnRecommended: Recommended voices with altNames only and voices not in name', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + ]; + const recommended = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], altNames: ['Name 1 with an altNames'], language: 'en-US', localizedName: "" }, + { name: 'Name 2', label: 'Voice 2', quality: ['normal'], language: 'es-ES', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [], + ]); +}); +test('filterOnRecommended: Recommended voices with multiple altNames and voices not in name', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 1-1', voiceURI: 'uri1-1', name: 'Name 1 with a second altNames', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + ]; + const recommended = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], altNames: ['Name 1 with an altNames', 'Name 1 with a second altNames'], language: 'en-US', localizedName: "" }, + { name: 'Name 2', label: 'Voice 2', quality: ['normal'], language: 'es-ES', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [ + { label: 'Voice 1', voiceURI: 'uri1-1', name: 'Name 1 with a second altNames', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + ]); +}); +test('groupByLanguage: ', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + ]; + const result = groupByLanguages(voices, ['fr-FR', 'es-ES'], ""); + t.deepEqual(result, new Map([ + ['fr', [ + { + label: 'Voice 2', + language: 'fr-FR', + name: 'Name 2', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri2', + }, + ]], + ['es', [ + { + label: 'Voice 4', + language: 'es-ES', + name: 'Name 4', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri4', + }, + ]], + ['en', [ + { + label: 'Voice 1', + language: 'en-US', + name: 'Name 1', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri1', + }, + { + label: 'Voice 3', + language: 'en-US', + name: 'Name 3', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri3', + }, + ]], + ])); +}); +test('groupByLanguage: localized en', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + ]; + const result = groupByLanguages(voices, ['fr-FR', 'es-ES'], "en"); + t.deepEqual(result, new Map([ + ['French', [ + { + label: 'Voice 2', + language: 'fr-FR', + name: 'Name 2', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri2', + }, + ]], + ['Spanish', [ + { + label: 'Voice 4', + language: 'es-ES', + name: 'Name 4', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri4', + }, + ]], + ['English', [ + { + label: 'Voice 1', + language: 'en-US', + name: 'Name 1', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri1', + }, + { + label: 'Voice 3', + language: 'en-US', + name: 'Name 3', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri3', + }, + ]], + ])); +}); +test('groupByRegion: ', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-GB', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 5', voiceURI: 'uri5', name: 'Name 5', language: 'en-CA', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 6', voiceURI: 'uri6', name: 'Name 6', language: 'fr-CA', offlineAvailability: true, pitchControl: true }, + ]; + const result = groupByRegions(voices, ['fr-FR', 'es-ES'], ""); + t.deepEqual(result, new Map([ + ['FR', [ + { + label: 'Voice 2', + language: 'fr-FR', + name: 'Name 2', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri2', + }, + ]], + ['ES', [ + { + label: 'Voice 4', + language: 'es-ES', + name: 'Name 4', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri4', + }, + ]], + ['US', [ + { + label: 'Voice 1', + language: 'en-US', + name: 'Name 1', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri1', + }, + ]], + ['CA', [ + { + label: 'Voice 5', + language: 'en-CA', + name: 'Name 5', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri5', + }, + { + label: 'Voice 6', + language: 'fr-CA', + name: 'Name 6', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri6', + }, + ]], + ['GB', [ + { + label: 'Voice 3', + language: 'en-GB', + name: 'Name 3', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri3', + }, + ]], + ])); +}); +test('groupByRegion: localized fr', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-GB', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 5', voiceURI: 'uri5', name: 'Name 5', language: 'en-CA', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 6', voiceURI: 'uri6', name: 'Name 6', language: 'fr-CA', offlineAvailability: true, pitchControl: true }, + ]; + const result = groupByRegions(voices, ['fr-FR', 'es-ES'], "fr"); + t.deepEqual(result, new Map([ + ['France', [ + { + label: 'Voice 2', + language: 'fr-FR', + name: 'Name 2', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri2', + }, + ]], + ['Espagne', [ + { + label: 'Voice 4', + language: 'es-ES', + name: 'Name 4', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri4', + }, + ]], + ['États-Unis', [ + { + label: 'Voice 1', + language: 'en-US', + name: 'Name 1', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri1', + }, + ]], + ['Canada', [ + { + label: 'Voice 5', + language: 'en-CA', + name: 'Name 5', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri5', + }, + { + label: 'Voice 6', + language: 'fr-CA', + name: 'Name 6', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri6', + }, + ]], + ['Royaume-Uni', [ + { + label: 'Voice 3', + language: 'en-GB', + name: 'Name 3', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri3', + }, + ]], + ])); +}); diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..58f63ca --- /dev/null +++ b/demo/index.html @@ -0,0 +1,15 @@ + + + + + + + Readium Speech Demo + + + + + + + + \ No newline at end of file diff --git a/demo/lit-html_3-2-0_esm.js b/demo/lit-html_3-2-0_esm.js new file mode 100644 index 0000000..93b0419 --- /dev/null +++ b/demo/lit-html_3-2-0_esm.js @@ -0,0 +1,12 @@ +/** + * Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2. + * Original file: /npm/lit-html@3.2.0/lit-html.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +const t=globalThis,e=t.trustedTypes,s=e?e.createPolicy("lit-html",{createHTML:t=>t}):void 0,i="$lit$",n=`lit$${Math.random().toFixed(9).slice(2)}$`,o="?"+n,r=`<${o}>`,h=document,l=()=>h.createComment(""),$=t=>null===t||"object"!=typeof t&&"function"!=typeof t,a=Array.isArray,A=t=>a(t)||"function"==typeof t?.[Symbol.iterator],c="[ \t\n\f\r]",_=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,d=/-->/g,p=/>/g,u=RegExp(`>|${c}(?:([^\\s"'>=/]+)(${c}*=${c}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),g=/'/g,v=/"/g,f=/^(?:script|style|textarea|title)$/i,m=t=>(e,...s)=>({_$litType$:t,strings:e,values:s}),y=m(1),H=m(2),x=m(3),N=Symbol.for("lit-noChange"),T=Symbol.for("lit-nothing"),b=new WeakMap,M=h.createTreeWalker(h,129);function w(t,e){if(!a(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==s?s.createHTML(e):e}const S=(t,e)=>{const s=t.length-1,o=[];let h,l=2===e?"":3===e?"":"",$=_;for(let e=0;e"===A[0]?($=h??_,c=-1):void 0===A[1]?c=-2:(c=$.lastIndex-A[2].length,a=A[1],$=void 0===A[3]?u:'"'===A[3]?v:g):$===v||$===g?$=u:$===d||$===p?$=_:($=u,h=void 0);const y=$===u&&t[e+1].startsWith("/>")?" ":"";l+=$===_?s+r:c>=0?(o.push(a),s.slice(0,c)+i+s.slice(c)+n+y):s+n+(-2===c?e:y)}return[w(t,l+(t[s]||"")+(2===e?"":3===e?"":"")),o]};class I{constructor({strings:t,_$litType$:s},r){let h;this.parts=[];let $=0,a=0;const A=t.length-1,c=this.parts,[_,d]=S(t,s);if(this.el=I.createElement(_,r),M.currentNode=this.el.content,2===s||3===s){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(h=M.nextNode())&&c.length0){h.textContent=e?e.emptyScript:"";for(let e=0;e2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=T}_$AI(t,e=this,s,i){const n=this.strings;let o=!1;if(void 0===n)t=C(this,t,e,0),o=!$(t)||t!==this._$AH&&t!==N,o&&(this._$AH=t);else{const i=t;let r,h;for(t=n[0],r=0;r{const i=s?.renderBefore??e;let n=i._$litPart$;if(void 0===n){const t=s?.renderBefore??null;i._$litPart$=n=new B(e.insertBefore(l(),t),t,void 0,s??{})}return n._$AI(t),n};export{W as _$LH,y as html,x as mathml,N as noChange,T as nothing,D as render,H as svg};export default null; \ No newline at end of file diff --git a/demo/script.js b/demo/script.js new file mode 100644 index 0000000..2475031 --- /dev/null +++ b/demo/script.js @@ -0,0 +1,215 @@ + +import { voicesSelection } from "../build/mjs/src/index.js"; +const { getSpeechSynthesisVoices, parseSpeechSynthesisVoices, filterOnNovelty, filterOnVeryLowQuality, + filterOnRecommended, sortByLanguage, sortByQuality, getVoices, groupByKindOfVoices, groupByRegions, + getLanguages, filterOnOfflineAvailability, listLanguages, filterOnGender, filterOnLanguage } = voicesSelection; + +import * as lit from './lit-html_3-2-0_esm.js' +const { html, render } = lit; + +async function loadJSONData(url) { + try { + const response = await fetch(url); + const jsonData = JSON.parse(await response.text()); + return jsonData; + } catch (error) { + console.error('Error loading JSON data:', error); + return null; + } +} + +function downloadJSON(obj, filename) { + // Convert the JSON object to a string + const data = JSON.stringify(obj, null, 2); + + // Create a blob from the string + const blob = new Blob([data], { type: "application/json" }); + + // Generate an object URL + const jsonObjectUrl = URL.createObjectURL(blob); + + // Create an anchor element + const anchorEl = document.createElement("a"); + anchorEl.href = jsonObjectUrl; + anchorEl.download = `${filename}.json`; + + // Simulate a click on the anchor element + anchorEl.click(); + + // Revoke the object URL + URL.revokeObjectURL(jsonObjectUrl); +} + +const viewRender = () => render(content(), document.body); + +const voices = await getVoices(); +console.log(voices); + +const languages = getLanguages(voices); + +let voicesFiltered = voices; +let languagesFiltered = languages; + +let textToRead = ""; +let textToReadFormated = ""; + +let selectedLanguage = undefined; + +let voicesSelectElem = []; + +let selectedVoice = ""; + +let selectedGender = "all"; + +let checkboxOfflineChecked = false; + +const readTextWithSelectedVoice = () => { + const voices = window.speechSynthesis.getVoices(); + + const utterance = new SpeechSynthesisUtterance(); + utterance.text = textToReadFormated; + + for (const voice of voices) { + if (voice.name === selectedVoice) { + utterance.voice = voice; + utterance.lang = voice.lang; + break; + } + } + + if (!utterance.voice) { + console.error("Speech : Voice NOT FOUND"); + alert("voice not found"); + } + + console.log("Speech", utterance); + + + speechSynthesis.speak(utterance); +} + +const filterVoices = () => { + + voicesFiltered = voices; + + if (selectedGender !== "all") { + voicesFiltered = filterOnGender(voicesFiltered, selectedGender); + } + + if (checkboxOfflineChecked) { + voicesFiltered = filterOnOfflineAvailability(voicesFiltered, true); + } + + languagesFiltered = getLanguages(voicesFiltered); + + const voicesFilteredOnLanguage = filterOnLanguage(voicesFiltered, selectedLanguage); + const voicesGroupedByRegions = groupByRegions(voicesFilteredOnLanguage); + + voicesSelectElem = listVoicesWithLanguageSelected(voicesGroupedByRegions); + + viewRender(); +} + +const setSelectVoice = (name) => { + + selectedVoice = name; + textToReadFormated = textToRead.replace("{name}", selectedVoice); +} + +const languageSelectOnChange = async (ev) => { + + selectedLanguage = ev.target.value; + + const jsonData = await loadJSONData("https://raw.githubusercontent.com/HadrienGardeur/web-speech-recommended-voices/main/json/" + selectedLanguage + ".json"); + + textToRead = jsonData?.testUtterance || ""; + + filterVoices(); +} + +const listVoicesWithLanguageSelected = (voiceMap) => { + + const elem = []; + selectedVoice = ""; + + for (const [region, voice] of voiceMap) { + const option = []; + + for (const {name, label} of voice) { + option.push(html``); + if (!selectedVoice) setSelectVoice(name); + } + elem.push(html` + + ${option} + + `) + } + + return elem; +} + +const aboutVoice = () => { + return html` +
+

${JSON.stringify(voicesFiltered.filter(({name}) => name === selectedVoice, null, 4))}

+
+ `; +} + +const getVoicesInputForDebug = () => { + const a = window.speechSynthesis.getVoices() || []; + return a.map(({ default: def, lang, localService, name, voiceURI}) => ({default: def, lang, localService, name, voiceURI})); +} + +const content = () => html` +

ReadiumSpeech

+ +

Language :

+ + +

Voices :

+ + +

Gender :

+ + +

Filter :

+
+ { + checkboxOfflineChecked = e.target.checked; + filterVoices(); + }}> + +
+ +

Text :

+ textToReadFormated = e.target.value ? e.target.value : textToReadFormated}> + +
+ +
+ +
+ ${selectedVoice ? aboutVoice() : undefined} +
+ +
+ +
+ +`; +viewRender(); \ No newline at end of file diff --git a/demo/styles.css b/demo/styles.css new file mode 100644 index 0000000..55577b6 --- /dev/null +++ b/demo/styles.css @@ -0,0 +1,81 @@ +body, +html { + margin: 0; +} + +html { + height: 100%; +} + +body { + height: 90%; + max-width: 800px; + margin: 0 auto; +} + +h1, +p { + font-family: sans-serif; + text-align: center; + padding: 10px; +} + +.txt, +select, +form > div { + display: block; + margin: 0 auto; + font-family: sans-serif; + font-size: 16px; + padding: 5px; +} + +.txt { + width: 82%; +} + +select { + width: 83%; +} + +form > div { + width: 81%; +} + +.txt, +form > div { + margin-bottom: 10px; + overflow: auto; +} + +.clearfix { + clear: both; +} + +.controls { + text-align: center; + margin-top: 50px; +} + +.controls > * { + margin-bottom: 10px; + text-align: center; +} +.controls fieldset { + display: inline-block; + text-align: left; +} + +.controls button { + padding: 10px; + width: 100px; +} + +.checkbox { + text-align: center; +} + +.debug { + margin-top: 100px; + text-align: center; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4cd02c1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3633 @@ +{ + "name": "readium-speech", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "readium-speech", + "version": "1.0.0", + "license": "BSD-3-Clause", + "devDependencies": { + "@ava/typescript": "^5.0.0", + "ava": "^6.1.3", + "http-server": "^14.1.1", + "rimraf": "^6.0.1", + "typescript": "^5.5.4" + } + }, + "node_modules/@ava/typescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-5.0.0.tgz", + "integrity": "sha512-2twsQz2fUd95QK1MtKuEnjkiN47SKHZfi/vWj040EN6Eo2ZW3SNcAwncJqXXoMTYZTWtBRXYp3Fg8z+JkFI9aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0", + "execa": "^8.0.1" + }, + "engines": { + "node": "^18.18 || ^20.8 || ^21 || ^22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vercel/nft": { + "version": "0.26.5", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.26.5.tgz", + "integrity": "sha512-NHxohEqad6Ra/r4lGknO52uc/GrWILXAMs1BB4401GTqww0fw1bAqzpG1XHuDO+dprg4GvsD9ZLLSsdo78p9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.5", + "@rollup/pluginutils": "^4.0.0", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.2", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.2", + "node-gyp-build": "^4.2.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ava": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/ava/-/ava-6.1.3.tgz", + "integrity": "sha512-tkKbpF1pIiC+q09wNU9OfyTDYZa8yuWvU2up3+lFJ3lr1RmnYh2GBpPwzYUEB0wvTPIUysGjcZLNZr7STDviRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vercel/nft": "^0.26.2", + "acorn": "^8.11.3", + "acorn-walk": "^8.3.2", + "ansi-styles": "^6.2.1", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.1.0", + "cbor": "^9.0.1", + "chalk": "^5.3.0", + "chunkd": "^2.0.1", + "ci-info": "^4.0.0", + "ci-parallel-vars": "^1.0.1", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.3.4", + "emittery": "^1.0.1", + "figures": "^6.0.1", + "globby": "^14.0.0", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "memoize": "^10.0.0", + "ms": "^2.1.3", + "p-map": "^7.0.1", + "package-config": "^5.0.0", + "picomatch": "^3.0.1", + "plur": "^5.1.0", + "pretty-ms": "^9.0.0", + "resolve-cwd": "^3.0.0", + "stack-utils": "^2.0.6", + "strip-ansi": "^7.1.0", + "supertap": "^3.0.1", + "temp-dir": "^3.0.0", + "write-file-atomic": "^5.0.1", + "yargs": "^17.7.2" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": "^18.18 || ^20.8 || ^21 || ^22" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", + "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emittery": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.3.tgz", + "integrity": "sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-server/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/http-server/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", + "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", + "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", + "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "license": "MIT", + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/memoize": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.0.0.tgz", + "integrity": "sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/p-map": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.2.tgz", + "integrity": "sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/package-config/-/package-config-5.0.0.tgz", + "integrity": "sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "load-json-file": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/portfinder": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/pretty-ms": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.1.0.tgz", + "integrity": "sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..35da660 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "readium-speech", + "version": "1.0.0", + "description": "Readium Speech is a TypeScript library for implementing a read aloud feature with Web technologies. It follows [best practices](https://github.com/HadrienGardeur/read-aloud-best-practices) gathered through interviews with members of the digital publishing industry.", + "main": "build/cjs/voices.js", + "module": "build/mjs/voices.js", + "scripts": { + "test": "ava build/mjs/**/*.test.js", + "build": "rimraf build/* && tsc -p tsconfig-cjs.json && tsc -p tsconfig.json && /bin/sh script/fixup.sh", + "start": "node build/index.js", + "extract-json-data": "node script/extract-json.mjs", + "serve": "http-server ./", + "watch": "tsc -w" + }, + "author": "", + "license": "BSD-3-Clause", + "type": "module", + "devDependencies": { + "@ava/typescript": "^5.0.0", + "ava": "^6.1.3", + "http-server": "^14.1.1", + "rimraf": "^6.0.1", + "typescript": "^5.5.4" + } +} diff --git a/script/extract-json.mjs b/script/extract-json.mjs new file mode 100644 index 0000000..5523542 --- /dev/null +++ b/script/extract-json.mjs @@ -0,0 +1,255 @@ + +import { spawn } from 'node:child_process'; +import { rm } from 'node:fs/promises'; +import { join } from 'node:path'; +import { writeFileSync } from 'node:fs'; + +const repoUrl = 'https://github.com/HadrienGardeur/web-speech-recommended-voices.git'; +// const repoBranch = 'locales-for-voice-names'; +const repoBranch = 'main'; +const repoPath = 'script/web-speech-recommended-voices'; + +// Clone the repository +await new Promise((resolve, reject) => { + const cloneProcess = spawn('git', ['clone', '--depth=1', '--branch', repoBranch, repoUrl, repoPath]); + cloneProcess.on('close', (code) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`Git clone failed with code ${code}`)); + } + }); +}); + + +const jsonFiles = [ + 'bg.json', + 'ca.json', + 'cs.json', + 'da.json', + 'de.json', + 'el.json', + 'en.json', + 'es.json', + 'eu.json', + 'fi.json', + 'fr.json', + 'gl.json', + 'hr.json', + 'hu.json', + 'it.json', + 'ja.json', + 'ko.json', + 'nb.json', + 'nl.json', + 'pl.json', + 'pt.json', + 'ro.json', + 'ru.json', + 'sk.json', + 'sl.json', + 'sv.json', + 'tr.json', + 'uk.json', +]; + +const filters = [ + 'novelty.json', + 'veryLowQuality.json', +]; + +// const localizedNames = [ +// 'ca.json', +// 'da.json', +// 'de.json', +// 'en.json', +// 'es.json', +// 'fi.json', +// 'fr.json', +// 'it.json', +// 'nb.json', +// 'nl.json', +// 'pt.json', +// 'sv.json', +// ]; + +let novelty = []; +let veryLowQuality = []; + +// let localization = {}; + +let recommended = [] + +let quality = []; + +const defaultRegion = {}; + +// function generateLanguageRegionStrings(languages, regions) { + +// const result = {}; +// for (const languageCode in languages) { +// for (const regionCode in regions) { +// const bcp47Code = `${languageCode.toLowerCase()}-${regionCode.toLowerCase()}`; +// const translation = `(${languages[languageCode]} (${regions[regionCode]}))`; +// result[bcp47Code] = translation; +// } +// } + +// return result; +// } + +// function getAltName(languages) { + +// if (!languages.length) { +// return []; +// } + +// const result = []; +// for (const language of languages) { +// for (const langLocalization in localization) { + +// const v = localization[langLocalization][language.toLowerCase()]; +// if (v) { +// result.push(v); +// } +// } +// } + +// return result; +// } + +function filterBCP47(data) { + return data.filter((v) => /\w{2,3}-\w{2,3}/.test(v)); +} + +{ + const file = 'apple.json'; + const filePath = join(process.cwd(), repoPath, 'json', 'localizedNames', file); + try { + const { default: jsonData } = await import(filePath, { with: { type: 'json' } }); + console.log(`Imported localizedNames/${file}:` /*, jsonData*/); + + quality = jsonData.quality; + } catch (error) { + console.error(`Failed to import localizedNames/${file}: ${error.message}`); + } +} + +// for (const file of localizedNames) { +// const filePath = join(process.cwd(), repoPath, 'json', 'localizedNames', 'full', file); +// try { +// const { default: jsonData } = await import(filePath, { with: { type: 'json' } }); +// console.log(`Imported localizedNames/${file}:` /*, jsonData*/); + +// const lang = file.split(".")[0]; +// localization[lang] = generateLanguageRegionStrings(jsonData.languages, jsonData.regions); +// } catch (error) { +// console.error(`Failed to import localizedNames/${file}: ${error.message}`); +// } +// } +// // console.log(localization); + + +for (const file of jsonFiles) { + const filePath = join(process.cwd(), repoPath, 'json', file); + try { + const { default: jsonData } = await import(filePath, { with: { type: 'json' } }); + console.log(`Imported ${file}:` /*, jsonData*/); + + defaultRegion[jsonData.language] = jsonData.defaultRegion; + + const voices = jsonData.voices; + + for (const voice of voices) { + + recommended.push({ + label: voice.label, + name: voice.name || undefined, + altNames: voice.altNames || undefined, + language: voice.language || undefined, + gender: voice.gender || undefined, + age: voice.age || undefined, + quality: Array.isArray(voice.quality) ? voice.quality : [], + recommendedPitch: voice.pitchControl === false ? undefined : voice.pitch || 1, + recommendedRate: voice.pitchControl === false ? undefined : voice.rate || 1, + localizedName: voice.localizedName || "", + }); + } + + } catch (error) { + console.error(`Failed to import ${file}: ${error.message}`); + } +} + +for (const file of filters) { + const filePath = join(process.cwd(), repoPath, 'json', 'filters', file); + try { + const { default: jsonData } = await import(filePath, { with: { type: 'json' } }); + console.log(`Imported filters/${file}:` /*, jsonData*/); + + if (file.startsWith("novelty")) { + novelty = jsonData.voices.map(({ name, altNames }) => [name, ...(Array.isArray(altNames) ? altNames : [])]).flat(); + } + + if (file.startsWith("veryLow")) { + veryLowQuality = jsonData.voices.map(({ name, language, otherLanguages }) => { + // const languages = filterBCP47([language, otherLanguages].flat()); + // const altNamesGenerated = getAltName(languages); + // const altNames = altNamesGenerated.map((v) => name + " " + v); + + // return [name, altNames].flat(); + return name; + }).flat(); + } + } catch (error) { + console.error(`Failed to import filters/${file}: ${error.message}`); + } +} + + + +const content = ` + +export const novelty = ${JSON.stringify(novelty)}; + +export const veryLowQuality = ${JSON.stringify(veryLowQuality)}; + +export type TGender = "female" | "male" | "nonbinary" +export type TQuality = "veryLow" | "low" | "normal" | "high" | "veryHigh"; + +export interface IRecommended { + label: string; + name: string; + altNames?: string[]; + language: string; + gender?: TGender | undefined; + age?: string | undefined; + quality: TQuality[]; + recommendedPitch?: number | undefined; + recommendedRate?: number | undefined; + localizedName: string; +}; + +export const recommended: Array = ${JSON.stringify(recommended)}; + +export const quality = ${JSON.stringify(quality)}; + +export const defaultRegion = ${JSON.stringify(defaultRegion)}; +`; + +const filePath = './src/data.ts'; + +try { + writeFileSync(filePath, content); + console.log('File has been written successfully'); +} catch (err) { + console.error(err); +} + +// Delete the cloned repository +try { + await rm(repoPath, { recursive: true, force: true }); + console.log(`Deleted repository at ${repoPath}`); +} catch (error) { + console.error(`Failed to delete repository: ${error.message}`); +} diff --git a/script/fixup.sh b/script/fixup.sh new file mode 100644 index 0000000..bdf3bae --- /dev/null +++ b/script/fixup.sh @@ -0,0 +1,11 @@ +cat >build/cjs/package.json <build/mjs/package.json < = [{"label":"Kalina","name":"Microsoft Kalina Online (Natural) - Bulgarian (Bulgaria)","language":"bg-BG","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Borislav","name":"Microsoft Borislav Online (Natural) - Bulgarian (Bulgaria)","language":"bg-BG","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Daria","name":"Daria","language":"bg-BG","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Ivan","name":"Microsoft Ivan - Bulgarian (Bulgaria)","language":"bg-BG","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Женски глас","name":"Android Speech Recognition and Synthesis from Google bg-bg-x-ifk-network","altNames":["Android Speech Recognition and Synthesis from Google bg-bg-x-ifk-local","Android Speech Recognition and Synthesis from Google bg-bg-language"],"language":"bg-BG","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"български глас","name":"български България","language":"bg-BG","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Joana (Català)","name":"Microsoft Joana Online (Natural) - Catalan (Spain)","language":"ca-ES","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Enric (Català)","name":"Microsoft Enric Online (Natural) - Catalan (Spain)","language":"ca-ES","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Montse (Català)","name":"Montse","language":"ca-ES","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Pau (Valencià)","name":"Pau","language":"ca-ES-u-sd-esvc","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Jordi (Català)","name":"Jordi","language":"ca-ES","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Herena (Català)","name":"Microsoft Herena - Catalan (Spain)","language":"ca-ES","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Veu femenina catalana","name":"Android Speech Recognition and Synthesis from Google ca-es-x-caf-network","altNames":["Android Speech Recognition and Synthesis from Google ca-es-x-caf-local","Android Speech Recognition and Synthesis from Google ca-ES-language"],"language":"ca-ES","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Veu catalana","name":"català Espanya","language":"ca-ES","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Vlasta","name":"Microsoft Vlasta Online (Natural) - Czech (Czech)","language":"cs-CZ","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Antonin","name":"Microsoft Antonin Online (Natural) - Czech (Czech)","language":"cs-CZ","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Zuzana","name":"Zuzana","language":"cs-CZ","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Iveta","name":"Iveta","language":"cs-CZ","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Jakub","name":"Microsoft Jakub - Czech (Czech)","language":"cs-CZ","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Ženský hlas","name":"Google čeština (Natural)","altNames":["Android Speech Recognition and Synthesis from Google cs-cz-x-jfs-network","Chrome OS čeština","Android Speech Recognition and Synthesis from Google cs-cz-x-jfs-local","Android Speech Recognition and Synthesis from Google cs-CZ-language"],"language":"cs-CZ","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Český hlas","name":"čeština Česko","language":"cs-CZ","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Christel","name":"Microsoft Christel Online (Natural) - Danish (Denmark)","language":"da-DK","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Jeppe","name":"Microsoft Jeppe Online (Natural) - Danish (Denmark)","language":"da-DK","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Sara","name":"Sara","language":"da-DK","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Magnus","name":"Magnus","language":"da-DK","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Helle","name":"Microsoft Helle - Danish (Denmark)","language":"da-DK","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvindestemme 1","name":"Google Dansk 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google da-dk-x-kfm-network","Chrome OS Dansk 1","Android Speech Recognition and Synthesis from Google da-dk-x-kfm-local","Android Speech Recognition and Synthesis from Google da-DK-language"],"language":"da-DK","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvindestemme 2","name":"Google Dansk 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google da-dk-x-sfp-network","Chrome OS Dansk 3","Android Speech Recognition and Synthesis from Google da-dk-x-sfp-local"],"language":"da-DK","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvindestemme 3","name":"Google Dansk 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google da-dk-x-vfb-network","Chrome OS Dansk 4","Android Speech Recognition and Synthesis from Google da-dk-x-vfb-local"],"language":"da-DK","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mandsstemme","name":"Google Dansk 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google da-dk-x-nmm-network","Chrome OS Dansk 2","Android Speech Recognition and Synthesis from Google da-dk-x-nmm-local"],"language":"da-DK","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Dansk stemme","name":"dansk Danmark","language":"da-DK","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Seraphina (Deutschland)","name":"Microsoft SeraphinaMultilingual Online (Natural) - German (Germany)","language":"de-DE","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Amala (Deutschland)","name":"Microsoft Amala Online (Natural) - German (Germany)","language":"de-DE","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Katja (Deutschland)","name":"Microsoft Katja Online (Natural) - German (Germany)","language":"de-DE","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Florian (Deutschland)","name":"Microsoft FlorianMultilingual Online (Natural) - German (Germany)","language":"de-DE","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Conrad (Deutschland)","name":"Microsoft Conrad Online (Natural) - German (Germany)","language":"de-DE","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Killian (Deutschland)","name":"Microsoft Killian Online (Natural) - German (Germany)","language":"de-DE","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Ingrid (Österreich)","name":"Microsoft Ingrid Online (Natural) - German (Austria)","language":"de-AT","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Jonas (Österreich)","name":"Microsoft Jonas Online (Natural) - German (Austria)","language":"de-AT","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Leni (Schweiz)","name":"Microsoft Leni Online (Natural) - German (Switzerland)","language":"de-CH","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Jan (Schweiz)","name":"Microsoft Jan Online (Natural) - German (Switzerland)","language":"de-CH","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Petra (Deutschland)","name":"Petra","language":"de-DE","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Anna (Deutschland)","name":"Anna","language":"de-DE","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Helena (Deutschland)","name":"Helena","language":"de-DE","gender":"female","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Markus (Deutschland)","name":"Markus","language":"de-DE","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Viktor (Deutschland)","name":"Viktor","language":"de-DE","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Yannick (Deutschland)","name":"Yannick","language":"de-DE","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Martin (Deutschland)","name":"Martin","language":"de-DE","gender":"male","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Weibliche Google-Stimme (Deutschland)","name":"Google Deutsch","language":"de-DE","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Hedda (Deutschland)","name":"Microsoft Hedda - German (Germany)","language":"de-DE","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Katja (Deutschland)","name":"Microsoft Katja - German (Germany)","language":"de-DE","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Stefan (Deutschland)","name":"Microsoft Stefan - German (Germany)","language":"de-DE","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Michael (Österreich)","name":"Microsoft Michael - German (Austria)","language":"de-AT","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Karsten (Schweiz)","name":"Microsoft Karsten - German (Switzerland)","language":"de-CH","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Weibliche Stimme 1 (Deutschland)","name":"Google Deutsch 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google de-de-x-dea-network","Chrome OS Deutsch 2","Android Speech Recognition and Synthesis from Google de-de-x-dea-local","Android Speech Recognition and Synthesis from Google de-DE-language"],"language":"de-DE","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Weibliche Stimme 2 (Deutschland)","name":"Google Deutsch 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google de-de-x-nfh-network","Chrome OS Deutsch 1","Android Speech Recognition and Synthesis from Google de-de-x-nfh-local"],"language":"de-DE","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Männliche Stimme 1 (Deutschland)","name":"Google Deutsch 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google de-de-x-deb-network","Chrome OS Deutsch 3","Android Speech Recognition and Synthesis from Google de-de-x-deb-local"],"language":"de-DE","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Männliche Stimme 2 (Deutschland)","name":"Google Deutsch 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google de-de-x-deg-network","Chrome OS Deutsch 4","Android Speech Recognition and Synthesis from Google de-de-x-deg-local"],"language":"de-DE","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Deutsche Stimme","name":"Deutsch Deutschland","language":"de-DE","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Athina","name":"Microsoft Athina Online (Natural) - Greek (Greece)","language":"el-GR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Nestoras","name":"Microsoft Nestoras Online (Natural) - Greek (Greece)","language":"el-GR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Melina","name":"Melina","language":"el-GR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Nikos","name":"Nikos","language":"el-GR","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Stefanos","name":"Microsoft Stefanos - Greek (Greece)","language":"el-GR","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Γυναικεία φωνή","name":"Google Ελληνικά (Natural)","altNames":["Android Speech Recognition and Synthesis from Google el-gr-x-vfz-network","Chrome OS Ελληνικά","Android Speech Recognition and Synthesis from Google el-gr-x-vfz-local","Android Speech Recognition and Synthesis from Google el-GR-language"],"language":"el-GR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Ελληνική φωνή","name":"Ελληνικά Ελλάδα","language":"el-GR","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Emma (US)","name":"Microsoft EmmaMultilingual Online (Natural) - English (United States)","altNames":["Microsoft Emma Online (Natural) - English (United States)"],"language":"en-US","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Microsoft Ava (US)","name":"Microsoft AvaMultilingual Online (Natural) - English (United States)","altNames":["Microsoft Ava Online (Natural) - English (United States)"],"language":"en-US","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Jenny (US)","name":"Microsoft Jenny Online (Natural) - English (United States)","language":"en-US","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Aria (US)","name":"Microsoft Aria Online (Natural) - English (United States)","language":"en-US","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Michelle (US)","name":"Microsoft Michelle Online (Natural) - English (United States)","language":"en-US","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Ana (US)","name":"Microsoft Ana Online (Natural) - English (United States)","language":"en-US","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Andrew (US)","name":"Microsoft AndrewMultilingual Online (Natural) - English (United States)","altNames":["Microsoft Andrew Online (Natural) - English (United States)"],"language":"en-US","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Brian (US)","name":"Microsoft BrianMultilingual Online (Natural) - English (United States)","altNames":["Microsoft Brian Online (Natural) - English (United States)"],"language":"en-US","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Guy (US)","name":"Microsoft Guy Online (Natural) - English (United States)","language":"en-US","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Eric (US)","name":"Microsoft Eric Online (Natural) - English (United States)","language":"en-US","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Steffan (US)","name":"Microsoft Steffan Online (Natural) - English (United States)","language":"en-US","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Christopher (US)","name":"Microsoft Christopher Online (Natural) - English (United States)","language":"en-US","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Roger (US)","name":"Microsoft Roger Online (Natural) - English (United States)","language":"en-US","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Sonia (UK)","name":"Microsoft Sonia Online (Natural) - English (United Kingdom)","language":"en-GB","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Libby (UK)","name":"Microsoft Libby Online (Natural) - English (United Kingdom)","language":"en-GB","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Maisie (UK)","name":"Microsoft Maisie Online (Natural) - English (United Kingdom)","language":"en-GB","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Ryan (UK)","name":"Microsoft Ryan Online (Natural) - English (United Kingdom)","language":"en-GB","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Thomas (UK)","name":"Microsoft Thomas Online (Natural) - English (United Kingdom)","language":"en-GB","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Natasha (Australia)","name":"Microsoft Natasha Online (Natural) - English (Australia)","language":"en-AU","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"William (Australia)","name":"Microsoft William Online (Natural) - English (Australia)","language":"en-AU","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Clara (Canada)","name":"Microsoft Clara Online (Natural) - English (Canada)","language":"en-CA","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Liam (Canada)","name":"Microsoft Liam Online (Natural) - English (Canada)","language":"en-CA","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Apple Ava (US)","name":"Ava","language":"en-US","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Zoe (US)","name":"Zoe","language":"en-US","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Allison (US)","name":"Allison","language":"en-US","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Nicky (US)","name":"Nicky","language":"en-US","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Samantha (US)","name":"Samantha","language":"en-US","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Joelle (US)","name":"Joelle","language":"en-US","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Evan (US)","name":"Evan","language":"en-US","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Nathan (US)","name":"Nathan","language":"en-US","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Tom (US)","name":"Tom","language":"en-US","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Alex (US)","name":"Alex","language":"en-US","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Aaron (US)","name":"Aaron","language":"en-US","gender":"male","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Kate (UK)","name":"Kate","language":"en-GB","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Stephanie (UK)","name":"Stephanie","language":"en-GB","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Serena (UK)","name":"Serena","language":"en-GB","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Martha (UK)","name":"Martha","language":"en-GB","gender":"female","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Jamie (UK)","name":"Jamie","language":"en-GB","gender":"male","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Oliver (UK)","name":"Oliver","language":"en-GB","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Daniel (UK)","name":"Daniel","language":"en-GB","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Arthur (UK)","name":"Arthur","language":"en-GB","gender":"male","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Matilda (Australia)","name":"Matilda","language":"en-AU","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Karen (Australia)","name":"Karen","language":"en-AU","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Catherine (Australia)","name":"Catherine","language":"en-AU","gender":"female","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Lee (Australia)","name":"Lee","language":"en-AU","gender":"male","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Gordon (Australia)","name":"Gordon","language":"en-AU","gender":"male","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Female Google voice (US)","name":"Google US English","language":"en-US","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female Google voice (UK)","name":"Google UK English Female","language":"en-GB","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male Google voice (UK)","name":"Google UK English Male","language":"en-GB","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Zira (US)","name":"Microsoft Zira - English (United States)","language":"en-US","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"David (US)","name":"Microsoft David - English (United States)","language":"en-US","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mark (US)","name":"Microsoft Mark - English (United States)","language":"en-US","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Hazel (UK)","name":"Microsoft Hazel - English (Great Britain)","language":"en-GB","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Susan (UK)","name":"Microsoft Susan - English (Great Britain)","language":"en-GB","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"George (UK)","name":"Microsoft George - English (Great Britain)","language":"en-GB","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Catherine (Australia)","name":"Microsoft Catherine - English (Austalia)","language":"en-AU","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"James (Australia)","name":"Microsoft Richard - English (Australia)","language":"en-AU","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Linda (Canada)","name":"Microsoft Linda - English (Canada)","language":"en-CA","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Richard (Canada)","name":"Microsoft Richard - English (Canada)","language":"en-CA","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 1 (US)","name":"Google US English 5 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-us-x-tpc-network","Chrome OS US English 5","Android Speech Recognition and Synthesis from Google en-us-x-tpc-local","Android Speech Recognition and Synthesis from Google en-US-language"],"language":"en-US","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 2 (US)","name":"Google US English 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-us-x-iob-network","Chrome OS US English 1","Android Speech Recognition and Synthesis from Google en-us-x-iob-local"],"language":"en-US","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 3 (US)","name":"Google US English 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-us-x-iog-network","Chrome OS US English 2","Android Speech Recognition and Synthesis from Google en-us-x-iog-local"],"language":"en-US","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 4 (US)","name":"Google US English 7 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-us-x-tpf-network","Chrome OS US English 7","Android Speech Recognition and Synthesis from Google en-us-x-tpf-local"],"language":"en-US","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 5 (US)","name":"Android Speech Recognition and Synthesis from Google en-us-x-sfg-network","altNames":["Android Speech Recognition and Synthesis from Google en-us-x-sfg-local"],"language":"en-US","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 6 (US)","name":"Chrome OS US English 8","language":"en-US","gender":"female","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 1 (US)","name":"Google US English 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-us-x-iom-network","Chrome OS US English 4","Android Speech Recognition and Synthesis from Google en-us-x-iom-local"],"language":"en-US","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 2 (US)","name":"Google US English 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-us-x-iol-network","Chrome OS US English 3","Android Speech Recognition and Synthesis from Google en-us-x-iol-local"],"language":"en-US","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 3 (US)","name":"Google US English 6 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-us-x-tpd-network","Chrome OS US English 6","Android Speech Recognition and Synthesis from Google en-us-x-tpd-local"],"language":"en-US","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 1 (UK)","name":"Google UK English 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-gb-x-gba-network","Chrome OS UK English 2","Android Speech Recognition and Synthesis from Google en-gb-x-gba-local","Android Speech Recognition and Synthesis from Google en-GB-language"],"language":"en-GB","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 2 (UK)","name":"Google UK English 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-gb-x-gbc-network","Chrome OS UK English 4","Android Speech Recognition and Synthesis from Google en-gb-x-gbc-local"],"language":"en-GB","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 3 (UK)","name":"Google UK English 6 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-gb-x-gbg-network","Chrome OS UK English 6","Android Speech Recognition and Synthesis from Google en-gb-x-gbg-local"],"language":"en-GB","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 4 (UK)","name":"Chrome OS UK English 7","language":"en-GB","gender":"female","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 1 (UK)","name":"Google UK English 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-gb-x-rjs-network","Chrome OS UK English 1","Android Speech Recognition and Synthesis from Google en-gb-x-rjs-local"],"language":"en-GB","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 2 (UK)","name":"Google UK English 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-gb-x-gbb-network","Chrome OS UK English 3","Android Speech Recognition and Synthesis from Google en-gb-x-gbb-local"],"language":"en-GB","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 3 (UK)","name":"Google UK English 5 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-gb-x-gbd-network","Chrome OS UK English 5","Android Speech Recognition and Synthesis from Google en-gb-x-gbd-local"],"language":"en-GB","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 1 (Australia)","name":"Google Australian English 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-au-x-aua-network","Chrome OS Australian English 1","Android Speech Recognition and Synthesis from Google en-au-x-aua-local","Android Speech Recognition and Synthesis from Google en-AU-language"],"language":"en-AU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Female voice 2 (Australia)","name":"Google Australian English 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-au-x-auc-network","Chrome OS Australian English 3","Android Speech Recognition and Synthesis from Google en-au-x-auc-local"],"language":"en-AU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 1 (Australia)","name":"Google Australian English 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-au-x-aub-network","Chrome OS Australian English 2","Android Speech Recognition and Synthesis from Google en-au-x-aub-local"],"language":"en-AU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 2 (Australia)","name":"Google Australian English 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google en-au-x-aud-network","Chrome OS Australian English 4","Android Speech Recognition and Synthesis from Google en-au-x-aud-local"],"language":"en-AU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Male voice 3 (Australia)","name":"Chrome OS Australian English 5","language":"en-AU","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"US voice","name":"English United States","language":"en-US","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"UK voice","name":"English United Kingdom","language":"en-GB","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Australian voice","name":"English Australia","language":"en-AU","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Elvira (España)","name":"Microsoft Elvira Online (Natural) - Spanish (Spain)","language":"es-ES","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Alvaro (España)","name":"Microsoft Alvaro Online (Natural) - Spanish (Spain)","language":"es-ES","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Dalia (México)","name":"Microsoft Dalia Online (Natural) - Spanish (Mexico)","language":"es-MX","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Microsoft Jorge (México)","name":"Microsoft Jorge Online (Natural) - Spanish (Mexico)","language":"es-MX","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Elena (Argentina)","name":"Microsoft Elena Online (Natural) - Spanish (Argentina)","language":"es-AR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Tomas (Argentina)","name":"Microsoft Tomas Online (Natural) - Spanish (Argentina)","language":"es-AR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Sofia (Bolivia)","name":"Microsoft Sofia Online (Natural) - Spanish (Bolivia)","language":"es-BO","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Marcelo (Bolivia)","name":"Microsoft Marcelo Online (Natural) - Spanish (Bolivia)","language":"es-BO","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Catalina (Chile)","name":"Microsoft Catalina Online (Natural) - Spanish (Chile)","language":"es-CL","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Lorenzo (Chile)","name":"Microsoft Lorenzo Online (Natural) - Spanish (Chile)","language":"es-CL","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Ximena (Colombia)","name":"Microsoft Ximena Online (Natural) - Spanish (Colombia)","language":"es-CO","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Salome (Colombia)","name":"Microsoft Salome Online (Natural) - Spanish (Colombia)","language":"es-CO","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Gonzalo (Colombia)","name":"Microsoft Gonzalo Online (Natural) - Spanish (Colombia)","language":"es-CO","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Maria (Costa Rica)","name":"Microsoft Maria Online (Natural) - Spanish (Costa Rica)","language":"es-CR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Juan (Costa Rica)","name":"Microsoft Juan Online (Natural) - Spanish (Costa Rica)","language":"es-CR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Belkys (Cuba)","name":"Microsoft Belkys Online (Natural) - Spanish (Cuba)","language":"es-CU","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Manuel (Cuba)","name":"Microsoft Manuel Online (Natural) - Spanish (Cuba)","language":"es-CU","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Andrea (Ecuador)","name":"Microsoft Andrea Online (Natural) - Spanish (Ecuador)","language":"es-EC","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Luis (Ecuador)","name":"Microsoft Luis Online (Natural) - Spanish (Ecuador)","language":"es-EC","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Lorena (El Salvador)","name":"Microsoft Lorena Online (Natural) - Spanish (El Salvador)","language":"es-SV","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Rodrigo (El Salvador)","name":"Microsoft Rodrigo Online (Natural) - Spanish (El Salvador)","language":"es-SV","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Paloma (Estados Unidos)","name":"Microsoft Paloma Online (Natural) - Spanish (United States)","language":"es-US","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Alonso (Estados Unidos)","name":"Microsoft Alonso Online (Natural) - Spanish (United States)","language":"es-US","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Marta (Guatemala)","name":"Microsoft Marta Online (Natural) - Spanish (Guatemala)","language":"es-GT","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Andres (Guatemala)","name":"Microsoft Andres Online (Natural) - Spanish (Guatemala)","language":"es-GT","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Teresa (Guinea Ecuatorial)","name":"Microsoft Teresa Online (Natural) - Spanish (Equatorial Guinea)","language":"es-GQ","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Javier (Guinea Ecuatorial)","name":"Microsoft Javier Online (Natural) - Spanish (Equatorial Guinea)","language":"es-GQ","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Karla (Honduras)","name":"Microsoft Karla Online (Natural) - Spanish (Honduras)","language":"es-HN","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Carlos (Honduras)","name":"Microsoft Carlos Online (Natural) - Spanish (Honduras)","language":"es-HN","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Yolanda (Nicaragua)","name":"Microsoft Yolanda Online (Natural) - Spanish (Nicaragua)","language":"es-NI","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Federico (Nicaragua)","name":"Microsoft Federico Online (Natural) - Spanish (Nicaragua)","language":"es-NI","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Margarita (Panamá)","name":"Microsoft Margarita Online (Natural) - Spanish (Panama)","language":"es-PA","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Roberto (Panamá)","name":"Microsoft Roberto Online (Natural) - Spanish (Panama)","language":"es-PA","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Tania (Paraguay)","name":"Microsoft Tania Online (Natural) - Spanish (Paraguay)","language":"es-PY","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Mario (Paraguay)","name":"Microsoft Mario Online (Natural) - Spanish (Paraguay)","language":"es-PY","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Camila (Perú)","name":"Microsoft Camila Online (Natural) - Spanish (Peru)","language":"es-PE","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Alex (Perú)","name":"Microsoft Alex Online (Natural) - Spanish (Peru)","language":"es-PE","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Karina (Puerto Rico)","name":"Microsoft Karina Online (Natural) - Spanish (Puerto Rico)","language":"es-PR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Victor (Puerto Rico)","name":"Microsoft Victor Online (Natural) - Spanish (Puerto Rico)","language":"es-PR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Ramona (República Dominicana)","name":"Microsoft Ramona Online (Natural) - Spanish (Dominican Republic)","language":"es-DO","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Emilio (República Dominicana)","name":"Microsoft Emilio Online (Natural) - Spanish (Dominican Republic)","language":"es-DO","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Valentina (Uruguay)","name":"Microsoft Valentina Online (Natural) - Spanish (Uruguay)","language":"es-UY","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Mateo (Uruguay)","name":"Microsoft Mateo Online (Natural) - Spanish (Uruguay)","language":"es-UY","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Paola (Venezuela)","name":"Microsoft Paola Online (Natural) - Spanish (Venezuela)","language":"es-VE","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Sebastian (Venezuela)","name":"Microsoft Sebastian Online (Natural) - Spanish (Venezuela)","language":"es-VE","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Marisol (España)","name":"Marisol","language":"es-ES","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Mónica (España)","name":"Mónica","language":"es-ES","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Apple Jorge (España)","name":"Jorge","language":"es-ES","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Angelica (México)","name":"Angelica","language":"es-MX","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Paulina (México)","name":"Paulina","language":"es-MX","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Juan (México)","name":"Juan","language":"es-MX","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Isabela (Argentina)","name":"Isabela","language":"es-AR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Diego (Argentina)","name":"Diego","language":"es-AR","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Francesca (Chile)","name":"Francesca","language":"es-CL","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Soledad (Colombia)","name":"Soledad","language":"es-CO","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Jimena (Colombia)","name":"Jimena","language":"es-CO","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Carlos (Colombia)","name":"Carlos","language":"es-CO","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Voz Google masculina (España)","name":"Google español","language":"es-ES","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz Google femenina (Estados Unidos)","name":"Google español de Estados Unidos","language":"es-US","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Helena (España)","name":"Microsoft Helena - Spanish (Spain)","language":"es-ES","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Laura (España)","name":"Microsoft Laura - Spanish (Spain)","language":"es-ES","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Pablo (España)","name":"Microsoft Pablo - Spanish (Spain)","language":"es-ES","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Sabina (México)","name":"Microsoft Sabina - Spanish (Mexico)","language":"es-MX","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Raul (México)","name":"Microsoft Raul - Spanish (Mexico)","language":"es-MX","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz femenina 1 (España)","name":"Google español 4 (Natural)","altNames":["Chrome OS español 4","Android Speech Recognition and Synthesis from Google es-es-x-eee-local","Android Speech Recognition and Synthesis from Google es-ES-language"],"language":"es-ES","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz femenina 2 (España)","name":"Google español 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google es-es-x-eea-network","Chrome OS español 1","Android Speech Recognition and Synthesis from Google es-es-x-eea-local"],"language":"es-ES","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz femenina 3 (España)","name":"Google español 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google es-es-x-eec-network","Chrome OS español 2","Android Speech Recognition and Synthesis from Google es-es-x-eec-local"],"language":"es-ES","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz masculina 1 (España)","name":"Google español 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google es-es-x-eed-network","Chrome OS español 3","Android Speech Recognition and Synthesis from Google es-es-x-eed-local"],"language":"es-ES","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz masculina 2 (España)","name":"Google español 5 (Natural)","altNames":["Chrome OS español 5","Android Speech Recognition and Synthesis from Google es-es-x-eef-local"],"language":"es-ES","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz femenina 1 (Estados Unidos)","name":"Google español de Estados Unidos 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google es-us-x-esc-network","Chrome OS español de Estados Unidos","Android Speech Recognition and Synthesis from Google es-us-x-esc-local","Android Speech Recognition and Synthesis from Google es-US-language"],"language":"es-US","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz femenina 2 (Estados Unidos)","name":"Google español de Estados Unidos 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google es-us-x-sfb-network","Android Speech Recognition and Synthesis from Google es-us-x-sfb-local"],"language":"es-US","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz masculina 1 (Estados Unidos)","name":"Google español de Estados Unidos 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google es-us-x-esd-network","Android Speech Recognition and Synthesis from Google es-us-x-esd-local"],"language":"es-US","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz masculina 2 (Estados Unidos)","name":"Google español de Estados Unidos 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google es-us-x-esf-network","Android Speech Recognition and Synthesis from Google es-us-x-esf-local"],"language":"es-US","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz de España","name":"español España","language":"es-ES","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Voz de Estados Unidos","name":"español Estados Unidos","language":"es-US","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Miren","name":"Miren","language":"eu-ES","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Noora","name":"Microsoft Noora Online (Natural) - Finnish (Finland)","language":"fi-FI","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Harri","name":"Microsoft Harri Online (Natural) - Finnish (Finland)","language":"fi-FI","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Satu","name":"Satu","language":"fi-FI","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Onni","name":"Onni","language":"fi-FI","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Heidi","name":"Microsoft Heidi - Finnish (Finland)","language":"fi-FI","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Suomalainen naisääni","name":"Google Suomi (Natural)","altNames":["Android Speech Recognition and Synthesis from Google fi-fi-x-afi-network","Chrome OS Suomi","Android Speech Recognition and Synthesis from Google fi-fi-x-afi-local","Android Speech Recognition and Synthesis from Google fi-FI-language"],"language":"fi-FI","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Suomalainen ääni","name":"suomi Suomi","language":"fi-FI","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Vivienne (France)","name":"Microsoft VivienneMultilingual Online (Natural) - French (France)","language":"fr-FR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Denise (France)","name":"Microsoft Denise Online (Natural) - French (France)","language":"fr-FR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Charline (Belgique)","name":"Microsoft Charline Online (Natural) - French (Belgium)","language":"fr-BE","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Ariane (Suisse)","name":"Microsoft Ariane Online (Natural) - French (Switzerland)","language":"fr-CH","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Eloise (France)","name":"Microsoft Eloise Online (Natural) - French (France)","language":"fr-FR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Remy (France)","name":"Microsoft RemyMultilingual Online (Natural) - French (France)","language":"fr-FR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Henri (France)","name":"Microsoft Henri Online (Natural) - French (France)","language":"fr-FR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Gerard (Belgique)","name":"Microsoft Gerard Online (Natural) - French (Belgium)","language":"fr-BE","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Fabrice (Suisse)","name":"Microsoft Fabrice Online (Natural) - French (Switzerland)","language":"fr-CH","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Sylvie (Canada)","name":"Microsoft Sylvie Online (Natural) - French (Canada)","language":"fr-CA","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Antoine (Canada)","name":"Microsoft Antoine Online (Natural) - French (Canada)","language":"fr-CA","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Jean (Canada)","name":"Microsoft Jean Online (Natural) - French (Canada)","language":"fr-CA","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Thierry (Canada)","name":"Microsoft Thierry Online (Natural) - French (Canada)","language":"fr-CA","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Audrey (France)","name":"Audrey","language":"fr-FR","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Aurélie (France)","name":"Aurélie","language":"fr-FR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":0.9,"localizedName":"apple"},{"label":"Marie (France)","name":"Marie","language":"fr-FR","gender":"female","quality":["low"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Thomas (France)","name":"Thomas","language":"fr-FR","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Aude (Belgique)","name":"Aude","language":"fr-BE","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Chantal (Canada)","name":"Chantal","language":"fr-CA","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Amélie (Canada)","name":"Amélie","language":"fr-CA","gender":"female","quality":["low","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Nicolas (Canada)","name":"Nicolas","language":"fr-CA","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Voix Google féminine (France)","name":"Google français","language":"fr-FR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Julie (France)","name":"Microsoft Julie - French (France)","language":"fr-FR","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Hortence (France)","name":"Microsoft Hortence - French (France)","language":"fr-FR","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Paul (France)","name":"Microsoft Paul - French (France)","language":"fr-FR","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Caroline (Canada)","name":"Microsoft Caroline - French (Canada)","language":"fr-CA","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Claude (Canada)","name":"Microsoft Claude - French (Canada)","language":"fr-CA","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Guillaume (Suisse)","name":"Microsoft Claude - French (Switzerland)","language":"fr-CH","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix féminine 1 (France)","name":"Google français 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google fr-fr-x-frc-network","Chrome OS français 4","Android Speech Recognition and Synthesis from Google fr-fr-x-frc-local","Android Speech Recognition and Synthesis from Google fr-FR-language"],"language":"fr-FR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix féminine 2 (France)","name":"Google français 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google fr-fr-x-fra-network","Chrome OS français 2","Android Speech Recognition and Synthesis from Google fr-fr-x-fra-local"],"language":"fr-FR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix féminine 3 (France)","name":"Google français 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google fr-fr-x-vlf-network","Chrome OS français 1","Android Speech Recognition and Synthesis from Google fr-fr-x-vlf-local"],"language":"fr-FR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix masculine 1 (France)","name":"Google français 5 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google fr-fr-x-frd-network","Chrome OS français 5","Android Speech Recognition and Synthesis from Google fr-fr-x-frd-local"],"language":"fr-FR","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix masculine 2 (France)","name":"Google français 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google fr-fr-x-frb-network","Chrome OS français 3","Android Speech Recognition and Synthesis from Google fr-fr-x-frb-local"],"language":"fr-FR","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix féminine 1 (Canada)","name":"Android Speech Recognition and Synthesis from Google fr-ca-x-caa-network","altNames":["Android Speech Recognition and Synthesis from Google fr-ca-x-caa-local","Android Speech Recognition and Synthesis from Google fr-CA-language"],"language":"fr-CA","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix féminine 2 (Canada)","name":"Android Speech Recognition and Synthesis from Google fr-ca-x-cac-network","altNames":["Android Speech Recognition and Synthesis from Google fr-ca-x-cac-local"],"language":"fr-CA","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix masculine 1 (Canada)","name":"Android Speech Recognition and Synthesis from Google fr-ca-x-cab-network","altNames":["Android Speech Recognition and Synthesis from Google fr-ca-x-cab-local"],"language":"fr-CA","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix masculine 2 (Canada)","name":"Android Speech Recognition and Synthesis from Google fr-ca-x-cad-network","altNames":["Android Speech Recognition and Synthesis from Google fr-ca-x-cad-local"],"language":"fr-CA","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voix française","name":"français France","language":"fr-FR","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Voix canadienne","name":"français Canada","language":"fr-CA","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Sabela","name":"Microsoft Sabela Online (Natural) - Galician (Spain)","language":"gl-ES","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Roi","name":"Microsoft Roi Online (Natural) - Galician (Spain)","language":"gl-ES","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Carmela","name":"Carmela","language":"gl-ES","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Gabrijela","name":"Microsoft Gabrijela Online (Natural) - Croatian (Croatia)","language":"hr-HR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Srecko","name":"Microsoft Srecko Online (Natural) - Croatian (Croatia)","language":"hr-HR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Lana","name":"Lana","altNames":["Lana (poboljšani)","Lana (hrvatski (Hrvatska))"],"language":"hr-HR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Matej","name":"Microsoft Matej - Croatian (Croatia)","language":"hr-HR","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Ženski glas","name":"Android Speech Recognition and Synthesis from Google hr-hr-x-hra-network","altNames":["Android Speech Recognition and Synthesis from Google hr-hr-x-hra-local"],"language":"hr-HR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Muški glas","name":"Android Speech Recognition and Synthesis from Google hr-hr-x-hrb-network","altNames":["Android Speech Recognition and Synthesis from Google hr-hr-x-hrb-local","Android Speech Recognition and Synthesis from Google hr-HR-language"],"language":"hr-HR","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Hrvatski glas","name":"hrvatski Hrvatska","language":"hr-HR","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Noemi","name":"Microsoft Noemi Online (Natural) - Hungarian (Hungary)","language":"hu-HU","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Tamas","name":"Microsoft Tamas Online (Natural) - Hungarian (Hungary)","language":"hu-HU","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Tünde","name":"Tünde","language":"hu-HU","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Szabolcs","name":"Microsoft Szabolcs - Hungarian (Hungary)","language":"hu-HU","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Női hang","name":"Google Magyar (Natural)","altNames":["Android Speech Recognition and Synthesis from Google hu-hu-x-kfl-network","Chrome OS Magyar","Android Speech Recognition and Synthesis from Google hu-hu-x-kfl-local","Android Speech Recognition and Synthesis from Google hu-HU-language"],"language":"hu-HU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Magyar hang","name":"magyar Magyarország","language":"hu-HU","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Elsa (Alta qualita)","name":"Microsoft Elsa Online (Natural) - Italian (Italy)","language":"it-IT","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Isabella","name":"Microsoft Isabella Online (Natural) - Italian (Italy)","language":"it-IT","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Giuseppe","name":"Microsoft Giuseppe Online (Natural) - Italian (Italy)","language":"it-IT","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Diego","name":"Microsoft Diego Online (Natural) - Italian (Italy)","language":"it-IT","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Federica","name":"Federica","language":"it-IT","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Emma","name":"Emma","language":"it-IT","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Alice","name":"Alice","language":"it-IT","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Paola","name":"Paola","language":"it-IT","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Luca","name":"Luca","language":"it-IT","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Voce Google femminile","name":"Google italiano","language":"it-IT","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Elsa","name":"Microsoft Elsa - Italian (Italy)","language":"it-IT","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Cosimo","name":"Microsoft Cosimo - Italian (Italy)","language":"it-IT","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voce femminile 1","name":"Google italiano 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google it-it-x-itb-network","Chrome OS italiano 2","Android Speech Recognition and Synthesis from Google it-it-x-itb-local","Android Speech Recognition and Synthesis from Google it-IT-language"],"language":"it-IT","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voce femminile 2","name":"Google italiano 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google it-it-x-kda-network","Chrome OS italiano 1","Android Speech Recognition and Synthesis from Google it-it-x-kda-local"],"language":"it-IT","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voce maschile 1","name":"Google italiano 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google it-it-x-itc-network","Chrome OS italiano 3","Android Speech Recognition and Synthesis from Google it-it-x-itc-local"],"language":"it-IT","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voce maschile 2","name":"Google italiano 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google it-it-x-itd-network","Chrome OS italiano 4","Android Speech Recognition and Synthesis from Google it-it-x-itd-local"],"language":"it-IT","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voce italiana","name":"italiano Italia","language":"it-IT","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Nanami","name":"Microsoft Nanami Online (Natural) - Japanese (Japan)","language":"ja-JP","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Keita","name":"Microsoft Keita Online (Natural) - Japanese (Japan)","language":"ja-JP","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"O-Ren","name":"O-Ren","language":"ja-JP","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Kyoko","name":"Kyoko","language":"ja-JP","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Otoya","name":"Otoya","language":"ja-JP","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Hattori","name":"Hattori","language":"ja-JP","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Google の女性の声","name":"Google 日本語","language":"ja-JP","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Ayumi","name":"Microsoft Ayumi - Japanese (Japan)","language":"ja-JP","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Haruka","name":"Microsoft Haruka - Japanese (Japan)","language":"ja-JP","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Ichiro","name":"Microsoft Ichiro - Japanese (Japan)","language":"ja-JP","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"女性の声1","name":"Google 日本語 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google ja-jp-x-htm-network","Chrome OS 日本語 1","Android Speech Recognition and Synthesis from Google ja-jp-x-htm-local","Android Speech Recognition and Synthesis from Google ja-JP-language"],"language":"ja-JP","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"女性の声2","name":"Chrome OS 日本語 2","altNames":["Android Speech Recognition and Synthesis from Google ja-jp-x-jab-network","Android Speech Recognition and Synthesis from Google ja-jp-x-jab-local"],"language":"ja-JP","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"男性の声1","name":"Google 日本語 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google ja-jp-x-jac-network","Chrome OS 日本語 3","Android Speech Recognition and Synthesis from Google ja-jp-x-jac-local"],"language":"ja-JP","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"男性の声2","name":"Google 日本語 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google ja-jp-x-jad-network","Chrome OS 日本語 4","Android Speech Recognition and Synthesis from Google ja-jp-x-jad-local"],"language":"ja-JP","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"日本の音声","name":"日本語 日本","language":"ja-JP","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"SunHi","name":"Microsoft SunHi Online (Natural) - Korean (Korea)","language":"ko-KR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Hyunsu","name":"Microsoft Hyunsu Online (Natural) - Korean (Korea)","language":"ko-KR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"InJoon","name":"Microsoft InJoon Online (Natural) - Korean (Korea)","language":"ko-KR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Yuna","name":"Yuna","language":"ko-KR","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Jian","name":"Jian","language":"ko-KR","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Suhyun","name":"Suhyun","language":"ko-KR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Sora","name":"Sora","language":"ko-KR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Minsu","name":"Minsu","language":"ko-KR","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Google 여성 음성","name":"Google 한국의","language":"ko-KR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Heami","name":"Microsoft Heami - Korean (Korea)","language":"ko-KR","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"여성 목소리 1","name":"Google 한국어 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google ko-kr-x-kob-network","Chrome OS 한국어 2","Android Speech Recognition and Synthesis from Google ko-kr-x-kob-local","Android Speech Recognition and Synthesis from Google ko-KR-language"],"language":"ko-KR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"여성 목소리 2","name":"Google 한국어 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google ko-kr-x-ism-network","Chrome OS 한국어 1","Android Speech Recognition and Synthesis from Google ko-kr-x-ism-local"],"language":"ko-KR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"남성 1","name":"Google 한국어 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google ko-kr-x-koc-network","Chrome OS 한국어 3","Android Speech Recognition and Synthesis from Google ko-kr-x-koc-local"],"language":"ko-KR","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"남성 2","name":"Google 한국어 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google ko-kr-x-kod-network","Chrome OS 한국어 4","Android Speech Recognition and Synthesis from Google ko-kr-x-kod-local"],"language":"ko-KR","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"한국어 음성","name":"한국어 대한민국","language":"ko-KR","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Pernille","name":"Microsoft Pernille Online (Natural) - Norwegian (Bokmål, Norway)","language":"nb-NO","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Finn","name":"Microsoft Finn Online (Natural) - Norwegian (Bokmål Norway)","language":"nb-NO","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Nora","name":"Nora","language":"nb-NO","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Henrik","name":"Henrik","language":"nb-NO","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Jon","name":"Microsoft Jon - Norwegian (Bokmål Norway)","language":"nb-NO","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvinnestemme 1","name":"Google Norsk Bokmål 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nb-no-x-cfl-network","Chrome OS Norsk Bokmål 2","Android Speech Recognition and Synthesis from Google nb-no-x-cfl-local","Android Speech Recognition and Synthesis from Google nb-NO-language"],"language":"nb-NO","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvinnestemme 2","name":"Google Norsk Bokmål 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nb-no-x-rfj-network","Chrome OS Norsk Bokmål 1","Android Speech Recognition and Synthesis from Google nb-no-x-rfj-local"],"language":"nb-NO","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvinnestemme 3","name":"Google Norsk Bokmål 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nb-no-x-tfs-network","Chrome OS Norsk Bokmål 4","Android Speech Recognition and Synthesis from Google nb-no-x-tfs-local"],"language":"nb-NO","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mannsstemme 1","name":"Google Norsk Bokmål 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nb-no-x-cmj-network","Chrome OS Norsk Bokmål 3","Android Speech Recognition and Synthesis from Google nb-no-x-cmj-local"],"language":"nb-NO","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mannsstemme 2","name":"Google Norsk Bokmål 5 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nb-no-x-tmg-network","Chrome OS Norsk Bokmål 5","Android Speech Recognition and Synthesis from Google nb-no-x-tmg-local"],"language":"nb-NO","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Norsk stemme","name":"norsk bokmål Norge","language":"nb-NO","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Colette (Nederland)","name":"Microsoft Colette Online (Natural) - Dutch (Netherlands)","language":"nl-NL","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Fenna (Nederland)","name":"Microsoft Fenna Online (Natural) - Dutch (Netherlands)","language":"nl-NL","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Maarten (Nederland)","name":"Microsoft Maarten Online (Natural) - Dutch (Netherlands)","language":"nl-NL","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Dena (België)","name":"Microsoft Dena Online (Natural) - Dutch (Belgium)","language":"nl-BE","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Arnaud (België)","name":"Microsoft Arnaud Online (Natural) - Dutch (Belgium)","language":"nl-BE","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Claire (Nederland)","name":"Claire","language":"nl-NL","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Xander (Nederland)","name":"Xander","language":"nl-NL","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Ellen (België)","name":"Ellen","language":"nl-BE","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Google mannelijke stem (Nederland)","name":"Google Nederlands","language":"nl-NL","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Frank (Nederland)","name":"Microsoft Frank - Dutch (Netherlands)","language":"nl-NL","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Vrouwelijke stem 1 (Nederland)","name":"Google Nederlands 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nl-nl-x-lfc-network","Chrome OS Nederlands 4","Android Speech Recognition and Synthesis from Google nl-nl-x-lfc-local","Android Speech Recognition and Synthesis from Google nl-NL-language"],"language":"nl-NL","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Vrouwelijke stem 2 (Nederland)","name":"Google Nederlands 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nl-nl-x-tfb-network","Chrome OS Nederlands 1","Android Speech Recognition and Synthesis from Google nl-nl-x-tfb-local"],"language":"nl-NL","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Vrouwelijke stem 3 (Nederland)","name":"Google Nederlands 5 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nl-nl-x-yfr-network","Chrome OS Nederlands 5","Android Speech Recognition and Synthesis from Google nl-nl-x-yfr-local"],"language":"nl-NL","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mannelijke stem 1 (Nederland)","name":"Google Nederlands 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nl-nl-x-bmh-network","Chrome OS Nederlands 2","Android Speech Recognition and Synthesis from Google nl-nl-x-bmh-local"],"language":"nl-NL","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mannelijke stem 2 (Nederland)","name":"Google Nederlands 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google nl-nl-x-dma-network","Chrome OS Nederlands 3","Android Speech Recognition and Synthesis from Google nl-nl-x-dma-local"],"language":"nl-NL","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Vrouwelijke stem (België)","name":"Android Speech Recognition and Synthesis from Google nl-be-x-bec-network","altNames":["Android Speech Recognition and Synthesis from Google nl-be-x-bec-local","Android Speech Recognition and Synthesis from Google nl-BE-language"],"language":"nl-NL","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mannelijke stem (België)","name":"Android Speech Recognition and Synthesis from Google nl-be-x-bed-network","altNames":["Android Speech Recognition and Synthesis from Google nl-be-x-bed-local"],"language":"nl-NL","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Nederlandse stem (Nederland)","name":"Nederlands Nederland","language":"nl-NL","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Nederlandse stem (België)","name":"Nederlands België","language":"nl-BE","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Zofia","name":"Microsoft Zofia Online (Natural) - Polish (Poland)","language":"pl-PL","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Marek","name":"Microsoft Marek Online (Natural) - Polish (Poland)","language":"pl-PL","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Ewa","name":"Ewa","language":"pl-PL","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Zosia","name":"Zosia","language":"pl-PL","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Krzysztof","name":"Krzysztof","language":"pl-PL","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Żeński głos Google’a","name":"Google polski","language":"pl-PL","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Paulina","name":"Microsoft Paulina - Polish (Poland)","language":"pl-PL","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Adam","name":"Microsoft Adam - Polish (Poland)","language":"pl-PL","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Głos żeński 1","name":"Google Polski 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pl-pl-x-afb-network","Chrome OS Polski 2","Android Speech Recognition and Synthesis from Google pl-pl-x-afb-local","Android Speech Recognition and Synthesis from Google pl-PL-language"],"language":"pl-PL","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Głos żeński 2","name":"Google Polski 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pl-pl-x-oda-network","Chrome OS Polski 1","Android Speech Recognition and Synthesis from Google pl-pl-x-oda-local"],"language":"pl-PL","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Głos żeński 3","name":"Google Polski 5 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pl-pl-x-zfg-network","Chrome OS Polski 5","Android Speech Recognition and Synthesis from Google pl-pl-x-zfg-local"],"language":"pl-PL","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Głos męski 1","name":"Google Polski 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pl-pl-x-bmg-network","Chrome OS Polski 3","Android Speech Recognition and Synthesis from Google pl-pl-x-bmg-local"],"language":"pl-PL","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Głos męski 2","name":"Google Polski 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pl-pl-x-jmk-network","Chrome OS Polski 4","Android Speech Recognition and Synthesis from Google pl-pl-x-jmk-local"],"language":"pl-PL","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Głos polski","name":"polski Polska","language":"pl-PL","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Raquel (Portugal)","name":"Microsoft Raquel Online (Natural) - Portuguese (Portugal)","language":"pt-PT","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Duarte (Portugal)","name":"Microsoft Duarte Online (Natural) - Portuguese (Portugal)","language":"pt-PT","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Francisca (Brasil)","name":"Microsoft Francisca Online (Natural) - Portuguese (Brazil)","language":"pt-BR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Thalita (Brasil)","name":"Microsoft Thalita Online (Natural) - Portuguese (Brazil)","language":"pt-BR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Antonio (Brasil)","name":"Microsoft Antonio Online (Natural) - Portuguese (Brazil)","language":"pt-BR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Catarina (Portugal)","name":"Catarina","language":"pt-PT","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Joana (Portugal)","name":"Joana","language":"pt-PT","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Joaquim (Portugal)","name":"Joaquim","language":"pt-PT","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Fernanda (Brasil)","name":"Fernanda","language":"pt-BR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Luciana (Brasil)","name":"Luciana","language":"pt-BR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Felipe (Brasil)","name":"Felipe","language":"pt-BR","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Voz Google (Brasil)","name":"Google português do Brasil","language":"pt-BR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Helia (Portugal)","name":"Microsoft Helia - Portuguese (Portugal)","language":"pt-PT","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Maria (Brasil)","name":"Microsoft Maria - Portuguese (Brazil)","language":"pt-BR","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Daniel (Brasil)","name":"Microsoft Daniel - Portuguese (Brazil)","language":"pt-BR","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz feminina 1 (Portugal)","name":"Google português de Portugal 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pt-pt-x-jfb-network","Android Speech Recognition and Synthesis from Google pt-pt-x-jfb-local","Android Speech Recognition and Synthesis from Google pt-PT-language"],"language":"pt-PT","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz feminina 2 (Portugal)","name":"Google português de Portugal 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pt-pt-x-sfs-network","Chrome OS português de Portugal","Android Speech Recognition and Synthesis from Google pt-pt-x-sfs-local"],"language":"pt-PT","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz masculina 1 (Portugal)","name":"Google português de Portugal 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pt-pt-x-jmn-network","Android Speech Recognition and Synthesis from Google pt-pt-x-jmn-local"],"language":"pt-PT","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz masculina 2 (Portugal)","name":"Google português de Portugal 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pt-pt-x-pmj-network","Android Speech Recognition and Synthesis from Google pt-pt-x-pmj-local"],"language":"pt-PT","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz feminina 1 (Brasil)","name":"Google português do Brasil 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pt-br-x-afs-network","Chrome OS português do Brasil","Android Speech Recognition and Synthesis from Google pt-br-x-afs-local","Android Speech Recognition and Synthesis from Google pt-BR-language"],"language":"pt-BR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz feminina 2 (Brasil)","name":"Google português do Brasil 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pt-br-x-pte-network","Android Speech Recognition and Synthesis from Google pt-br-x-pte-local"],"language":"pt-BR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz masculina (Brasil)","name":"Google português do Brasil 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google pt-br-x-ptd-network","Android Speech Recognition and Synthesis from Google pt-br-x-ptd-local"],"language":"pt-BR","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voz do Portugal","name":"português Portugal","language":"pt-PT","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Voz do Brasil","name":"português Brasil","language":"pt-BR","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Alina","name":"Microsoft Alina Online (Natural) - Romanian (Romania)","language":"ro-RO","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Emil","name":"Microsoft Emil Online (Natural) - Romanian (Romania)","language":"ro-RO","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Ioana","name":"Ioana","language":"ro-RO","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Andrei","name":"Microsoft Andrei - Romanian (Romania)","language":"ro-RO","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voce feminină","name":"Android Speech Recognition and Synthesis from Google ro-ro-x-vfv-network","altNames":["Android Speech Recognition and Synthesis from Google ro-ro-x-vfv-local","Android Speech Recognition and Synthesis from Google ro-RO-language"],"language":"ro-RO","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Voce românească","name":"română România","language":"ro-RO","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Svetlana","name":"Microsoft Svetlana Online (Natural) - Russian (Russia)","language":"ru-RU","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Dmitry","name":"Microsoft Dmitry Online (Natural) - Russian (Russia)","language":"ru-RU","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Katya","name":"Katya","language":"ru-RU","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Milena","name":"Milena","language":"ru-RU","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Yuri","name":"Yuri","language":"ru-RU","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Google женский голос","name":"Google русский","language":"ru-RU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Irina","name":"Microsoft Irina - Russian (Russian)","language":"ru-RU","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Pavel","name":"Microsoft Pavel - Russian (Russian)","language":"ru-RU","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Женский голос 1","name":"Android Speech Recognition and Synthesis from Google ru-ru-x-dfc-network","altNames":["Android Speech Recognition and Synthesis from Google ru-ru-x-dfc-local"],"language":"ru-RU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Женский голос 2","name":"Android Speech Recognition and Synthesis from Google ru-ru-x-ruc-network","altNames":["Android Speech Recognition and Synthesis from Google ru-ru-x-ruc-local"],"language":"ru-RU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Женский голос 3","name":"Android Speech Recognition and Synthesis from Google ru-ru-x-rue-network","altNames":["Android Speech Recognition and Synthesis from Google ru-ru-x-rue-local"],"language":"ru-RU","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Мужской голос 1","name":"Android Speech Recognition and Synthesis from Google ru-ru-x-rud-network","altNames":["Android Speech Recognition and Synthesis from Google ru-ru-x-rud-local"],"language":"ru-RU","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Мужской голос 2","name":"Android Speech Recognition and Synthesis from Google ru-ru-x-ruf-network","altNames":["Android Speech Recognition and Synthesis from Google ru-ru-x-ruf-local"],"language":"ru-RU","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Русский голос","name":"русский Россия","language":"ru-RU","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Viktoria","name":"Microsoft Viktoria Online (Natural) - Slovak (Slovakia)","language":"sk-SK","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Lukas","name":"Microsoft Lukas Online (Natural) - Slovak (Slovakia)","language":"sk-SK","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Laura","name":"Laura","language":"sk-SK","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Filip","name":"Microsoft Filip - Slovak (Slovakia)","language":"sk-SK","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Ženský hlas","name":"Google Slovenčina (Natural)","altNames":["Android Speech Recognition and Synthesis from Google sk-sk-x-sfk-network","Android Speech Recognition and Synthesis from Google sk-sk-x-sfk-local","Chrome OS Slovenčina","Android Speech Recognition and Synthesis from Google sk-SK-language"],"language":"sk-SK","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Slovenský hlas","name":"slovenčina Slovensko","language":"sk-SK","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Petra","name":"Microsoft Petra Online (Natural) - Slovenian (Slovenia)","language":"sl-SI","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Rok","name":"Microsoft Rok Online (Natural) - Slovenian (Slovenia)","language":"sl-SI","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Tina","name":"Tina","language":"sl-SI","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Lado","name":"Microsoft Lado - Slovenian (Slovenia)","language":"sl-SI","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Ženski glas","name":"Android Speech Recognition and Synthesis from Google sl-si-x-frm-local","altNames":["Android Speech Recognition and Synthesis from Google sl-SI-language"],"language":"sl-SI","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Sofie","name":"Microsoft Sofie Online (Natural) - Swedish (Sweden)","language":"sv-SE","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Mattias","name":"Microsoft Mattias Online (Natural) - Swedish (Sweden)","language":"sv-SE","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Klara","name":"Klara","language":"sv-SE","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Alva","name":"Alva","language":"sv-SE","gender":"female","quality":["low","normal","high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Oskar","name":"Oskar","language":"sv-SE","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Bengt","name":"Microsoft Bengt - Swedish (Sweden)","language":"sv-SE","gender":"female","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvinnlig röst 1","name":"Google Svenska 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google sv-se-x-lfs-network","Chrome OS Svenska","Android Speech Recognition and Synthesis from Google sv-se-x-lfs-local","Android Speech Recognition and Synthesis from Google sv-SE-language"],"language":"sv-SE","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvinnlig röst 2","name":"Google Svenska 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google sv-se-x-afp-network","Android Speech Recognition and Synthesis from Google sv-se-x-afp-local"],"language":"sv-SE","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kvinnlig röst 3","name":"Google Svenska 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google sv-se-x-cfg-network","Android Speech Recognition and Synthesis from Google sv-se-x-cfg-local"],"language":"sv-SE","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mansröst 1","name":"Google Svenska 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google sv-se-x-cmh-network","Android Speech Recognition and Synthesis from Google sv-se-x-cmh-local"],"language":"sv-SE","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Mansröst 2","name":"Google Svenska 5 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google sv-se-x-dmc-network","Android Speech Recognition and Synthesis from Google sv-se-x-dmc-local"],"language":"sv-SE","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Svensk röst","name":"svenska Sverige","language":"sv-SE","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Emel","name":"Microsoft Emel Online (Natural) - Turkish (Turkey)","language":"tr-TR","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Ahmet","name":"Microsoft Ahmet Online (Natural) - Turkish (Turkey)","language":"tr-TR","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Yelda","name":"Yelda","altNames":["Yelda (Geliştirilmiş)","Yelda (Türkçe (Türkiye))"],"language":"tr-TR","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Cem","name":"Cem","language":"tr-TR","gender":"male","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Tolga","name":"Microsoft Tolga - Turkish (Turkey)","language":"tr-TR","gender":"male","quality":["normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kadın sesi 1","name":"Google Türkçe 3 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google tr-tr-x-cfs-network","Chrome OS Türkçe 3","Android Speech Recognition and Synthesis from Google tr-tr-x-cfs-local","Android Speech Recognition and Synthesis from Google tr-TR-language"],"language":"tr-TR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kadın sesi 2","name":"Google Türkçe 4 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google tr-tr-x-efu-network","Chrome OS Türkçe 4","Android Speech Recognition and Synthesis from Google tr-tr-x-efu-local"],"language":"tr-TR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Kadın sesi 3","name":"Google Türkçe 1 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google tr-tr-x-mfm-network","Chrome OS Türkçe 1","Android Speech Recognition and Synthesis from Google tr-tr-x-mfm-local"],"language":"tr-TR","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Erkek sesi 1","name":"Google Türkçe 2 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google tr-tr-x-ama-network","Chrome OS Türkçe 2","Android Speech Recognition and Synthesis from Google tr-tr-x-ama-local"],"language":"tr-TR","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Erkek sesi 2","name":"Google Türkçe 5 (Natural)","altNames":["Android Speech Recognition and Synthesis from Google tr-tr-x-tmc-network","Chrome OS Türkçe 5","Android Speech Recognition and Synthesis from Google tr-tr-x-tmc-local"],"language":"tr-TR","gender":"male","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Türkçe ses","name":"Türkçe Türkiye","language":"tr-TR","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"},{"label":"Polina","name":"Microsoft Polina Online (Natural) - Ukrainian (Ukraine)","language":"uk-UA","gender":"female","quality":["veryHigh"],"localizedName":""},{"label":"Ostap","name":"Microsoft Ostap Online (Natural) - Ukrainian (Ukraine)","language":"uk-UA","gender":"male","quality":["veryHigh"],"localizedName":""},{"label":"Lesya","name":"Lesya","language":"uk-UA","gender":"female","quality":["low","normal"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"apple"},{"label":"Жіночий голос","name":"Google українська (Natural)","altNames":["Android Speech Recognition and Synthesis from Google uk-ua-x-hfd-network","Chrome OS українська","Android Speech Recognition and Synthesis from Google uk-ua-x-hfd-local","Android Speech Recognition and Synthesis from Google uk-UA-language"],"language":"uk-UA","gender":"female","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":""},{"label":"Український голос","name":"українська Україна","language":"uk-UA","quality":["high"],"recommendedPitch":1,"recommendedRate":1,"localizedName":"android"}]; + +export const quality = {"ca":{"normal":"millorada","high":"prèmium"},"cs":{"normal":"vylepšená verze","high":"prémiový"},"da":{"normal":"forbedret","high":"høj kvalitet"},"de":{"normal":"erweitert","high":"premium"},"el":{"normal":"βελτιωμένη","high":"υψηλής ποιότητας"},"en":{"normal":"Enhanced","high":"Premium"},"es":{"normal":"mejorada","high":"premium"},"fi":{"normal":"parannettu","high":"korkealaatuinen"},"fr":{"normal":"premium","high":"de qualité"},"hu":{"normal":"továbbfejlesztett","high":"prémium"},"hr":{"normal":"poboljšani","high":"vrhunski"},"it":{"normal":"ottimizzata","high":"premium"},"ja":{"normal":"拡張","high":"プレミアム"},"ko":{"normal":"고품질","high":"프리미엄"},"nb":{"normal":"forbedret","high":"premium"},"nl":{"normal":"verbeterd","high":"premium"},"pl":{"normal":"rozszerzony","high":"premium"},"pt":{"normal":"melhorada","high":"premium"},"ro":{"normal":"îmbunătățită","high":"premium"},"ru":{"normal":"улучшенный","high":"высшее качество"},"sk":{"normal":"vylepšený","high":"prémiový"},"sl":{"normal":"izboljšano","high":"prvovrsten"},"sv":{"normal":"förbättrad","high":"premium"},"tr":{"normal":"Geliştirilmiş","high":"Yüksek Kaliteli"},"uk":{"normal":"вдосконалений","high":"високої якості"}}; + +export const defaultRegion = {"bg":"bg-BG","ca":"ca-ES","cs":"cs-CZ","da":"da-DK","de":"de-DE","el":"el-GR","en":"en-US","es":"es-ES","eu":"eu-ES","fi":"fi-FI","fr":"fr-FR","gl":"gl-ES","hr":"hr-HR","hu":"hu-HU","it":"it-IT","ja":"ja-JP","ko":"ko-KR","nb":"nb-NO","nl":"nl-NL","pl":"pl-PL","pt":"pt-BR","ro":"ro-RO","ru":"ru-RU","sk":"sk-SK","sl":"sl-SI","sv":"sv-SE","tr":"tr-TR","uk":"uk-UA"}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..95a6baf --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export * as voicesSelection from "./voices.js"; diff --git a/src/voices.ts b/src/voices.ts new file mode 100644 index 0000000..b9621d4 --- /dev/null +++ b/src/voices.ts @@ -0,0 +1,491 @@ + +import { novelty, quality, recommended, veryLowQuality, TGender, TQuality, IRecommended, defaultRegion } from "./data.js"; + +// export type TOS = 'Android' | 'ChromeOS' | 'iOS' | 'iPadOS' | 'macOS' | 'Windows'; +// export type TBrowser = 'ChromeDesktop' | 'Edge' | 'Firefox' | 'Safari'; + +const navigatorLanguages = () => window?.navigator?.languages || []; +const navigatorLang = () => (navigator?.language || "").split("-")[0].toLowerCase(); + +export interface IVoices { + label: string; + voiceURI: string; + name: string; + language: string; + gender?: TGender | undefined; + age?: string | undefined; + offlineAvailability: boolean; + quality?: TQuality | undefined; + pitchControl: boolean; + recommendedPitch?: number | undefined; + recommendedRate?: number | undefined; +} + +const normalQuality = Object.values(quality).map(({ normal }) => normal); +const highQuality = Object.values(quality).map(({ high }) => high); + +function compareQuality(a?: TQuality, b?: TQuality): number { + const qualityToNumber = (quality: TQuality) => { + switch (quality) { + case "veryLow": {return 0;} + case "low": {return 1;} + case "normal": {return 2;} + case "high": {return 3;} + case "veryHigh": {return 4;} + default: {return -1}; + } + } + + return qualityToNumber(b || "low") - qualityToNumber(a || "low"); +}; + +export async function getSpeechSynthesisVoices(): Promise { + const a = () => speechSynthesis.getVoices(); + + const voices = a(); + if (Array.isArray(voices) && voices.length) return voices; + + return new Promise((resolve, _reject) => { + + let counter = 1000; + const tick = () => { + if (counter < 1) return resolve([]); + // console.log(counter); + + --counter; + const voices = a(); + if (Array.isArray(voices) && voices.length) return resolve(voices); + setTimeout(tick, 10); + } + setTimeout(tick, 10); + }); +} + +export function parseSpeechSynthesisVoices(speechSynthesisVoices: SpeechSynthesisVoice[]): IVoices[] { + + const parseAndFormatBCP47 = (lang: string) => { + const speechVoiceLang = lang.replace("_", "-"); + if (/\w{2,3}-\w{2,3}/.test(speechVoiceLang)) { + return `${speechVoiceLang.split("-")[0].toLowerCase()}-${speechVoiceLang.split("-")[1].toUpperCase()}`; + } + + // bad formated !? + return lang; + }; + return speechSynthesisVoices.map((speechVoice) => ({ + label: speechVoice.name, + voiceURI: speechVoice.voiceURI , + name: speechVoice.name, + language: parseAndFormatBCP47(speechVoice.lang) , + gender: undefined, + age: undefined, + offlineAvailability: speechVoice.localService, + quality: undefined, + pitchControl: true, + recommendedPitch: undefined, + recomendedRate: undefined, + })); +} + +export function filterOnOfflineAvailability(voices: IVoices[], offline = true): IVoices[] { + return voices.filter(({offlineAvailability}) => { + return offlineAvailability === offline; + }); +} + +export function filterOnGender(voices: IVoices[], gender: TGender): IVoices[] { + return voices.filter(({gender: voiceGender}) => { + return voiceGender === gender; + }) +} + +export function filterOnLanguage(voices: IVoices[], language: string | string[]): IVoices[] { + language = Array.isArray(language) ? language : [language]; + language = language.map((l) => extractLangRegionFromBCP47(l)[0]); + return voices.filter(({language: voiceLanguage}) => { + const [lang] = extractLangRegionFromBCP47(voiceLanguage); + return language.includes(lang); + }) +} + +export function filterOnQuality(voices: IVoices[], quality: TQuality | TQuality[]): IVoices[] { + quality = Array.isArray(quality) ? quality : [quality]; + return voices.filter(({quality: voiceQuality}) => { + return quality.some((qual) => qual === voiceQuality); + }); +} + +export function filterOnNovelty(voices: IVoices[]): IVoices[] { + return voices.filter(({ name }) => { + return !novelty.includes(name); + }); +} + +export function filterOnVeryLowQuality(voices: IVoices[]): IVoices[] { + return voices.filter(({ name }) => { + return !veryLowQuality.find((v) => name.startsWith(v)); + }); +} + +function updateVoiceInfo(recommendedVoice: IRecommended, voice: IVoices) { + voice.label = recommendedVoice.label; + voice.gender = recommendedVoice.gender; + voice.recommendedPitch = recommendedVoice.recommendedPitch; + voice.recommendedRate = recommendedVoice.recommendedRate; + + return voice; +} +export type TReturnFilterOnRecommended = [voicesRecommended: IVoices[], voicesLowerQuality: IVoices[]]; +export function filterOnRecommended(voices: IVoices[], _recommended: IRecommended[] = recommended): TReturnFilterOnRecommended { + + const voicesRecommended = []; + const voicesLowerQuality = []; + + recommendedVoiceLoop: + for (const recommendedVoice of _recommended) { + if (Array.isArray(recommendedVoice.quality) && recommendedVoice.quality.length > 1) { + + const voicesFound = voices.filter(({ name }) => name.startsWith(recommendedVoice.name)); + if (voicesFound.length) { + + for (const qualityTested of ["high", "normal"] as TQuality[]) { + for (let i = 0; i < voicesFound.length; i++) { + const voice = voicesFound[i]; + + const rxp = /^.*\((.*)\)$/; + if (rxp.test(voice.name)) { + const res = rxp.exec(voice.name); + const maybeQualityString = res ? res[1] || "" : ""; + const qualityDataArray = qualityTested === "high" ? highQuality : normalQuality; + + if (recommendedVoice.quality.includes(qualityTested) && qualityDataArray.includes(maybeQualityString)) { + voice.quality = qualityTested; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + + voicesFound.splice(i, 1); + voicesLowerQuality.push(...(voicesFound.map((v) => { + v.quality = "low"; // Todo need to be more precise for 'normal' quality voices + return updateVoiceInfo(recommendedVoice, v); + }))); + + continue recommendedVoiceLoop; + } + } + } + } + const voice = voicesFound[0]; + for (let i = 1; i < voicesFound.length; i++) { + voicesLowerQuality.push(voicesFound[i]); + } + + voice.quality = voicesFound.length > 3 ? "veryHigh" : voicesFound.length > 2 ? "high" : "normal"; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + + } + } else if (Array.isArray(recommendedVoice.altNames) && recommendedVoice.altNames.length) { + + const voiceFound = voices.find(({ name }) => name === recommendedVoice.name); + if (voiceFound) { + const voice = voiceFound; + + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + + // voice Name found so altNames array must be filter and push to voicesLowerQuality + const altNamesVoicesFound = voices.filter(({name}) => recommendedVoice.altNames!.includes(name)); + // TODO: Typescript bug type assertion doesn't work, need to force the compiler with the Non-null Assertion Operator + + voicesLowerQuality.push(...(altNamesVoicesFound.map((v) => { + v.quality = recommendedVoice.quality[0]; + return updateVoiceInfo(recommendedVoice, v); + }))); + } else { + + // filter voices on altNames, keep the first and push the remaining to voicesLowerQuality + const altNamesVoicesFound = voices.filter(({name}) => recommendedVoice.altNames!.includes(name)); + if (altNamesVoicesFound.length) { + + const voice = altNamesVoicesFound.shift() as IVoices; + + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + + + voicesLowerQuality.push(...(altNamesVoicesFound.map((v) => { + v.quality = recommendedVoice.quality[0]; + return updateVoiceInfo(recommendedVoice, v); + }))); + } + } + } else { + + const voiceFound = voices.find(({ name }) => name === recommendedVoice.name); + if (voiceFound) { + + const voice = voiceFound; + + voice.quality = Array.isArray(recommendedVoice.quality) ? recommendedVoice.quality[0] : undefined; + voicesRecommended.push(updateVoiceInfo(recommendedVoice, voice)); + + } + } + } + + return [voicesRecommended, voicesLowerQuality]; +} + +const extractLangRegionFromBCP47 = (l: string) => [l.split("-")[0].toLowerCase(), l.split("-")[1]?.toUpperCase()]; + +export function sortByQuality(voices: IVoices[]) { + return voices.sort(({quality: qa}, {quality: qb}) => { + return compareQuality(qa, qb); + }); +} + +export function sortByName(voices: IVoices[]) { + return voices.sort(({name: na}, {name: nb}) => { + return na.localeCompare(nb); + }) +} + +export function sortByGender(voices: IVoices[], genderFirst: TGender) { + return voices.sort(({gender: ga}, {gender: gb}) => { + return ga === gb ? 0 : ga === genderFirst ? -1 : gb === genderFirst ? -1 : 1; + }) +} + +function orderByPreferredLanguage(preferredLanguage?: string[] | string): string[] { + preferredLanguage = Array.isArray(preferredLanguage) ? preferredLanguage : + preferredLanguage ? [preferredLanguage] : []; + + return [...(new Set([...preferredLanguage, ...navigatorLanguages()]))]; +} +function orderByPreferredRegion(preferredLanguage?: string[] | string): string[] { + preferredLanguage = Array.isArray(preferredLanguage) ? preferredLanguage : + preferredLanguage ? [preferredLanguage] : []; + + const regionByDefaultArray = Object.values(defaultRegion); + + return [...(new Set([...preferredLanguage, ...navigatorLanguages(), ...regionByDefaultArray]))]; +} + +const getLangFromBCP47Array = (a: string[]) => { + return [...(new Set(a.map((v) => extractLangRegionFromBCP47(v)[0]).filter((v) => !!v)))]; +} +const getRegionFromBCP47Array = (a: string[]) => { + return [...(new Set(a.map((v) => (extractLangRegionFromBCP47(v)[1] || "").toUpperCase()).filter((v) => !!v)))]; +} + +export function sortByLanguage(voices: IVoices[], preferredLanguage: string[] | string = [], localization: string | undefined = navigatorLang()): IVoices[] { + + const languages = getLangFromBCP47Array(orderByPreferredLanguage(preferredLanguage)); + + const voicesSorted: IVoices[] = []; + for (const lang of languages) { + voicesSorted.push(...voices.filter(({language: voiceLanguage}) => lang === extractLangRegionFromBCP47(voiceLanguage)[0])); + } + + let langueName: Intl.DisplayNames | undefined = undefined; + if (localization) { + try { + langueName = new Intl.DisplayNames([localization], { type: 'language' }); + } catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + + const remainingVoices = voices.filter((v) => !voicesSorted.includes(v)); + remainingVoices.sort(({ language: a }, { language: b }) => { + + let nameA = a, nameB = b; + try { + if (langueName) { + nameA = langueName.of(extractLangRegionFromBCP47(a)[0]) || a; + nameB = langueName.of(extractLangRegionFromBCP47(b)[0]) || b; + } + } catch (e) { + // ignore + } + return nameA.localeCompare(nameB); + }); + + return [...voicesSorted, ...remainingVoices]; +} + +export function sortByRegion(voices: IVoices[], preferredRegions: string[] | string = [], localization: string | undefined = navigatorLang()): IVoices[] { + + const regions = getRegionFromBCP47Array(orderByPreferredRegion(preferredRegions)); + + const voicesSorted: IVoices[] = []; + for (const reg of regions) { + voicesSorted.push(...voices.filter(({language: voiceLanguage}) => reg === extractLangRegionFromBCP47(voiceLanguage)[1])); + } + + let regionName: Intl.DisplayNames | undefined = undefined; + if (localization) { + try { + regionName = new Intl.DisplayNames([localization], { type: 'region' }); + } catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + + const remainingVoices = voices.filter((v) => !voicesSorted.includes(v)); + remainingVoices.sort(({ language: a }, { language: b }) => { + + let nameA = a, nameB = b; + try { + if (regionName) { + nameA = regionName.of(extractLangRegionFromBCP47(a)[1]) || a; + nameB = regionName.of(extractLangRegionFromBCP47(b)[1]) || b; + } + } catch (e) { + // ignore + } + return nameA.localeCompare(nameB); + }); + + return [...voicesSorted, ...remainingVoices]; +} + +export interface ILanguages { + label: string; + code: string; + count: number; +} +export function listLanguages(voices: IVoices[], localization: string | undefined = navigatorLang()): ILanguages[] { + let langueName: Intl.DisplayNames | undefined = undefined; + if (localization) { + try { + langueName = new Intl.DisplayNames([localization], { type: 'language' }); + } catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + return voices.reduce((acc, cv) => { + const [lang] = extractLangRegionFromBCP47(cv.language); + let name = lang; + try { + if (langueName) { + name = langueName.of(lang) || lang; + } + } catch (e) { + console.error("langueName.of throw an error with ", lang, e); + } + const found = acc.find(({code}) => code === lang) + if (found) { + found.count++; + } else { + acc.push({code: lang, count: 1, label: name}); + } + return acc; + }, []); +} +export function listRegions(voices: IVoices[], localization: string | undefined = navigatorLang()): ILanguages[] { + let regionName: Intl.DisplayNames | undefined = undefined; + if (localization) { + try { + regionName = new Intl.DisplayNames([localization], { type: 'region' }); + } catch (e) { + console.error("Intl.DisplayNames throw an exception with ", localization, e); + } + } + return voices.reduce((acc, cv) => { + const [,region] = extractLangRegionFromBCP47(cv.language); + let name = region; + try { + if (regionName) { + name = regionName.of(region) || region; + } + } catch (e) { + console.error("regionName.of throw an error with ", region, e); + } + const found = acc.find(({code}) => code === region); + if (found) { + found.count++; + } else { + acc.push({code: region, count: 1, label: name}); + } + return acc; + }, []); +} + +export type TGroupVoices = Map; +export function groupByLanguages(voices: IVoices[], preferredLanguage: string[] | string = [], localization: string | undefined = navigatorLang()): TGroupVoices { + + const voicesSorted = sortByLanguage(voices, preferredLanguage, localization); + + const languagesStructure = listLanguages(voicesSorted, localization); + const res: TGroupVoices = new Map(); + for (const { code, label } of languagesStructure) { + res.set(label, voicesSorted + .filter(({ language: voiceLang }) => { + const [l] = extractLangRegionFromBCP47(voiceLang); + return l === code; + })); + } + return res; +} + +export function groupByRegions(voices: IVoices[], preferredRegions: string[] | string = [], localization: string | undefined = navigatorLang()): TGroupVoices { + + const voicesSorted = sortByRegion(voices, preferredRegions, localization); + + const languagesStructure = listRegions(voicesSorted, localization); + const res: TGroupVoices = new Map(); + for (const { code, label } of languagesStructure) { + res.set(label, voicesSorted + .filter(({ language: voiceLang }) => { + const [, r] = extractLangRegionFromBCP47(voiceLang); + return r === code; + })); + } + return res; +} + +export function groupByKindOfVoices(allVoices: IVoices[]): TGroupVoices { + + const [recommendedVoices, lowQualityVoices] = filterOnRecommended(allVoices); + const remainingVoice = allVoices.filter((v) => !recommendedVoices.includes(v) && !lowQualityVoices.includes(v)); + const noveltyFiltered = filterOnNovelty(remainingVoice); + const noveltyVoices = remainingVoice.filter((v) => !noveltyFiltered.includes(v)); + const veryLowQualityFiltered = filterOnVeryLowQuality(remainingVoice); + const veryLowQualityVoices = remainingVoice.filter((v) => !veryLowQualityFiltered.includes(v)); + const remainingVoiceFiltered = filterOnNovelty(filterOnVeryLowQuality(remainingVoice)); + + const res: TGroupVoices = new Map(); + res.set("recommendedVoices", recommendedVoices); + res.set("lowerQuality", lowQualityVoices); + res.set("novelty", noveltyVoices); + res.set("veryLowQuality", veryLowQualityVoices); + res.set("remaining", remainingVoiceFiltered); + + return res; +} + +export function getLanguages(voices: IVoices[], preferredLanguage: string[] | string = [], localization: string | undefined = navigatorLang()): ILanguages[] { + + const group = groupByLanguages(voices, preferredLanguage, localization); + + return Array.from(group.entries()).map(([label, _voices]) => { + return {label, count: _voices.length, code: extractLangRegionFromBCP47(_voices[0]?.language || "")[0]} + }); +} + +/** + * Parse and extract SpeechSynthesisVoices, + * @returns IVoices[] + */ +export async function getVoices(preferredLanguage?: string[] | string, localization?: string) { + + const allVoices = parseSpeechSynthesisVoices(await getSpeechSynthesisVoices()); + const [recommendedVoices, lowQualityVoices] = filterOnRecommended(allVoices); + const remainingVoice = allVoices.filter((v) => !recommendedVoices.includes(v) && !lowQualityVoices.includes(v)); + const remainingVoiceFiltered = filterOnNovelty(filterOnVeryLowQuality(remainingVoice)); + + const voices = [recommendedVoices, remainingVoiceFiltered].flat(); + + const voicesSorted = sortByLanguage(sortByQuality(voices), preferredLanguage, localization || navigatorLang()); + + return voicesSorted; +} \ No newline at end of file diff --git a/test/voices.test.ts b/test/voices.test.ts new file mode 100644 index 0000000..8df280d --- /dev/null +++ b/test/voices.test.ts @@ -0,0 +1,554 @@ +import test from "ava"; +import { filterOnRecommended, groupByLanguages, IVoices, sortByLanguage, groupByRegions } from "../src/voices.js"; +import { IRecommended } from "../src/data.js"; +// import { getVoices } from "../src/voices.js"; + +test('dumb test', t => { + t.deepEqual([], []); +}); + +test.before(t => { + // This runs before all tests + globalThis.window = { navigator: { languages: [] } as any } as any; +}); + +test('sortByLanguage: Empty preferred language list', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, [], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); + +test('sortByLanguage: Preferred language list with one language', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, ['fr-FR'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'fr-FR'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'en-US'); +}); + +test('sortByLanguage: Preferred language list with multiple languages', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, ['fr-FR', 'es-ES'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'fr-FR'); + t.true(result[1].language === 'es-ES'); + t.true(result[2].language === 'en-US'); + t.true(result[3].language === 'en-US'); +}); + +test('sortByLanguage: No matching languages', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, ['de-DE'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); + +test('sortByLanguage: Preferred language list is not an array', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, 'en-US', ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); + +test('sortByLanguage: Preferred language undefined and navigator langua', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, 'en-US', ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); + +test('sortByLanguage: Preferred language list with one language and navigator.languages', t => { + (globalThis.window.navigator as any).languages = ['fr-FR', 'en-US']; + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, ['fr-FR'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'fr-FR'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'en-US'); +}); + +test('sortByLanguage: Preferred language list with multiple languages and navigator.languages', t => { + (globalThis.window.navigator as any).languages = ['fr-FR', 'en-US']; + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, ['fr-FR', 'es-ES'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'fr-FR'); + t.true(result[1].language === 'es-ES'); + t.true(result[2].language === 'en-US'); + t.true(result[3].language === 'en-US'); +}); + +test('sortByLanguage: No matching languages and navigator.languages', t => { + (globalThis.window.navigator as any).languages = ['de-DE', 'en-US']; + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, ['de-DE'], ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); + +test('sortByLanguage: Preferred language list is not an array and navigator.languages', t => { + (globalThis.window.navigator as any).languages = ['fr-FR', 'en-US']; + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + + const result = sortByLanguage(voices, 'en-US', ""); + t.true(result.length === voices.length); + t.true(result[0].language === 'en-US'); + t.true(result[1].language === 'en-US'); + t.true(result[2].language === 'fr-FR'); +}); + +test('filterOnRecommended: Empty input', t => { + const voices: IVoices[] = []; + const result = filterOnRecommended(voices); + t.deepEqual(result, [[], []]); +}); + +test('filterOnRecommended: No recommended voices', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + ]; + const result = filterOnRecommended(voices, []); + t.deepEqual(result, [[], []]); +}); + +test('filterOnRecommended: Single recommended voice with single quality', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + ]; + const recommended: IRecommended[] = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], language: 'en-US', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [], + ]); +}); + +test('filterOnRecommended: Single recommended voice with multiple qualities', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + ]; + const recommended: IRecommended[] = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high', 'normal'], language: 'en-US', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [], + ]); +}); + +test('filterOnRecommended: Single recommended voice with multiple qualities and remaining lowQuality', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 1 (Premium)', language: 'en-US', offlineAvailability: true, pitchControl: true }, + ]; + const recommended: IRecommended[] = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high', 'normal'], language: 'en-US', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri3', name: 'Name 1 (Premium)', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'low', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + ]); +}); + +test('filterOnRecommended: Multiple recommended voices', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + ]; + const recommended: IRecommended[] = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], language: 'en-US', localizedName: "" }, + { name: 'Name 2', label: 'Voice 2', quality: ['normal'], language: 'es-ES', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [], + ]); +}); +test('filterOnRecommended: Recommended voices with altNames', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 1-1', voiceURI: 'uri1-1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + ]; + const recommended: IRecommended[] = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], altNames: ['Name 1 with an altNames'], language: 'en-US', localizedName: "" }, + { name: 'Name 2', label: 'Voice 2', quality: ['normal'], language: 'es-ES', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [ + { label: 'Voice 1', voiceURI: 'uri1-1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + ]); +}); +test('filterOnRecommended: Recommended voices with altNames only and voices not in name', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + ]; + const recommended: IRecommended[] = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], altNames: ['Name 1 with an altNames'], language: 'en-US', localizedName: "" }, + { name: 'Name 2', label: 'Voice 2', quality: ['normal'], language: 'es-ES', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [ + ], + ]); +}); +test('filterOnRecommended: Recommended voices with multiple altNames and voices not in name', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 1-1', voiceURI: 'uri1-1', name: 'Name 1 with a second altNames', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + ]; + const recommended: IRecommended[] = [ + { name: 'Name 1', label: 'Voice 1', quality: ['high'], altNames: ['Name 1 with an altNames', 'Name 1 with a second altNames'], language: 'en-US', localizedName: "" }, + { name: 'Name 2', label: 'Voice 2', quality: ['normal'], language: 'es-ES', localizedName: "" }, + ]; + const result = filterOnRecommended(voices, recommended); + t.deepEqual(result, [ + [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1 with an altNames', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'es-ES', offlineAvailability: false, pitchControl: false, quality: 'normal', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + [ + { label: 'Voice 1', voiceURI: 'uri1-1', name: 'Name 1 with a second altNames', language: 'en-US', offlineAvailability: true, pitchControl: true, quality: 'high', recommendedRate: undefined, recommendedPitch: undefined, gender: undefined }, + ], + ]); +}); +test('groupByLanguage: ', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + ]; + const result = groupByLanguages(voices, ['fr-FR', 'es-ES'], ""); + t.deepEqual(result, new Map([ + ['fr', [ + { + label: 'Voice 2', + language: 'fr-FR', + name: 'Name 2', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri2', + }, + ]], + ['es', [ + { + label: 'Voice 4', + language: 'es-ES', + name: 'Name 4', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri4', + }, + ]], + ['en', [ + { + label: 'Voice 1', + language: 'en-US', + name: 'Name 1', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri1', + }, + { + label: 'Voice 3', + language: 'en-US', + name: 'Name 3', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri3', + }, + ]], + ])); +}); +test('groupByLanguage: localized en', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + ]; + const result = groupByLanguages(voices, ['fr-FR', 'es-ES'], "en"); + t.deepEqual(result, new Map([ + ['French', [ + { + label: 'Voice 2', + language: 'fr-FR', + name: 'Name 2', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri2', + }, + ]], + ['Spanish', [ + { + label: 'Voice 4', + language: 'es-ES', + name: 'Name 4', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri4', + }, + ]], + ['English', [ + { + label: 'Voice 1', + language: 'en-US', + name: 'Name 1', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri1', + }, + { + label: 'Voice 3', + language: 'en-US', + name: 'Name 3', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri3', + }, + ]], + ])); +}); +test('groupByRegion: ', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-GB', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 5', voiceURI: 'uri5', name: 'Name 5', language: 'en-CA', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 6', voiceURI: 'uri6', name: 'Name 6', language: 'fr-CA', offlineAvailability: true, pitchControl: true }, + ]; + const result = groupByRegions(voices, ['fr-FR', 'es-ES'], ""); + t.deepEqual(result, new Map([ + ['FR', [ + { + label: 'Voice 2', + language: 'fr-FR', + name: 'Name 2', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri2', + }, + ]], + ['ES', [ + { + label: 'Voice 4', + language: 'es-ES', + name: 'Name 4', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri4', + }, + ]], + ['US', [ + { + label: 'Voice 1', + language: 'en-US', + name: 'Name 1', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri1', + }, + ]], + ['CA', [ + { + label: 'Voice 5', + language: 'en-CA', + name: 'Name 5', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri5', + }, + { + label: 'Voice 6', + language: 'fr-CA', + name: 'Name 6', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri6', + }, + ]], + ['GB', [ + { + label: 'Voice 3', + language: 'en-GB', + name: 'Name 3', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri3', + }, + ]], + ])); +}); +test('groupByRegion: localized fr', t => { + const voices = [ + { label: 'Voice 1', voiceURI: 'uri1', name: 'Name 1', language: 'en-US', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 2', voiceURI: 'uri2', name: 'Name 2', language: 'fr-FR', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 3', voiceURI: 'uri3', name: 'Name 3', language: 'en-GB', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 4', voiceURI: 'uri4', name: 'Name 4', language: 'es-ES', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 5', voiceURI: 'uri5', name: 'Name 5', language: 'en-CA', offlineAvailability: true, pitchControl: true }, + { label: 'Voice 6', voiceURI: 'uri6', name: 'Name 6', language: 'fr-CA', offlineAvailability: true, pitchControl: true }, + ]; + const result = groupByRegions(voices, ['fr-FR', 'es-ES'], "fr"); + t.deepEqual(result, new Map([ + ['France', [ + { + label: 'Voice 2', + language: 'fr-FR', + name: 'Name 2', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri2', + }, + ]], + ['Espagne', [ + { + label: 'Voice 4', + language: 'es-ES', + name: 'Name 4', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri4', + }, + ]], + ['États-Unis', [ + { + label: 'Voice 1', + language: 'en-US', + name: 'Name 1', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri1', + }, + ]], + ['Canada', [ + { + label: 'Voice 5', + language: 'en-CA', + name: 'Name 5', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri5', + }, + { + label: 'Voice 6', + language: 'fr-CA', + name: 'Name 6', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri6', + }, + ]], + ['Royaume-Uni', [ + { + label: 'Voice 3', + language: 'en-GB', + name: 'Name 3', + offlineAvailability: true, + pitchControl: true, + voiceURI: 'uri3', + }, + ]], + ])); +}); \ No newline at end of file diff --git a/tsconfig-base.json b/tsconfig-base.json new file mode 100644 index 0000000..67df891 --- /dev/null +++ b/tsconfig-base.json @@ -0,0 +1,110 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ES2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "NodeNext", + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./build", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "exclude": ["node_modules", "dist"], + "include": ["src", "test"] +} diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json new file mode 100644 index 0000000..cb4802a --- /dev/null +++ b/tsconfig-cjs.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "moduleResolution": "node", + "module": "commonjs", + "outDir": "build/cjs", + "target": "es2015", + "lib": ["es2019", "DOM", "ES2020.Intl"] + }, + "exclude": ["node_modules", "dist", "test"], +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5356592 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "moduleResolution": "NodeNext", + "module": "NodeNext", + "outDir": "build/mjs", + "target": "esnext" + } +} \ No newline at end of file