// available langs export type LanguageItem = { cr_locale?: string; hd_locale?: string; adn_locale?: string; new_hd_locale?: string; locale: string; code: string; name: string; language?: string; }; const languages: LanguageItem[] = [ { locale: 'un', code: 'und', name: 'Undetermined', language: 'Undetermined', new_hd_locale: 'und', cr_locale: 'und', adn_locale: 'und' }, { cr_locale: 'en-US', new_hd_locale: 'en-US', hd_locale: 'English', locale: 'en', code: 'eng', name: 'English' }, { cr_locale: 'en-IN', locale: 'en-IN', code: 'eng', name: 'English (India)' }, { cr_locale: 'es-LA', new_hd_locale: 'es-MX', hd_locale: 'Spanish LatAm', locale: 'es-419', code: 'spa', name: 'Spanish', language: 'Latin American Spanish' }, { cr_locale: 'es-419', hd_locale: 'Spanish', locale: 'es-419', code: 'spa-419', name: 'Spanish', language: 'Latin American Spanish' }, { cr_locale: 'es-ES', new_hd_locale: 'es-ES', hd_locale: 'Spanish Europe', locale: 'es-ES', code: 'spa-ES', name: 'Castilian', language: 'European Spanish' }, { cr_locale: 'pt-BR', new_hd_locale: 'pt-BR', hd_locale: 'Portuguese', locale: 'pt-BR', code: 'por', name: 'Portuguese', language: 'Brazilian Portuguese' }, { cr_locale: 'pt-PT', locale: 'pt-PT', code: 'por', name: 'Portuguese (Portugal)', language: 'Portugues (Portugal)' }, { cr_locale: 'fr-FR', adn_locale: 'fr', hd_locale: 'French', locale: 'fr', code: 'fra', name: 'French' }, { cr_locale: 'de-DE', adn_locale: 'de', hd_locale: 'German', locale: 'de', code: 'deu', name: 'German' }, { cr_locale: 'ar-ME', locale: 'ar', code: 'ara-ME', name: 'Arabic' }, { cr_locale: 'ar-SA', hd_locale: 'Arabic', locale: 'ar', code: 'ara', name: 'Arabic (Saudi Arabia)' }, { cr_locale: 'it-IT', hd_locale: 'Italian', locale: 'it', code: 'ita', name: 'Italian' }, { cr_locale: 'ru-RU', hd_locale: 'Russian', locale: 'ru', code: 'rus', name: 'Russian' }, { cr_locale: 'tr-TR', hd_locale: 'Turkish', locale: 'tr', code: 'tur', name: 'Turkish' }, { cr_locale: 'hi-IN', locale: 'hi', code: 'hin', name: 'Hindi' }, { locale: 'zh', code: 'cmn', name: 'Chinese (Mandarin, PRC)' }, { cr_locale: 'zh-CN', locale: 'zh-CN', code: 'zho', name: 'Chinese (Mainland China)' }, { cr_locale: 'zh-TW', locale: 'zh-TW', code: 'chi', name: 'Chinese (Taiwan)' }, { cr_locale: 'zh-HK', locale: 'zh-HK', code: 'zh-HK', name: 'Chinese (Hong-Kong)', language: '中文 (粵語)' }, { cr_locale: 'ko-KR', hd_locale: 'Korean', locale: 'ko', code: 'kor', name: 'Korean' }, { cr_locale: 'ca-ES', locale: 'ca-ES', code: 'cat', name: 'Catalan' }, { cr_locale: 'pl-PL', locale: 'pl-PL', code: 'pol', name: 'Polish' }, { cr_locale: 'th-TH', locale: 'th-TH', code: 'tha', name: 'Thai', language: 'ไทย' }, { cr_locale: 'ta-IN', locale: 'ta-IN', code: 'tam', name: 'Tamil (India)', language: 'தமிழ்' }, { cr_locale: 'ms-MY', locale: 'ms-MY', code: 'may', name: 'Malay (Malaysia)', language: 'Bahasa Melayu' }, { cr_locale: 'vi-VN', locale: 'vi-VN', code: 'vie', name: 'Vietnamese', language: 'Tiếng Việt' }, { cr_locale: 'id-ID', locale: 'id-ID', code: 'ind', name: 'Indonesian', language: 'Bahasa Indonesia' }, { cr_locale: 'te-IN', locale: 'te-IN', code: 'tel', name: 'Telugu (India)', language: 'తెలుగు' }, { cr_locale: 'ja-JP', adn_locale: 'ja', hd_locale: 'Japanese', locale: 'ja', code: 'jpn', name: 'Japanese' } ]; // add en language names (() => { for (const languageIndex in languages) { if (!languages[languageIndex].language) { languages[languageIndex].language = languages[languageIndex].name; } } })(); // construct dub language codes const dubLanguageCodes = (() => { const dubLanguageCodesArray: string[] = []; for (const language of languages) { dubLanguageCodesArray.push(language.code); } return [...new Set(dubLanguageCodesArray)]; })(); // construct subtitle languages filter const subtitleLanguagesFilter = (() => { const subtitleLanguagesExtraParameters = ['all', 'none']; return [ ...subtitleLanguagesExtraParameters, ...new Set( languages.map((l) => { return l.locale; }) ) ]; })(); const searchLocales = (() => { return [ '', ...new Set( languages .map((l) => { return l.cr_locale; }) .slice(0, -1) ), ...new Set( languages .map((l) => { return l.adn_locale; }) .slice(0, -1) ) ]; })(); // convert const fixLanguageTag = (tag: string) => { tag = typeof tag == 'string' ? tag : 'und'; const tagLangLC = tag.match(/^(\w{2})-?(\w{2})$/); if (tagLangLC) { const tagLang = `${tagLangLC[1]}-${tagLangLC[2].toUpperCase()}`; if (findLang(tagLang).cr_locale != 'und') { return findLang(tagLang).cr_locale; } else { return tagLang; } } else { return tag; } }; // find lang by cr_locale const findLang = (cr_locale: string) => { const lang = languages.find((l) => { return l.cr_locale == cr_locale; }); return lang ? lang : languages.find((l) => l.code === 'und') || { cr_locale: 'und', locale: 'un', code: 'und', name: 'Undetermined', language: 'Undetermined' }; }; const fixAndFindCrLC = (cr_locale: string) => { const str = fixLanguageTag(cr_locale); return findLang(str || ''); }; // rss subs lang parser const parseRssSubtitlesString = (subs: string) => { const splitMap = subs .replace(/\s/g, '') .split(',') .map((s) => { return fixAndFindCrLC(s).locale; }); const sort = sortTags(splitMap); return sort.join(', '); }; // parse subtitles Array const parseSubtitlesArray = (tags: string[]) => { const sort = sortSubtitles( tags.map((t) => { return { locale: fixAndFindCrLC(t).locale }; }) ); return sort .map((t) => { return t.locale; }) .join(', '); }; // sort subtitles const sortSubtitles = < T extends { [key: string]: unknown; } = Record >( data: T[], sortkey?: keyof T ): T[] => { const idx: Record = {}; const key = sortkey || ('locale' as keyof T); const tags = [...new Set(Object.values(languages).map((e) => e.locale))]; for (const l of tags) { idx[l] = Object.keys(idx).length + 1; } data.sort((a, b) => { const ia = idx[a[key] as string] ? idx[a[key] as string] : 50; const ib = idx[b[key] as string] ? idx[b[key] as string] : 50; return ia - ib; }); return data; }; const sortTags = (data: string[]) => { const retData = data.map((e) => { return { locale: e }; }); const sort = sortSubtitles(retData); return sort.map((e) => e.locale as string); }; const subsFile = (fnOutput: string, subsIndex: string, langItem: LanguageItem, isCC: boolean, ccTag: string, isSigns?: boolean, format?: string) => { subsIndex = (parseInt(subsIndex) + 1).toString().padStart(2, '0'); return `${fnOutput}.${subsIndex}.${langItem.code}.${langItem.language}${isCC ? `.${ccTag}` : ''}${isSigns ? '.signs' : ''}.${format ? format : 'ass'}`; }; // construct dub langs const const dubLanguages = (() => { const dubDb: Record = {}; for (const lang of languages) { if (!Object.keys(dubDb).includes(lang.name)) { dubDb[lang.name] = lang.code; } } return dubDb; })(); // dub regex const dubRegExpStr = `\\((${Object.keys(dubLanguages).join('|')})(?: (Dub|VO))?\\)$`; const dubRegExp = new RegExp(dubRegExpStr); // code to lang name const langCode2name = (code: string) => { const codeIdx = dubLanguageCodes.indexOf(code); return Object.keys(dubLanguages)[codeIdx]; }; // locale to lang name const locale2language = (locale: string) => { const filteredLocale = languages.filter((l) => { return l.locale == locale; }); return filteredLocale[0]; }; // output export { languages, dubLanguageCodes, dubLanguages, langCode2name, locale2language, dubRegExp, subtitleLanguagesFilter, searchLocales, fixLanguageTag, findLang, fixAndFindCrLC, parseRssSubtitlesString, parseSubtitlesArray, sortSubtitles, sortTags, subsFile };