'use strict'; const spacesRegExp = /[  ]/g; //учитывается обычный пробел и тот, что ставится на японской раскладке const kanaRegExp = /\p{Script=Kana}{1}|\p{Script=Hira}{1}|ー/ug; function isThereKanaAround(prevSyllable, nextSyllable) { if (prevSyllable && isKana(prevSyllable)) return true; if (nextSyllable && isKana(nextSyllable)) return true; return false; } function isKana(string) { return !!string.match(kanaRegExp); } function isItSmallKana(string) { return !!string.match(/[ぁァァぃィィぅゥゥぇェェぉォォゎヮゕヵゖヶゃャゅュょョ]/); } function getLangFromSystem(system) { if (system === 'nonstandard-ru' || system === 'polivanov' || system === 'static-ru') return 'ru'; return 'en'; } function isItPolysyllableItrationMark(symbolBefore, symbolAfter) { switch (symbolBefore) { case '〱': return true; case '〳': return symbolAfter === '〵'; case '/': return symbolAfter === '\'; case '/': return symbolAfter === '\\'; default: return false; } } //supported extended kana - https://wikipedia.org/wiki/Katakana#Extended_katakana function fromKanaEN(kanaText, system = 'hepburn') { const splitedSentence = kanaText.toLowerCase().split(spacesRegExp); const isThereOnlyOneWord = splitedSentence.length === 1; const transcriptedWords = []; for (const word of splitedSentence) { const splitedWord = Array.from(word); if (!splitedWord) continue; const isItTheOnlySyllable = splitedWord.length === 1; const transcriptedSplitedWord = []; splitedWord.forEach((kana, index) => { let transcriptedSyllable = kana; switch (kana) { case 'あ': case 'ア': case '㋐': case 'ア': transcriptedSyllable = 'a'; break; case 'い': case 'イ': case '㋑': case 'イ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'y'; } else { transcriptedSyllable = 'i'; } break; } case 'う': case 'ウ': case '㋒': case 'ウ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'w'; } else { transcriptedSyllable = 'u'; } break; } case 'え': case 'エ': case '㋓': case 'エ': transcriptedSyllable = 'e'; break; case 'お': case 'オ': case '㋔': case 'オ': transcriptedSyllable = 'o'; break; case 'か': case 'カ': case '㋕': case 'カ': transcriptedSyllable = 'ka'; break; case 'き': case 'キ': case '㋖': case 'キ': transcriptedSyllable = 'ki'; break; case 'く': case 'ク': case '㋗': case 'ク': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && (nextSyllable === 'ゎ' || nextSyllable === 'ヮ')) { transcriptedSyllable = 'k'; } else if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'kw'; } else { transcriptedSyllable = 'ku'; } break; } case 'け': case 'ケ': case '㋘': case 'ケ': transcriptedSyllable = 'ke'; break; case 'こ': case 'コ': case '㋙': case 'コ': transcriptedSyllable = 'ko'; break; case 'が': case 'ガ': transcriptedSyllable = 'ga'; break; case 'ぎ': case 'ギ': transcriptedSyllable = 'gi'; break; case 'ぐ': case 'グ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && (nextSyllable === 'ゎ' || nextSyllable === 'ヮ')) { transcriptedSyllable = 'g'; } else if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'gw'; } else { transcriptedSyllable = 'gu'; } break; } case 'げ': case 'ゲ': transcriptedSyllable = 'ge'; break; case 'ご': case 'ゴ': transcriptedSyllable = 'go'; break; case 'さ': case 'サ': case '㋚': case 'サ': transcriptedSyllable = 'sa'; break; case 'し': case 'シ': case '㋛': case 'シ': transcriptedSyllable = system === 'hepburn' ? 'shi' : 'si'; break; case 'す': case 'ス': case '㋜': case 'ス': transcriptedSyllable = 'su'; break; case 'せ': case 'セ': case '㋝': case 'セ': transcriptedSyllable = 'se'; break; case 'そ': case 'ソ': case '㋞': case 'ソ': transcriptedSyllable = 'so'; break; case 'ざ': case 'ザ': transcriptedSyllable = 'za'; break; case 'じ': case 'ジ': transcriptedSyllable = system === 'hepburn' ? 'ji' : 'zi'; break; case 'ず': case 'ズ': transcriptedSyllable = 'zu'; break; case 'ぜ': case 'ゼ': transcriptedSyllable = 'ze'; break; case 'ぞ': case 'ゾ': transcriptedSyllable = 'zo'; break; case 'た': case 'タ': case '㋟': case 'タ': transcriptedSyllable = 'ta'; break; case 'ち': case 'チ': case '㋠': case 'チ': transcriptedSyllable = system === 'hepburn' ? 'chi' : 'ti'; break; case 'つ': case 'ツ': case '㋡': case 'ツ': transcriptedSyllable = system === 'hepburn' ? 'tsu' : 'tu'; break; case 'て': case 'テ': case '㋢': case 'テ': transcriptedSyllable = 'te'; break; case 'と': case 'ト': case '㋣': case 'ト': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'tw'; } else { transcriptedSyllable = 'to'; } break; } case 'だ': case 'ダ': transcriptedSyllable = 'da'; break; case 'ぢ': case 'ヂ': transcriptedSyllable = system === 'hepburn' ? 'ji' : 'zi'; break; case 'づ': case 'ヅ': transcriptedSyllable = 'zu'; break; case 'で': case 'デ': transcriptedSyllable = 'de'; break; case 'ど': case 'ド': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'dw'; } else { transcriptedSyllable = 'do'; } break; } case 'な': case 'ナ': case '㋤': case 'ナ': transcriptedSyllable = 'na'; break; case 'に': case 'ニ': case '㋥': case 'ニ': transcriptedSyllable = 'ni'; break; case 'ぬ': case 'ヌ': case '㋦': case 'ヌ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'nw'; } else { transcriptedSyllable = 'nu'; } break; } case 'ね': case 'ネ': case '㋧': case 'ネ': transcriptedSyllable = 'ne'; break; case 'の': case 'ノ': case '㋨': case 'ノ': transcriptedSyllable = 'no'; break; case 'は': case 'ハ': case '㋩': case 'ハ': { if (!isThereOnlyOneWord && system !== 'nihon-shiki') { if (isItTheOnlySyllable) { transcriptedSyllable = 'wa'; break; } else { const nextSyllable = splitedWord[index + 1]; const prevSyllable = splitedWord[index - 1]; if (!isThereKanaAround(prevSyllable, nextSyllable)) { transcriptedSyllable = 'wa'; break; } } } transcriptedSyllable = 'ha'; break; } case 'ひ': case 'ヒ': case '㋪': case 'ヒ': transcriptedSyllable = 'hi'; break; case 'ふ': case 'フ': case '㋫': case 'フ': transcriptedSyllable = system === 'hepburn' ? 'fu' : 'hu'; break; case 'へ': case 'ヘ': case '㋬': case 'ヘ': { if (!isThereOnlyOneWord && system !== 'nihon-shiki') { if (isItTheOnlySyllable) { transcriptedSyllable = 'e'; break; } else { const nextSyllable = splitedWord[index + 1]; const prevSyllable = splitedWord[index - 1]; if (!isThereKanaAround(prevSyllable, nextSyllable)) { transcriptedSyllable = 'e'; break; } } } transcriptedSyllable = 'he'; break; } case 'ほ': case 'ホ': case '㋭': case 'ホ': transcriptedSyllable = 'ho'; break; case 'ば': case 'バ': transcriptedSyllable = 'ba'; break; case 'び': case 'ビ': transcriptedSyllable = 'bi'; break; case 'ぶ': case 'ブ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'bw'; } else { transcriptedSyllable = 'bu'; } break; } case 'べ': case 'ベ': transcriptedSyllable = 'be'; break; case 'ぼ': case 'ボ': transcriptedSyllable = 'bo'; break; case 'ぱ': case 'パ': transcriptedSyllable = 'pa'; break; case 'ぴ': case 'ピ': transcriptedSyllable = 'pi'; break; case 'ぷ': case 'プ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'pw'; } else { transcriptedSyllable = 'pu'; } break; } case 'ぺ': case 'ペ': transcriptedSyllable = 'pe'; break; case 'ぽ': case 'ポ': transcriptedSyllable = 'po'; break; case 'ま': case 'マ': case '㋮': case 'マ': transcriptedSyllable = 'ma'; break; case 'み': case 'ミ': case '㋯': case 'ミ': transcriptedSyllable = 'mi'; break; case 'む': case 'ム': case '㋰': case 'ム': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'mw'; } else { transcriptedSyllable = 'mu'; } break; } case 'め': case 'メ': case '㋱': case 'メ': transcriptedSyllable = 'me'; break; case 'も': case 'モ': case '㋲': case 'モ': transcriptedSyllable = 'mo'; break; case 'や': case 'ヤ': case '㋳': case 'ヤ': transcriptedSyllable = 'ya'; break; case 'ゆ': case 'ユ': case '㋴': case 'ユ': transcriptedSyllable = 'yu'; break; case '𛀁': transcriptedSyllable = 'ye'; break; case 'よ': case 'ヨ': case '㋵': case 'ヨ': transcriptedSyllable = 'yo'; break; case 'ら': case 'ラ': case '㋶': case 'ラ': transcriptedSyllable = 'ra'; break; case 'り': case 'リ': case '㋷': case 'リ': transcriptedSyllable = 'ri'; break; case 'る': case 'ル': case '㋸': case 'ル': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'rw'; } else { transcriptedSyllable = 'ru'; } break; } case 'れ': case 'レ': case '㋹': case 'レ': transcriptedSyllable = 're'; break; case 'ろ': case 'ロ': case '㋺': case 'ロ': transcriptedSyllable = 'ro'; break; case 'わ': case 'ワ': case '㋻': case 'ワ': transcriptedSyllable = 'wa'; break; case 'を': case 'ヲ': case '㋾': case 'ヲ': { if (!isThereOnlyOneWord && system !== 'nihon-shiki') { if (isItTheOnlySyllable) { transcriptedSyllable = 'o'; break; } else { const nextSyllable = splitedWord[index + 1]; const prevSyllable = splitedWord[index - 1]; if (!isThereKanaAround(prevSyllable, nextSyllable)) { transcriptedSyllable = 'o'; break; } } } transcriptedSyllable = 'wo'; break; } case 'ん': case 'ン': case 'ン': { const nextKana = splitedWord[index + 1]; const nextSyllableTranscription = nextKana ? fromKanaEN(nextKana) : undefined; const nextLetter = nextSyllableTranscription ? nextSyllableTranscription[0] : undefined; if (nextLetter && hasOnlyVowel$1(nextLetter)) { transcriptedSyllable = "n'"; break; } transcriptedSyllable = 'n'; break; } case 'ゃ': case 'ャ': case 'ャ': { let resultVowel = 'ya'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; if (system === 'hepburn') { //учёт расширенной каны つゃ/ツャ - tsya if (prevSyllableConsonants.length === 2 && prevSyllableConsonants !== 'ts') resultVowel = 'a'; if (prevSyllableConsonants === 'j') resultVowel = 'a'; } } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ゅ': case 'ュ': case 'ュ': { let resultVowel = 'yu'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; if (system === 'hepburn') { //учёт расширенной каны つゅ/ツュ - tsyu if (prevSyllableConsonants.length === 2 && prevSyllableConsonants !== 'ts') resultVowel = 'u'; if (prevSyllableConsonants === 'j') resultVowel = 'u'; } } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ょ': case 'ョ': case 'ョ': { let resultVowel = 'yo'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; if (system === 'hepburn') { //учёт расширенной каны つょ/ツョ - tsyo if (prevSyllableConsonants.length === 2 && prevSyllableConsonants !== 'ts') resultVowel = 'o'; if (prevSyllableConsonants === 'j') resultVowel = 'o'; } } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'っ': case 'ッ': case 'ッ': { transcriptedSyllable = system === 'hepburn' ? 'tsu' : 'tu'; const nextKana = splitedWord[index + 1]; const nextSyllableTranscription = fromKanaEN(nextKana); if (!nextKana || !nextSyllableTranscription) break; const nextLetter = nextSyllableTranscription[0]; if (!hasOnlyConsonants$3(nextLetter)) break; transcriptedSyllable = (nextKana === 'ち' || nextKana === 'チ') ? 't' : nextLetter; //По Хепбёрну っち пишется как tchi break; } case 'ー': case 'ー': { const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevKana || !prevSyllableTranscription) break; const prevLetter = prevSyllableTranscription.slice(-1); if (!hasOnlyVowel$1(prevLetter)) break; const vowelForExtending = prevLetter; transcriptedSyllable = vowelForExtending; break; } //расширенная кана case 'か゚': case 'カ゚': transcriptedSyllable = 'nga'; break; case 'き゚': case 'キ゚': transcriptedSyllable = 'ngi'; break; case 'く゚': case 'ク゚': transcriptedSyllable = 'ngu'; break; case 'け゚': case 'ケ゚': transcriptedSyllable = 'nge'; break; case 'こ゚': case 'コ゚': transcriptedSyllable = 'ngo'; break; case 'ら゚': case 'ラ゚': transcriptedSyllable = 'la'; break; case 'り゚': case 'リ゚': transcriptedSyllable = 'li'; break; case 'る゚': case 'ル゚': transcriptedSyllable = 'lu'; break; case 'れ゚': case 'レ゚': transcriptedSyllable = 'le'; break; case 'ろ゚': case 'ロ゚': transcriptedSyllable = 'lo'; break; case 'ぁ': case 'ァ': case 'ァ': { let resultVowel = 'a'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ぃ': case 'ィ': case 'ィ': { let resultVowel = 'i'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { resultVowel = 'y'; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ぅ': case 'ゥ': case 'ゥ': { let resultVowel = 'u'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ぇ': case 'ェ': case 'ェ': { let resultVowel = 'e'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } if (prevKana === 'に' || prevKana === 'ニ' || prevKana === 'ひ' || prevKana === 'ヒ' || prevKana === 'び' || prevKana === 'ビ' || prevKana === 'ぴ' || prevKana === 'ピ' || prevKana === 'み' || prevKana === 'ミ' || prevKana === 'り' || prevKana === 'リ' || prevKana === 'り゚' || prevKana === 'リ゚') resultVowel = 'ye'; if (system !== 'hepburn' && (prevKana === 'し' || prevKana === 'シ' || prevKana === 'ち' || prevKana === 'チ' || prevKana === 'じ' || prevKana === 'ジ' || prevKana === 'ぢ' || prevKana === 'ヂ')) resultVowel = 'ye'; transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ぉ': case 'ォ': case 'ォ': { let resultVowel = 'o'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ゎ': case 'ヮ': { let resultVowel = 'wa'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$2(prevSyllableTranscription); const needToChangeLastSyllab = !!(prevSyllableConsonants && prevSyllableConsonants.length <= 2); if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; transcriptedSyllable = prevSyllableConsonants + resultVowel; } else { transcriptedSyllable = resultVowel; } break; } case 'ゔ': case 'ヴ': transcriptedSyllable = 'vu'; break; case 'ウ゚': transcriptedSyllable = 'n'; break; case 'ゐ': case 'ヰ': case '㋼': transcriptedSyllable = 'wi'; break; case 'ゑ': case 'ヱ': case '㋽': transcriptedSyllable = 'we'; break; case 'わ゙': case 'ヷ': transcriptedSyllable = 'va'; break; case 'ゐ゙': case 'ヸ': transcriptedSyllable = 'vi'; break; case 'ゑ゙': case 'ヹ': transcriptedSyllable = 've'; break; case 'を゙': case 'ヺ': transcriptedSyllable = 'vo'; break; //iteration marks case 'ゝ': case 'ヽ': { const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription) break; if (isVoicedSyllable$1(prevSyllableTranscription)) { const unvoicedSyllable = getUnvoicedSyllable$1(prevSyllableTranscription); transcriptedSyllable = unvoicedSyllable; } else { transcriptedSyllable = prevSyllableTranscription; } break; } case 'ゞ': case 'ヾ': { const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription) break; if (isUnvoicedSyllable$1(prevSyllableTranscription)) { const voicedSyllable = getVoicedSyllable$1(prevSyllableTranscription); transcriptedSyllable = voicedSyllable; } else { transcriptedSyllable = prevSyllableTranscription; } break; } //polysyllable iteration marks case '〱': { const currentWordTranscription = transcriptedSplitedWord.join(''); if (!currentWordTranscription) break; transcriptedSyllable = currentWordTranscription; break; } case '〲': { const firstSyllabOfTheWordTranscription = transcriptedSplitedWord[0]; const afterFirstSyllabOfTheWordTranscription = transcriptedSplitedWord.slice(1).join(''); if (!firstSyllabOfTheWordTranscription) break; if (isUnvoicedSyllable$1(firstSyllabOfTheWordTranscription)) { const voicedSyllable = getVoicedSyllable$1(firstSyllabOfTheWordTranscription); transcriptedSyllable = voicedSyllable + afterFirstSyllabOfTheWordTranscription; } else { transcriptedSyllable = firstSyllabOfTheWordTranscription + afterFirstSyllabOfTheWordTranscription; } break; } case '〳': { const nextSyllable = splitedWord[index + 1]; const syllabAfterTheNext = splitedWord[index + 2]; //in case there is a splited voicing mark following the iteration mark if (!nextSyllable || (nextSyllable !== '〵' && syllabAfterTheNext !== '〵')) break; const currentWordTranscription = transcriptedSplitedWord.join(''); if (!currentWordTranscription) break; transcriptedSyllable = currentWordTranscription; break; } case '〴': { const nextSyllable = splitedWord[index + 1]; if (!nextSyllable || (nextSyllable !== '〵')) break; const firstSyllabOfTheWordTranscription = transcriptedSplitedWord[0]; const afterFirstSyllabOfTheWordTranscription = transcriptedSplitedWord.slice(1).join(''); if (!firstSyllabOfTheWordTranscription) break; if (isUnvoicedSyllable$1(firstSyllabOfTheWordTranscription)) { const voicedSyllable = getVoicedSyllable$1(firstSyllabOfTheWordTranscription); transcriptedSyllable = voicedSyllable + afterFirstSyllabOfTheWordTranscription; } else { transcriptedSyllable = firstSyllabOfTheWordTranscription + afterFirstSyllabOfTheWordTranscription; } break; } case '/': { const nextSyllable = splitedWord[index + 1]; const syllabAfterTheNext = splitedWord[index + 2]; //in case there is a splited voicing mark following the iteration mark if (!nextSyllable || (nextSyllable !== '\' && syllabAfterTheNext !== '\')) break; const currentWordTranscription = transcriptedSplitedWord.join(''); if (!currentWordTranscription) break; transcriptedSyllable = currentWordTranscription; break; } case '/': { const nextSyllable = splitedWord[index + 1]; const syllabAfterTheNext = splitedWord[index + 2]; //in case there is a splited voicing mark following the iteration mark if (!nextSyllable || (nextSyllable !== '\\' && syllabAfterTheNext !== '\\')) break; const currentWordTranscription = transcriptedSplitedWord.join(''); if (!currentWordTranscription) break; transcriptedSyllable = currentWordTranscription; break; } case '〵': { const prevSyllable = splitedWord[index - 1]; const syllabBeforeThePrev = splitedWord[index - 2]; //in case there is a splited voicing mark following the iteration mark if ((prevSyllable !== '〳' && prevSyllable !== '〴') && syllabBeforeThePrev !== '〳') break; transcriptedSyllable = ''; break; } case '\': { const prevSyllable = splitedWord[index - 1]; const syllabBeforeThePrev = splitedWord[index - 2]; //in case there is a splited voicing mark following the iteration mark if (prevSyllable !== '/' && syllabBeforeThePrev !== '/') break; transcriptedSyllable = ''; break; } case '\\': { const prevSyllable = splitedWord[index - 1]; const syllabBeforeThePrev = splitedWord[index - 2]; //in case there is a splited voicing mark following the iteration mark if (prevSyllable !== '/' && syllabBeforeThePrev !== '/') break; transcriptedSyllable = ''; break; } //digraph marks case 'ゟ': transcriptedSyllable = 'yori'; break; case 'ヿ': transcriptedSyllable = 'koto'; break; //voicing marks case '゛': case '\u3099': { const prevSyllable = splitedWord[index - 1]; const nextSyllable = splitedWord[index + 1]; const isPolysyllableItrationMark = isItPolysyllableItrationMark(prevSyllable, nextSyllable); const prevSyllableTranscription = isPolysyllableItrationMark ? transcriptedSplitedWord[0] : transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription) break; if (isUnvoicedSyllable$1(prevSyllableTranscription)) { const voicedSyllable = getVoicedSyllable$1(prevSyllableTranscription); transcriptedSplitedWord[index - 1] = ''; transcriptedSyllable = isPolysyllableItrationMark ? voicedSyllable + transcriptedSplitedWord.slice(1, -1).join('') : voicedSyllable; } break; } case '゜': case '\u309A': { const prevSyllable = splitedWord[index - 1]; const nextSyllable = splitedWord[index + 1]; const isPolysyllableItrationMark = isItPolysyllableItrationMark(prevSyllable, nextSyllable); const prevSyllableTranscription = isPolysyllableItrationMark ? transcriptedSplitedWord[0] : transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription) break; if (isSemivoicePissible$1(prevSyllableTranscription)) { const voicedSyllable = getSemivoicedSyllable$1(prevSyllableTranscription); transcriptedSplitedWord[index - 1] = ''; transcriptedSyllable = isPolysyllableItrationMark ? voicedSyllable + transcriptedSplitedWord.slice(1, -1).join('') : voicedSyllable; } break; } //squared kana case '㌀': transcriptedSyllable = 'apaato'; break; case '㌁': transcriptedSyllable = system === 'hepburn' ? 'arufa' : 'aruha'; break; case '㌂': transcriptedSyllable = 'anpea'; break; case '㌃': transcriptedSyllable = 'aaru'; break; case '㌄': transcriptedSyllable = 'iningu'; break; case '㌅': transcriptedSyllable = system === 'hepburn' ? 'inchi' : 'inti'; break; case '㌆': transcriptedSyllable = 'uon'; break; case '㌇': transcriptedSyllable = 'esukuudo'; break; case '㌈': transcriptedSyllable = 'eekaa'; break; case '㌉': transcriptedSyllable = 'onsu'; break; case '㌊': transcriptedSyllable = 'oomu'; break; case '㌋': transcriptedSyllable = 'kairi'; break; case '㌌': transcriptedSyllable = 'karatto'; break; case '㌍': transcriptedSyllable = 'karorii'; break; case '㌎': transcriptedSyllable = 'garon'; break; case '㌏': transcriptedSyllable = 'ganma'; break; case '㌐': transcriptedSyllable = 'giga'; break; case '㌑': transcriptedSyllable = 'ginii'; break; case '㌒': transcriptedSyllable = 'kyurii'; break; case '㌓': transcriptedSyllable = 'girudaa'; break; case '㌔': transcriptedSyllable = 'kiro'; break; case '㌕': transcriptedSyllable = 'kiroguramu'; break; case '㌖': transcriptedSyllable = 'kiromeetoru'; break; case '㌗': transcriptedSyllable = 'kirowatto'; break; case '㌘': transcriptedSyllable = 'guramu'; break; case '㌙': transcriptedSyllable = 'guramuton'; break; case '㌚': transcriptedSyllable = 'kuruzeiro'; break; case '㌛': transcriptedSyllable = 'kuroone'; break; case '㌜': transcriptedSyllable = 'keesu'; break; case '㌝': transcriptedSyllable = 'koruna'; break; case '㌞': transcriptedSyllable = 'koopu'; break; case '㌟': transcriptedSyllable = 'saikuru'; break; case '㌠': transcriptedSyllable = system === 'hepburn' ? 'sanchiimu' : 'santiimu'; break; case '㌡': transcriptedSyllable = 'siringu'; break; case '㌢': transcriptedSyllable = system === 'hepburn' ? 'senchi' : 'senti'; break; case '㌣': transcriptedSyllable = 'sento'; break; case '㌤': transcriptedSyllable = 'daasu'; break; case '㌥': transcriptedSyllable = system === 'hepburn' ? 'deshi' : 'desi'; break; case '㌦': transcriptedSyllable = 'doru'; break; case '㌧': transcriptedSyllable = 'ton'; break; case '㌨': transcriptedSyllable = 'nano'; break; case '㌩': transcriptedSyllable = 'notto'; break; case '㌪': transcriptedSyllable = system === 'hepburn' ? 'haitsu' : 'haitu'; break; case '㌫': transcriptedSyllable = 'paasento'; break; case '㌬': transcriptedSyllable = system === 'hepburn' ? 'paatsu' : 'paatu'; break; case '㌭': transcriptedSyllable = 'baareru'; break; case '㌮': transcriptedSyllable = 'piasutoru'; break; case '㌯': transcriptedSyllable = 'pikuru'; break; case '㌰': transcriptedSyllable = 'piko'; break; case '㌱': transcriptedSyllable = 'biru'; break; case '㌲': transcriptedSyllable = system === 'hepburn' ? 'faraddo' : 'haraddo'; break; case '㌳': transcriptedSyllable = system === 'hepburn' ? 'fiito' : 'hiito'; break; case '㌴': transcriptedSyllable = system === 'hepburn' ? 'bussheru' : 'bussyeru'; break; case '㌵': transcriptedSyllable = system === 'hepburn' ? 'furan' : 'huran'; break; case '㌶': transcriptedSyllable = 'hekutaaru'; break; case '㌷': transcriptedSyllable = 'peso'; break; case '㌸': transcriptedSyllable = 'penihi'; break; case '㌹': transcriptedSyllable = system === 'hepburn' ? 'herutsu' : 'herutu'; break; case '㌺': transcriptedSyllable = 'pensu'; break; case '㌻': transcriptedSyllable = system === 'hepburn' ? 'peeji' : 'peezi'; break; case '㌼': transcriptedSyllable = 'beeta'; break; case '㌽': transcriptedSyllable = 'pointo'; break; case '㌾': transcriptedSyllable = 'boruto'; break; case '㌿': transcriptedSyllable = 'hon'; break; case '㍀': transcriptedSyllable = 'pondo'; break; case '㍁': transcriptedSyllable = 'hooru'; break; case '㍂': transcriptedSyllable = 'hoon'; break; case '㍃': transcriptedSyllable = 'maikuro'; break; case '㍄': transcriptedSyllable = 'mairu'; break; case '㍅': transcriptedSyllable = 'mahha'; break; case '㍆': transcriptedSyllable = 'maruku'; break; case '㍇': transcriptedSyllable = system === 'hepburn' ? 'menshon' : 'mensyon'; break; case '㍈': transcriptedSyllable = 'mikuron'; break; case '㍉': transcriptedSyllable = 'miri'; break; case '㍊': transcriptedSyllable = 'miribaaru'; break; case '㍋': transcriptedSyllable = 'mega'; break; case '㍌': transcriptedSyllable = 'megaton'; break; case '㍍': transcriptedSyllable = 'meetoru'; break; case '㍎': transcriptedSyllable = 'yaado'; break; case '㍏': transcriptedSyllable = 'yaaru'; break; case '㍐': transcriptedSyllable = 'yuan'; break; case '㍑': transcriptedSyllable = 'rittoru'; break; case '㍒': transcriptedSyllable = 'rira'; break; case '㍓': transcriptedSyllable = 'rupii'; break; case '㍔': transcriptedSyllable = 'ruuburu'; break; case '㍕': transcriptedSyllable = 'remu'; break; case '㍖': transcriptedSyllable = 'rentogen'; break; case '㍗': transcriptedSyllable = 'watto'; break; } transcriptedSplitedWord.push(transcriptedSyllable); }); const transcriptedWord = transcriptedSplitedWord.join(''); transcriptedWords.push(transcriptedWord); } const transcriptedText = transcriptedWords.join(' '); return transcriptedText; } function hasOnlyVowel$1(str) { const vowelRegExp = /[^aiueoy]/; return !str.match(vowelRegExp); } function getVowels$1(str) { var _a; const vowelRegExp = /[aiueoy]/g; const vowels = (_a = str.match(vowelRegExp)) === null || _a === void 0 ? void 0 : _a.join(''); return vowels; } function hasOnlyConsonants$3(str) { const consonantRegExp = /[^kszgtcfdnhbpmrwjvl]/; return !str.match(consonantRegExp); } function getConsonants$2(str) { var _a; const consonantRegExp = /[kszgtcfdnhbpmrwjvl]/g; const consonants = (_a = str.match(consonantRegExp)) === null || _a === void 0 ? void 0 : _a.join(''); return consonants; } function isUnvoicedSyllable$1(syllable) { return /[kstcfhp]/.test(syllable[0]); } function isVoicedSyllable$1(syllable) { return /[zgdnbmrwjvl]/.test(syllable[0]); } function getUnvoicedSyllable$1(syllable) { const consonants = getConsonants$2(syllable); if (!consonants) return syllable; let unvoicedConsonants = ''; switch (consonants) { case 'g': unvoicedConsonants = 'k'; break; case 'z': unvoicedConsonants = 's'; break; case 'd': unvoicedConsonants = 't'; break; case 'b': unvoicedConsonants = 'h'; break; case 'j': unvoicedConsonants = 'sh'; break; default: unvoicedConsonants = consonants; } const vowels = getVowels$1(syllable); return vowels ? unvoicedConsonants + vowels : unvoicedConsonants; } function getVoicedSyllable$1(syllable) { const consonants = getConsonants$2(syllable); if (!consonants) return syllable; let voicedConsonants = ''; switch (consonants) { case 'k': voicedConsonants = 'g'; break; case 'sh': case 'ch': voicedConsonants = 'j'; break; case 's': case 'ts': voicedConsonants = 'z'; break; case 't': voicedConsonants = 'd'; break; case 'h': case 'p': voicedConsonants = 'b'; break; default: voicedConsonants = consonants; } const vowels = getVowels$1(syllable); return vowels ? voicedConsonants + vowels : voicedConsonants; } function isSemivoicePissible$1(syllable) { return /[hbrk]/.test(syllable[0]); } function getSemivoicedSyllable$1(syllable) { const consonants = getConsonants$2(syllable); if (!consonants) return syllable; let semivoicedConsonants = ''; switch (consonants) { case 'h': case 'b': semivoicedConsonants = 'p'; break; case 'r': semivoicedConsonants = 'l'; break; case 'k': semivoicedConsonants = 'ng'; break; default: semivoicedConsonants = semivoicedConsonants; break; } const vowels = getVowels$1(syllable); return vowels ? semivoicedConsonants + vowels : semivoicedConsonants; } //supported extended kana - https://wikipedia.org/wiki/Katakana#Extended_katakana function fromKanaRU(kanaText, system = 'polivanov') { const splitedSentence = kanaText.toLowerCase().split(spacesRegExp); const isThereOnlyOneWord = splitedSentence.length === 1; const transcriptedWords = []; for (const word of splitedSentence) { const splitedWord = Array.from(word); if (!splitedWord) continue; const isItTheOnlySyllable = splitedWord.length === 1; const transcriptedSplitedWord = []; splitedWord.forEach((kana, index) => { let transcriptedSyllable = kana; switch (kana) { case 'あ': case 'ア': case '㋐': case 'ア': transcriptedSyllable = 'а'; break; case 'い': case 'イ': case '㋑': case 'イ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'й'; } else { transcriptedSyllable = 'и'; } break; } case 'う': case 'ウ': case '㋒': case 'ウ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'в'; } else { transcriptedSyllable = 'у'; } break; } case 'え': case 'エ': case '㋓': case 'エ': transcriptedSyllable = 'э'; break; case 'お': case 'オ': case '㋔': case 'オ': transcriptedSyllable = 'о'; break; case 'か': case 'カ': case '㋕': case 'カ': transcriptedSyllable = 'ка'; break; case 'き': case 'キ': case '㋖': case 'キ': transcriptedSyllable = 'ки'; break; case 'く': case 'ク': case '㋗': case 'ク': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && (nextSyllable === 'ゎ' || nextSyllable === 'ヮ')) { transcriptedSyllable = 'к'; } else if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'кв'; } else { transcriptedSyllable = 'ку'; } break; } case 'け': case 'ケ': case '㋘': case 'ケ': transcriptedSyllable = 'кэ'; break; case 'こ': case 'コ': case '㋙': case 'コ': transcriptedSyllable = 'ко'; break; case 'が': case 'ガ': transcriptedSyllable = 'га'; break; case 'ぎ': case 'ギ': transcriptedSyllable = 'ги'; break; case 'ぐ': case 'グ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && (nextSyllable === 'ゎ' || nextSyllable === 'ヮ')) { transcriptedSyllable = 'г'; } else if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'гв'; } else { transcriptedSyllable = 'гу'; } break; } case 'げ': case 'ゲ': transcriptedSyllable = 'гэ'; break; case 'ご': case 'ゴ': transcriptedSyllable = 'го'; break; case 'さ': case 'サ': case '㋚': case 'サ': transcriptedSyllable = 'са'; break; case 'し': case 'シ': case '㋛': case 'シ': transcriptedSyllable = system === 'nonstandard-ru' ? 'щи' : 'си'; break; case 'す': case 'ス': case '㋜': case 'ス': transcriptedSyllable = 'су'; break; case 'せ': case 'セ': case '㋝': case 'セ': transcriptedSyllable = 'сэ'; break; case 'そ': case 'ソ': case '㋞': case 'ソ': transcriptedSyllable = 'со'; break; case 'ざ': case 'ザ': transcriptedSyllable = 'дза'; break; case 'じ': case 'ジ': transcriptedSyllable = system === 'nonstandard-ru' ? 'джи' : 'дзи'; break; case 'ず': case 'ズ': transcriptedSyllable = 'дзу'; break; case 'ぜ': case 'ゼ': transcriptedSyllable = 'дзэ'; break; case 'ぞ': case 'ゾ': transcriptedSyllable = 'дзо'; break; case 'た': case 'タ': case '㋟': case 'タ': transcriptedSyllable = 'та'; break; case 'ち': case 'チ': case '㋠': case 'チ': transcriptedSyllable = system === 'nonstandard-ru' ? 'чи' : 'ти'; break; case 'つ': case 'ツ': case '㋡': case 'ツ': transcriptedSyllable = 'цу'; break; case 'て': case 'テ': case '㋢': case 'テ': transcriptedSyllable = 'тэ'; break; case 'と': case 'ト': case '㋣': case 'ト': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'тв'; } else { transcriptedSyllable = 'то'; } break; } case 'だ': case 'ダ': transcriptedSyllable = 'да'; break; case 'ぢ': case 'ヂ': transcriptedSyllable = system === 'nonstandard-ru' ? 'джи' : 'дзи'; break; case 'づ': case 'ヅ': transcriptedSyllable = 'дзу'; break; case 'で': case 'デ': transcriptedSyllable = 'дэ'; break; case 'ど': case 'ド': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'дв'; } else { transcriptedSyllable = 'до'; } break; } case 'な': case 'ナ': case '㋤': case 'ナ': transcriptedSyllable = 'на'; break; case 'に': case 'ニ': case '㋥': case 'ニ': transcriptedSyllable = 'ни'; break; case 'ぬ': case 'ヌ': case '㋦': case 'ヌ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'нв'; } else { transcriptedSyllable = 'ну'; } break; } case 'ね': case 'ネ': case '㋧': case 'ネ': transcriptedSyllable = 'нэ'; break; case 'の': case 'ノ': case '㋨': case 'ノ': transcriptedSyllable = 'но'; break; case 'は': case 'ハ': case '㋩': case 'ハ': { if (!isThereOnlyOneWord && system !== 'static-ru') { if (isItTheOnlySyllable) { transcriptedSyllable = 'ва'; break; } else { const nextSyllable = splitedWord[index + 1]; const prevSyllable = splitedWord[index - 1]; if (!isThereKanaAround(prevSyllable, nextSyllable)) { transcriptedSyllable = 'ва'; break; } } } transcriptedSyllable = 'ха'; break; } case 'ひ': case 'ヒ': case '㋪': case 'ヒ': transcriptedSyllable = 'хи'; break; case 'ふ': case 'フ': case '㋫': case 'フ': transcriptedSyllable = 'фу'; break; case 'へ': case 'ヘ': case '㋬': case 'ヘ': { if (!isThereOnlyOneWord && system !== 'static-ru') { if (isItTheOnlySyllable) { transcriptedSyllable = 'э'; break; } else { const nextSyllable = splitedWord[index + 1]; const prevSyllable = splitedWord[index - 1]; if (!isThereKanaAround(prevSyllable, nextSyllable)) { transcriptedSyllable = 'э'; break; } } } transcriptedSyllable = 'хэ'; break; } case 'ほ': case 'ホ': case '㋭': case 'ホ': transcriptedSyllable = 'хо'; break; case 'ば': case 'バ': transcriptedSyllable = 'ба'; break; case 'び': case 'ビ': transcriptedSyllable = 'би'; break; case 'ぶ': case 'ブ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'бв'; } else { transcriptedSyllable = 'бу'; } break; } case 'べ': case 'ベ': transcriptedSyllable = 'бэ'; break; case 'ぼ': case 'ボ': transcriptedSyllable = 'бо'; break; case 'ぱ': case 'パ': transcriptedSyllable = 'па'; break; case 'ぴ': case 'ピ': transcriptedSyllable = 'пи'; break; case 'ぷ': case 'プ': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'пв'; } else { transcriptedSyllable = 'пу'; } break; } case 'ぺ': case 'ペ': transcriptedSyllable = 'пэ'; break; case 'ぽ': case 'ポ': transcriptedSyllable = 'по'; break; case 'ま': case 'マ': case '㋮': case 'マ': transcriptedSyllable = 'ма'; break; case 'み': case 'ミ': case '㋯': case 'ミ': transcriptedSyllable = 'ми'; break; case 'む': case 'ム': case '㋰': case 'ム': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'мв'; } else { transcriptedSyllable = 'му'; } break; } case 'め': case 'メ': case '㋱': case 'メ': transcriptedSyllable = 'мэ'; break; case 'も': case 'モ': case '㋲': case 'モ': transcriptedSyllable = 'мо'; break; case 'や': case 'ヤ': case '㋳': case 'ヤ': transcriptedSyllable = 'я'; break; case 'ゆ': case 'ユ': case '㋴': case 'ユ': transcriptedSyllable = 'ю'; break; case 'よ': case 'ヨ': case '㋵': case 'ヨ': transcriptedSyllable = 'ё'; break; case 'ら': case 'ラ': case '㋶': case 'ラ': transcriptedSyllable = 'ра'; break; case 'り': case 'リ': case '㋷': case 'リ': transcriptedSyllable = 'ри'; break; case 'る': case 'ル': case '㋸': case 'ル': { //для учёта расширенной каны const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { transcriptedSyllable = 'рв'; } else { transcriptedSyllable = 'ру'; } break; } case 'れ': case 'レ': case '㋹': case 'レ': transcriptedSyllable = 'рэ'; break; case 'ろ': case 'ロ': case '㋺': case 'ロ': transcriptedSyllable = 'ро'; break; case 'わ': case 'ワ': case '㋻': transcriptedSyllable = 'ва'; break; case 'を': case 'ヲ': case '㋾': case 'ヲ': { if (!isThereOnlyOneWord && system !== 'static-ru') { if (isItTheOnlySyllable) { transcriptedSyllable = 'о'; break; } else { const nextSyllable = splitedWord[index + 1]; const prevSyllable = splitedWord[index - 1]; if (!isThereKanaAround(prevSyllable, nextSyllable)) { transcriptedSyllable = 'о'; break; } } } transcriptedSyllable = 'во'; break; } case 'ん': case 'ン': { const nextKana = splitedWord[index + 1]; const nextSyllableTranscription = nextKana ? fromKanaRU(nextKana) : undefined; const nextLetter = nextSyllableTranscription ? nextSyllableTranscription[0] : undefined; if (nextLetter && hasOnlyVowel(nextLetter)) { transcriptedSyllable = 'нъ'; break; } if (nextLetter === 'б' || nextLetter === 'п' || nextLetter === 'м') { transcriptedSyllable = 'м'; break; } transcriptedSyllable = 'н'; break; } case 'ゃ': case 'ャ': case 'ャ': { let resultVowel = 'я'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ゅ': case 'ュ': case 'ュ': { let resultVowel = 'ю'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case '𛀁': transcriptedSyllable = 'йэ'; break; case 'ょ': case 'ョ': case 'ョ': { let resultVowel = 'ё'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'っ': case 'ッ': case 'ッ': { transcriptedSyllable = 'цу'; const nextKana = splitedWord[index + 1]; const nextSyllableTranscription = fromKanaRU(nextKana); if (!nextKana || !nextSyllableTranscription) break; const nextLetter = nextSyllableTranscription[0]; if (!hasOnlyConsonants$2(nextLetter)) break; transcriptedSyllable = (nextKana === 'つ' || nextKana === 'ツ') ? 'т' : nextLetter; //По Поливанову っつ пишется как тцу break; } case 'ー': case 'ー': { const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevKana || !prevSyllableTranscription) break; const prevLetter = prevSyllableTranscription.slice(-1); if (!hasOnlyVowel(prevLetter)) break; const vowelForExtending = (prevLetter === 'я' ? 'а' : prevLetter === 'ю' ? 'у' : prevLetter === 'ё' ? 'о' : prevLetter); transcriptedSyllable = vowelForExtending; break; } //расширенная кана (преимущественно катакана) case 'か゚': case 'カ゚': transcriptedSyllable = 'нга'; break; case 'き゚': case 'キ゚': transcriptedSyllable = 'нги'; break; case 'く゚': case 'ク゚': transcriptedSyllable = 'нгу'; break; case 'け゚': case 'ケ゚': transcriptedSyllable = 'нгэ'; break; case 'こ゚': case 'コ゚': transcriptedSyllable = 'нго'; break; case 'ら゚': case 'ラ゚': transcriptedSyllable = 'ла'; break; case 'り゚': case 'リ゚': transcriptedSyllable = 'ли'; break; case 'る゚': case 'ル゚': transcriptedSyllable = 'лу'; break; case 'れ゚': case 'レ゚': transcriptedSyllable = 'лэ'; break; case 'ろ゚': case 'ロ゚': transcriptedSyllable = 'ло'; break; case 'ぁ': case 'ァ': case 'ァ': { let resultVowel = 'а'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ぃ': case 'ィ': case 'ィ': { let resultVowel = 'и'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } const nextSyllable = splitedWord[index + 1]; if (nextSyllable && isItSmallKana(nextSyllable)) { resultVowel = 'й'; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ぅ': case 'ゥ': case 'ゥ': { let resultVowel = 'у'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ぇ': case 'ェ': case 'ェ': { let resultVowel = 'э'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } if (prevKana === 'に' || prevKana === 'ニ' || prevKana === 'ひ' || prevKana === 'ヒ' || prevKana === 'び' || prevKana === 'ビ' || prevKana === 'ぴ' || prevKana === 'ピ' || prevKana === 'み' || prevKana === 'ミ' || prevKana === 'り' || prevKana === 'リ' || prevKana === 'り゚' || prevKana === 'リ゚') resultVowel = 'йэ'; if (system !== 'nonstandard-ru' && (prevKana === 'し' || prevKana === 'シ' || prevKana === 'ち' || prevKana === 'チ' || prevKana === 'じ' || prevKana === 'ジ' || prevKana === 'ぢ' || prevKana === 'ヂ')) resultVowel = 'йэ'; transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ぉ': case 'ォ': case 'ォ': { let resultVowel = 'о'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!prevSyllableConsonants; if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; } transcriptedSyllable = prevSyllableConsonants ? prevSyllableConsonants + resultVowel : resultVowel; break; } case 'ゎ': case 'ヮ': { let resultVowel = 'ва'; const prevKana = splitedWord[index - 1]; const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription || !prevKana || prevKana === 'ん') { transcriptedSyllable = resultVowel; break; } const prevSyllableConsonants = getConsonants$1(prevSyllableTranscription); const needToChangeLastSyllab = !!(prevSyllableConsonants && prevSyllableConsonants.length <= 2); if (needToChangeLastSyllab) { transcriptedSplitedWord[index - 1] = ''; transcriptedSyllable = prevSyllableConsonants + resultVowel; } else { transcriptedSyllable = resultVowel; } break; } case 'ゔ': case 'ヴ': transcriptedSyllable = 'ву'; break; case 'ウ゚': transcriptedSyllable = 'н'; break; case 'ゐ': case 'ヰ': case '㋼': transcriptedSyllable = 'ви'; break; case 'ゑ': case 'ヱ': case '㋽': transcriptedSyllable = 'вэ'; break; case 'わ゙': case 'ヷ': transcriptedSyllable = 'ва'; break; case 'ゐ゙': case 'ヸ': transcriptedSyllable = 'ви'; break; case 'ゑ゙': case 'ヹ': transcriptedSyllable = 'вэ'; break; case 'ヺ': case 'を゙': transcriptedSyllable = 'во'; break; //iteration marks case 'ゝ': case 'ヽ': { const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription) break; if (isVoicedSyllable(prevSyllableTranscription)) { const unvoicedSyllable = getUnvoicedSyllable(prevSyllableTranscription); transcriptedSyllable = unvoicedSyllable; } else { transcriptedSyllable = prevSyllableTranscription; } break; } case 'ゞ': case 'ヾ': { const prevSyllableTranscription = transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription) break; if (isUnvoicedSyllable(prevSyllableTranscription)) { const voicedSyllable = getVoicedSyllable(prevSyllableTranscription); transcriptedSyllable = voicedSyllable; } else { transcriptedSyllable = prevSyllableTranscription; } break; } //polysyllable iteration marks case '〱': { const currentWordTranscription = transcriptedSplitedWord.join(''); if (!currentWordTranscription) break; transcriptedSyllable = currentWordTranscription; break; } case '〲': { const firstSyllabOfTheWordTranscription = transcriptedSplitedWord[0]; const afterFirstSyllabOfTheWordTranscription = transcriptedSplitedWord.slice(1).join(''); if (!firstSyllabOfTheWordTranscription) break; if (isUnvoicedSyllable(firstSyllabOfTheWordTranscription)) { const voicedSyllable = getVoicedSyllable(firstSyllabOfTheWordTranscription); transcriptedSyllable = voicedSyllable + afterFirstSyllabOfTheWordTranscription; } else { transcriptedSyllable = firstSyllabOfTheWordTranscription + afterFirstSyllabOfTheWordTranscription; } break; } case '〳': { const nextSyllable = splitedWord[index + 1]; const syllabAfterTheNext = splitedWord[index + 2]; //in case there is a splited voicing mark following the iteration mark if (!nextSyllable || (nextSyllable !== '〵' && syllabAfterTheNext !== '〵')) break; const currentWordTranscription = transcriptedSplitedWord.join(''); if (!currentWordTranscription) break; transcriptedSyllable = currentWordTranscription; break; } case '〴': { const nextSyllable = splitedWord[index + 1]; if (!nextSyllable || (nextSyllable !== '〵')) break; const firstSyllabOfTheWordTranscription = transcriptedSplitedWord[0]; const afterFirstSyllabOfTheWordTranscription = transcriptedSplitedWord.slice(1).join(''); if (!firstSyllabOfTheWordTranscription) break; if (isUnvoicedSyllable(firstSyllabOfTheWordTranscription)) { const voicedSyllable = getVoicedSyllable(firstSyllabOfTheWordTranscription); transcriptedSyllable = voicedSyllable + afterFirstSyllabOfTheWordTranscription; } else { transcriptedSyllable = firstSyllabOfTheWordTranscription + afterFirstSyllabOfTheWordTranscription; } break; } case '/': { const nextSyllable = splitedWord[index + 1]; const syllabAfterTheNext = splitedWord[index + 2]; //in case there is a splited voicing mark following the iteration mark if (!nextSyllable || (nextSyllable !== '\' && syllabAfterTheNext !== '\')) break; const currentWordTranscription = transcriptedSplitedWord.join(''); if (!currentWordTranscription) break; transcriptedSyllable = currentWordTranscription; break; } case '/': { const nextSyllable = splitedWord[index + 1]; const syllabAfterTheNext = splitedWord[index + 2]; //in case there is a splited voicing mark following the iteration mark if (!nextSyllable || (nextSyllable !== '\\' && syllabAfterTheNext !== '\\')) break; const currentWordTranscription = transcriptedSplitedWord.join(''); if (!currentWordTranscription) break; transcriptedSyllable = currentWordTranscription; break; } case '〵': { const prevSyllable = splitedWord[index - 1]; const syllabBeforeThePrev = splitedWord[index - 2]; //in case there is a splited voicing mark following the iteration mark if ((prevSyllable !== '〳' && prevSyllable !== '〴') && syllabBeforeThePrev !== '〳') break; transcriptedSyllable = ''; break; } case '\': { const prevSyllable = splitedWord[index - 1]; const syllabBeforeThePrev = splitedWord[index - 2]; //in case there is a splited voicing mark following the iteration mark if (prevSyllable !== '/' && syllabBeforeThePrev !== '/') break; transcriptedSyllable = ''; break; } case '\\': { const prevSyllable = splitedWord[index - 1]; const syllabBeforeThePrev = splitedWord[index - 2]; //in case there is a splited voicing mark following the iteration mark if (prevSyllable !== '/' && syllabBeforeThePrev !== '/') break; transcriptedSyllable = ''; break; } //digraph marks case 'ゟ': transcriptedSyllable = 'yori'; break; case 'ヿ': transcriptedSyllable = 'koto'; break; default: break; //voicing marks case '゛': case '\u3099': { const prevSyllable = splitedWord[index - 1]; const nextSyllable = splitedWord[index + 1]; const isPolysyllableItrationMark = isItPolysyllableItrationMark(prevSyllable, nextSyllable); const prevSyllableTranscription = isPolysyllableItrationMark ? transcriptedSplitedWord[0] : transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription) break; if (isUnvoicedSyllable(prevSyllableTranscription)) { const voicedSyllable = getVoicedSyllable(prevSyllableTranscription); transcriptedSplitedWord[index - 1] = ''; transcriptedSyllable = isPolysyllableItrationMark ? voicedSyllable + transcriptedSplitedWord.slice(1, -1).join('') : voicedSyllable; } break; } case '゜': case '\u309A': { const prevSyllable = splitedWord[index - 1]; const nextSyllable = splitedWord[index + 1]; const isPolysyllableItrationMark = isItPolysyllableItrationMark(prevSyllable, nextSyllable); const prevSyllableTranscription = isPolysyllableItrationMark ? transcriptedSplitedWord[0] : transcriptedSplitedWord[index - 1]; if (!prevSyllableTranscription) break; if (isSemivoicePissible(prevSyllableTranscription)) { const voicedSyllable = getSemivoicedSyllable(prevSyllableTranscription); transcriptedSplitedWord[index - 1] = ''; transcriptedSyllable = isPolysyllableItrationMark ? voicedSyllable + transcriptedSplitedWord.slice(1, -1).join('') : voicedSyllable; } break; } //squared kana case '㌀': transcriptedSyllable = 'апаато'; break; case '㌁': transcriptedSyllable = 'аруфа'; break; case '㌂': transcriptedSyllable = 'анпэа'; break; case '㌃': transcriptedSyllable = 'аару'; break; case '㌄': transcriptedSyllable = 'инингу'; break; case '㌅': transcriptedSyllable = system === 'nonstandard-ru' ? 'инчи' : 'инти'; break; case '㌆': transcriptedSyllable = 'уон'; break; case '㌇': transcriptedSyllable = 'эсукуудо'; break; case '㌈': transcriptedSyllable = 'ээкаа'; break; case '㌉': transcriptedSyllable = 'онсу'; break; case '㌊': transcriptedSyllable = 'оому'; break; case '㌋': transcriptedSyllable = 'каири'; break; case '㌌': transcriptedSyllable = 'каратто'; break; case '㌍': transcriptedSyllable = 'карории'; break; case '㌎': transcriptedSyllable = 'гарон'; break; case '㌏': transcriptedSyllable = 'ганма'; break; case '㌐': transcriptedSyllable = 'гига'; break; case '㌑': transcriptedSyllable = 'гинии'; break; case '㌒': transcriptedSyllable = 'кюрии'; break; case '㌓': transcriptedSyllable = 'гирудаа'; break; case '㌔': transcriptedSyllable = 'киро'; break; case '㌕': transcriptedSyllable = 'кирогураму'; break; case '㌖': transcriptedSyllable = 'киромээтору'; break; case '㌗': transcriptedSyllable = 'кироватто'; break; case '㌘': transcriptedSyllable = 'гураму'; break; case '㌙': transcriptedSyllable = 'гурамутон'; break; case '㌚': transcriptedSyllable = 'курудзэиро'; break; case '㌛': transcriptedSyllable = 'куроонэ'; break; case '㌜': transcriptedSyllable = 'кээсу'; break; case '㌝': transcriptedSyllable = 'коруна'; break; case '㌞': transcriptedSyllable = 'коопу'; break; case '㌟': transcriptedSyllable = 'саикуру'; break; case '㌠': transcriptedSyllable = system === 'nonstandard-ru' ? 'санчииму' : 'сантииму'; break; case '㌡': transcriptedSyllable = 'сирингу'; break; case '㌢': transcriptedSyllable = system === 'nonstandard-ru' ? 'сэнчи' : 'сэнти'; break; case '㌣': transcriptedSyllable = 'сэнто'; break; case '㌤': transcriptedSyllable = 'даасу'; break; case '㌥': transcriptedSyllable = system === 'nonstandard-ru' ? 'дэщи' : 'дэси'; break; case '㌦': transcriptedSyllable = 'дору'; break; case '㌧': transcriptedSyllable = 'тон'; break; case '㌨': transcriptedSyllable = 'нано'; break; case '㌩': transcriptedSyllable = 'нотто'; break; case '㌪': transcriptedSyllable = 'хаицу'; break; case '㌫': transcriptedSyllable = 'паасэнто'; break; case '㌬': transcriptedSyllable = 'паацу'; break; case '㌭': transcriptedSyllable = 'баарэру'; break; case '㌮': transcriptedSyllable = 'пиасутору'; break; case '㌯': transcriptedSyllable = 'пикуру'; break; case '㌰': transcriptedSyllable = 'пико'; break; case '㌱': transcriptedSyllable = 'биру'; break; case '㌲': transcriptedSyllable = 'фараддо'; break; case '㌳': transcriptedSyllable = 'фиито'; break; case '㌴': transcriptedSyllable = system === 'nonstandard-ru' ? 'бущщэру' : 'буссйэру'; break; case '㌵': transcriptedSyllable = 'фуран'; break; case '㌶': transcriptedSyllable = 'хэкутаару'; break; case '㌷': transcriptedSyllable = 'пэсо'; break; case '㌸': transcriptedSyllable = 'пэнихи'; break; case '㌹': transcriptedSyllable = 'хэруцу'; break; case '㌺': transcriptedSyllable = 'пэнсу'; break; case '㌻': transcriptedSyllable = system === 'nonstandard-ru' ? 'пээджи' : 'пээдзи'; break; case '㌼': transcriptedSyllable = 'бээта'; break; case '㌽': transcriptedSyllable = 'поинто'; break; case '㌾': transcriptedSyllable = 'боруто'; break; case '㌿': transcriptedSyllable = 'хон'; break; case '㍀': transcriptedSyllable = 'пондо'; break; case '㍁': transcriptedSyllable = 'хоору'; break; case '㍂': transcriptedSyllable = 'хоон'; break; case '㍃': transcriptedSyllable = 'маикуро'; break; case '㍄': transcriptedSyllable = 'маиру'; break; case '㍅': transcriptedSyllable = 'махха'; break; case '㍆': transcriptedSyllable = 'маруку'; break; case '㍇': transcriptedSyllable = system === 'nonstandard-ru' ? 'мэнщён' : 'мэнсён'; break; case '㍈': transcriptedSyllable = 'микурон'; break; case '㍉': transcriptedSyllable = 'мири'; break; case '㍊': transcriptedSyllable = 'мирибаару'; break; case '㍋': transcriptedSyllable = 'мэга'; break; case '㍌': transcriptedSyllable = 'мэгатон'; break; case '㍍': transcriptedSyllable = 'мээтору'; break; case '㍎': transcriptedSyllable = 'яадо'; break; case '㍏': transcriptedSyllable = 'яару'; break; case '㍐': transcriptedSyllable = 'юан'; break; case '㍑': transcriptedSyllable = 'риттору'; break; case '㍒': transcriptedSyllable = 'рира'; break; case '㍓': transcriptedSyllable = 'рупии'; break; case '㍔': transcriptedSyllable = 'руубуру'; break; case '㍕': transcriptedSyllable = 'рэму'; break; case '㍖': transcriptedSyllable = 'рэнтогэн'; break; case '㍗': transcriptedSyllable = 'ватто'; break; } transcriptedSplitedWord.push(transcriptedSyllable); }); const transcriptedWord = transcriptedSplitedWord.join(''); transcriptedWords.push(transcriptedWord); } const transcriptedText = transcriptedWords.join(' '); return transcriptedText; } function hasOnlyVowel(str) { const vowelRegExp = /[^аеёийоуэюя]/; return !str.match(vowelRegExp); } function getVowels(str) { var _a; const vowelRegExp = /[аеёийоуэюя]/g; const vowels = (_a = str.match(vowelRegExp)) === null || _a === void 0 ? void 0 : _a.join(''); return vowels; } function hasOnlyConsonants$2(str) { const consonantRegExp = /[^цкнгшщзхфвпрлджчсмтб]/; return !str.match(consonantRegExp); } function getConsonants$1(str) { var _a; const consonantRegExp = /[цкнгшщзхфвпрлджчсмтб]/g; const consonants = (_a = str.match(consonantRegExp)) === null || _a === void 0 ? void 0 : _a.join(''); return consonants; } function isUnvoicedSyllable(syllable) { return /[цкшщхфпчст]/.test(syllable[0]); } function isVoicedSyllable(syllable) { return /[нгзврлджмб]/.test(syllable[0]); } function getUnvoicedSyllable(syllable) { const consonants = getConsonants$1(syllable); if (!consonants) return syllable; let unvoicedConsonants = ''; switch (consonants) { case 'г': unvoicedConsonants = 'к'; break; case 'дз': case 'дж': case 'щ': unvoicedConsonants = 'с'; break; case 'д': unvoicedConsonants = 'т'; break; case 'б': unvoicedConsonants = 'х'; break; default: unvoicedConsonants = consonants; } const vowels = getVowels(syllable); return vowels ? unvoicedConsonants + vowels : unvoicedConsonants; } function getVoicedSyllable(syllable) { const consonants = getConsonants$1(syllable); if (!consonants) return syllable; let voicedConsonants = ''; switch (consonants) { case 'к': voicedConsonants = 'г'; break; case 'с': case 'ц': voicedConsonants = 'дз'; break; case 'щ': case 'ч': voicedConsonants = 'дж'; break; case 'т': voicedConsonants = 'д'; break; case 'х': case 'п': voicedConsonants = 'б'; break; default: voicedConsonants = consonants; } const vowels = getVowels(syllable); return vowels ? voicedConsonants + vowels : voicedConsonants; } function isSemivoicePissible(syllable) { return /[хбрк]/.test(syllable[0]); } function getSemivoicedSyllable(syllable) { const consonants = getConsonants$1(syllable); if (!consonants) return syllable; let semivoicedConsonants = ''; switch (consonants) { case 'х': case 'б': semivoicedConsonants = 'п'; break; case 'р': semivoicedConsonants = 'л'; break; case 'к': semivoicedConsonants = 'нг'; break; default: semivoicedConsonants = semivoicedConsonants; break; } const vowels = getVowels(syllable); return vowels ? semivoicedConsonants + vowels : semivoicedConsonants; } class MissingParamError extends Error { constructor() { super("There is a missing param: text must be provided."); this.name = 'MissingParamError'; } } class WrongParamValueError extends Error { constructor({ func, param }) { super(`An invalid param was passed. ${param ? `The param '${param}' must be ${param === 'text' ? "the 'String' type" : param === 'toKana' ? "equal to either 'hiragana' or 'katakana'" : param === 'fromLang' || param === 'toLang' ? "equal to either 'en' or 'ru'" : param === 'guess' ? "the 'Boolean' type" : param === 'system' ? "equal to either 'hepburn', 'kunrei-shiki', 'nihon-shiki', 'nonstandard-ru', 'polivanov', or 'static-ru'" : null}.` : `Execute the '${func}' function using one of the following scenarios: ${func === 'toKana' ? ` toKana(text: string, fromLang?: 'en' | 'ru') toKana(text: string, system?: 'hepburn' | 'kunrei-shiki' | 'nihon-shiki' | 'nonstandard-ru' | 'polivanov' | 'static-ru') toKana(text: string, toKana?: 'hiragana' | 'katakana') toKana(text: string, guess?: boolean) toKana(text: string, options?: object with the given above second params)` : func === 'fromKana' ? ` fromKana(kanaText: string, toLang?: 'en' | 'ru') fromKana(kanaText: string, system?: 'hepburn' | 'kunrei-shiki' | 'nihon-shiki' | 'nonstandard-ru' | 'polivanov' | 'static-ru') fromKana(kanaText: string, options?: object with the given above second params)` : func === 'convertKana' ? ` convertKana(text: string, toKana?: 'hiragana' | 'katakana')` : null}.`} `); this.name = "WrongParamValueError"; } } class WrongParamTypeError extends WrongParamValueError { constructor({ func, param }) { super({ func, param }); this.name = 'WrongParamTypeError'; } } class IncompatibleParamsError extends Error { constructor({ system, lang }) { super(`The passed system param incompatible with the language. You can: 1. Don't pass the system param, which will set it to the default value: ${lang === 'en' ? "'hepburn' for English." : lang === 'ru' ? "'polivanov' for Russian." : null} 2. Choose the system to use. For the '${lang}' lang you can pass: ${lang === 'en' ? "'hepburn', 'kunrei-shiki' or 'nihon-shiki'" : lang === 'ru' ? "'polivanov', 'nonstandard-ru' or 'static-ru'" : null}. Were provided: '${lang}' language with '${system}'. `); this.name = "IncompatibleParamsError"; } } const systemsEN = new Set(['hepburn', 'kunrei-shiki', 'nihon-shiki']); const systemsRU = new Set(['nonstandard-ru', 'polivanov', 'static-ru']); const systems = new Set([...systemsEN, ...systemsRU]); const langs = new Set(['en', 'ru']); const kana = new Set(['hiragana', 'katakana']); function fromKanaErrorCheck(kanaText, options) { if (typeof kanaText === 'undefined') { throw new MissingParamError(); } if (typeof kanaText !== 'string') { throw new WrongParamTypeError({ func: 'fromKana', param: 'text' }); } if (typeof options !== 'undefined' && typeof options !== 'string' && typeof options !== 'object') { throw new WrongParamTypeError({ func: 'fromKana' }); } if (typeof options === 'string') { if (!langs.has(options) && !systems.has(options)) { throw new WrongParamValueError({ func: 'fromKana' }); } } if (typeof options === 'object') { if (options.system && typeof options.system !== 'string') { throw new WrongParamTypeError({ func: 'fromKana', param: 'system' }); } if (options.toLang && typeof options.toLang !== 'string') { throw new WrongParamTypeError({ func: 'fromKana', param: 'toLang' }); } if (options.toLang && !langs.has(options.toLang)) { throw new WrongParamValueError({ func: 'fromKana', param: 'toLang' }); } if (options.system && !systems.has(options.system)) { throw new WrongParamValueError({ func: 'fromKana', param: 'system' }); } if ((options.toLang === 'en' && options.system && !systemsEN.has(options.system)) || (options.toLang === 'ru' && options.system && !systemsRU.has(options.system))) { throw new IncompatibleParamsError({ lang: options.toLang, system: options.system }); } } } function toKanaErrorCheck(text, options) { if (typeof text === 'undefined') { throw new MissingParamError(); } if (typeof text !== 'string') { throw new WrongParamTypeError({ func: 'toKana', param: 'text' }); } if (typeof options !== 'undefined' && typeof options !== 'string' && typeof options !== 'object' && typeof options !== 'boolean') { throw new WrongParamTypeError({ func: 'toKana' }); } if (typeof options === 'string') { if (!langs.has(options) && !systems.has(options) && !kana.has(options)) { throw new WrongParamValueError({ func: 'toKana' }); } } if (typeof options === 'object') { if (options.system && typeof options.system !== 'string') { throw new WrongParamTypeError({ func: 'toKana', param: 'system' }); } if (options.fromLang && typeof options.fromLang !== 'string') { throw new WrongParamTypeError({ func: 'toKana', param: 'fromLang' }); } if (options.toKana && typeof options.toKana !== 'string') { throw new WrongParamTypeError({ func: 'toKana', param: 'toKana' }); } if (options.guess && typeof options.guess !== 'boolean') { throw new WrongParamTypeError({ func: 'toKana', param: 'guess' }); } if (options.fromLang && !langs.has(options.fromLang)) { throw new WrongParamValueError({ func: 'toKana', param: 'fromLang' }); } if (options.toKana && !kana.has(options.toKana)) { throw new WrongParamValueError({ func: 'toKana', param: 'toKana' }); } if (options.system && !systems.has(options.system)) { throw new WrongParamValueError({ func: 'toKana', param: 'system' }); } if ((options.fromLang === 'en' && options.system && !systemsEN.has(options.system)) || (options.fromLang === 'ru' && options.system && !systemsRU.has(options.system))) { throw new IncompatibleParamsError({ lang: options.fromLang, system: options.system }); } } } function convertKanaErrorCheck(kanaText, toKana) { if (typeof kanaText === 'undefined') { throw new MissingParamError(); } if (typeof kanaText !== 'string') { throw new WrongParamTypeError({ func: 'convertKana', param: 'text' }); } if (toKana) { if (typeof kanaText !== 'string') { throw new WrongParamTypeError({ func: 'convertKana', param: 'toKana' }); } if (!kana.has(toKana)) { throw new WrongParamValueError({ func: 'convertKana', param: 'toKana' }); } } } /** * Converts the kana given in a text to one of the Japanese alphabet * * @param text A text where the kana is used * @param toKana A kana to convert to * * @returns The converted text * * @see {@link https://github.com/18degrees/kana-transcription/blob/main/docs/en/functions.md#convertKana|Functions overview} */ function convertKana(text, toKana = 'hiragana') { convertKanaErrorCheck(text, toKana); const syllabaryToChangeRegExp = toKana === 'hiragana' ? /\p{Script=Kana}{1}|ー/ug : /\p{Script=Hira}{1}/ug; const splitedWords = text.toLowerCase().split(spacesRegExp); let kanaTransformedArr = []; for (const word of splitedWords) { const textSplited = word.split(''); const transformedkanaSplited = textSplited.map((kana, index) => { const isTransformNeeded = kana.match(syllabaryToChangeRegExp); if (!isTransformNeeded) return kana; switch (kana) { case 'あ': case 'ア': return toKana === 'hiragana' ? 'あ' : 'ア'; case 'い': case 'イ': return toKana === 'hiragana' ? 'い' : 'イ'; case 'う': case 'ウ': return toKana === 'hiragana' ? 'う' : 'ウ'; case 'え': case 'エ': return toKana === 'hiragana' ? 'え' : 'エ'; case 'お': case 'オ': return toKana === 'hiragana' ? 'お' : 'オ'; case 'か': case 'カ': return toKana === 'hiragana' ? 'か' : 'カ'; case 'き': case 'キ': return toKana === 'hiragana' ? 'き' : 'キ'; case 'く': case 'ク': return toKana === 'hiragana' ? 'く' : 'ク'; case 'け': case 'ケ': return toKana === 'hiragana' ? 'け' : 'ケ'; case 'こ': case 'コ': return toKana === 'hiragana' ? 'こ' : 'コ'; case 'が': case 'ガ': return toKana === 'hiragana' ? 'が' : 'ガ'; case 'ぎ': case 'ギ': return toKana === 'hiragana' ? 'ぎ' : 'ギ'; case 'ぐ': case 'グ': return toKana === 'hiragana' ? 'ぐ' : 'グ'; case 'げ': case 'ゲ': return toKana === 'hiragana' ? 'げ' : 'ゲ'; case 'ご': case 'ゴ': return toKana === 'hiragana' ? 'ご' : 'ゴ'; case 'さ': case 'サ': return toKana === 'hiragana' ? 'さ' : 'サ'; case 'し': case 'シ': return toKana === 'hiragana' ? 'し' : 'シ'; case 'す': case 'ス': return toKana === 'hiragana' ? 'す' : 'ス'; case 'せ': case 'セ': return toKana === 'hiragana' ? 'せ' : 'セ'; case 'そ': case 'ソ': return toKana === 'hiragana' ? 'そ' : 'ソ'; case 'ざ': case 'ザ': return toKana === 'hiragana' ? 'ざ' : 'ザ'; case 'じ': case 'ジ': return toKana === 'hiragana' ? 'じ' : 'ジ'; case 'ず': case 'ズ': return toKana === 'hiragana' ? 'ず' : 'ズ'; case 'ぜ': case 'ゼ': return toKana === 'hiragana' ? 'ぜ' : 'ゼ'; case 'ぞ': case 'ゾ': return toKana === 'hiragana' ? 'ぞ' : 'ゾ'; case 'た': case 'タ': return toKana === 'hiragana' ? 'た' : 'タ'; case 'ち': case 'チ': return toKana === 'hiragana' ? 'ち' : 'チ'; case 'つ': case 'ツ': return toKana === 'hiragana' ? 'つ' : 'ツ'; case 'て': case 'テ': return toKana === 'hiragana' ? 'て' : 'テ'; case 'と': case 'ト': return toKana === 'hiragana' ? 'と' : 'ト'; case 'だ': case 'ダ': return toKana === 'hiragana' ? 'だ' : 'ダ'; case 'ぢ': case 'ヂ': return toKana === 'hiragana' ? 'ぢ' : 'ヂ'; case 'ぢ': case 'ヂ': return toKana === 'hiragana' ? 'ぢ' : 'ヂ'; case 'づ': case 'ヅ': return toKana === 'hiragana' ? 'づ' : 'ヅ'; case 'で': case 'デ': return toKana === 'hiragana' ? 'で' : 'デ'; case 'ど': case 'ド': return toKana === 'hiragana' ? 'ど' : 'ド'; case 'な': case 'ナ': return toKana === 'hiragana' ? 'な' : 'ナ'; case 'に': case 'ニ': return toKana === 'hiragana' ? 'に' : 'ニ'; case 'ぬ': case 'ヌ': return toKana === 'hiragana' ? 'ぬ' : 'ヌ'; case 'ね': case 'ネ': return toKana === 'hiragana' ? 'ね' : 'ネ'; case 'の': case 'ノ': return toKana === 'hiragana' ? 'の' : 'ノ'; case 'は': case 'ハ': return toKana === 'hiragana' ? 'は' : 'ハ'; case 'ひ': case 'ヒ': return toKana === 'hiragana' ? 'ひ' : 'ヒ'; case 'ふ': case 'フ': return toKana === 'hiragana' ? 'ふ' : 'フ'; case 'へ': case 'ヘ': return toKana === 'hiragana' ? 'へ' : 'ヘ'; case 'ほ': case 'ホ': return toKana === 'hiragana' ? 'ほ' : 'ホ'; case 'ば': case 'バ': return toKana === 'hiragana' ? 'ば' : 'バ'; case 'び': case 'ビ': return toKana === 'hiragana' ? 'び' : 'ビ'; case 'ぶ': case 'ブ': return toKana === 'hiragana' ? 'ぶ' : 'ブ'; case 'べ': case 'ベ': return toKana === 'hiragana' ? 'べ' : 'ベ'; case 'ぼ': case 'ボ': return toKana === 'hiragana' ? 'ぼ' : 'ボ'; case 'ぱ': case 'パ': return toKana === 'hiragana' ? 'ぱ' : 'パ'; case 'ぴ': case 'ピ': return toKana === 'hiragana' ? 'ぴ' : 'ピ'; case 'ぷ': case 'プ': return toKana === 'hiragana' ? 'ぷ' : 'プ'; case 'ぺ': case 'ペ': return toKana === 'hiragana' ? 'ぺ' : 'ペ'; case 'ぽ': case 'ポ': return toKana === 'hiragana' ? 'ぽ' : 'ポ'; case 'ま': case 'マ': return toKana === 'hiragana' ? 'ま' : 'マ'; case 'み': case 'ミ': return toKana === 'hiragana' ? 'み' : 'ミ'; case 'む': case 'ム': return toKana === 'hiragana' ? 'む' : 'ム'; case 'め': case 'メ': return toKana === 'hiragana' ? 'め' : 'メ'; case 'も': case 'モ': return toKana === 'hiragana' ? 'も' : 'モ'; case 'や': case 'ヤ': return toKana === 'hiragana' ? 'や' : 'ヤ'; case 'ゆ': case 'ユ': return toKana === 'hiragana' ? 'ゆ' : 'ユ'; case 'よ': case 'ヨ': return toKana === 'hiragana' ? 'よ' : 'ヨ'; case 'ら': case 'ラ': return toKana === 'hiragana' ? 'ら' : 'ラ'; case 'り': case 'リ': return toKana === 'hiragana' ? 'り' : 'リ'; case 'る': case 'ル': return toKana === 'hiragana' ? 'る' : 'ル'; case 'れ': case 'レ': return toKana === 'hiragana' ? 'れ' : 'レ'; case 'ろ': case 'ロ': return toKana === 'hiragana' ? 'ろ' : 'ロ'; case 'わ': case 'ワ': return toKana === 'hiragana' ? 'わ' : 'ワ'; case 'を': case 'ヲ': return toKana === 'hiragana' ? 'を' : 'ヲ'; case 'ん': case 'ン': return toKana === 'hiragana' ? 'ん' : 'ン'; case 'ゃ': case 'ャ': return toKana === 'hiragana' ? 'ゃ' : 'ャ'; case 'ゅ': case 'ュ': return toKana === 'hiragana' ? 'ゅ' : 'ュ'; case 'ょ': case 'ョ': return toKana === 'hiragana' ? 'ょ' : 'ョ'; case 'っ': case 'ッ': return toKana === 'hiragana' ? 'っ' : 'ッ'; case 'ー': if (toKana === 'katakana') return ''; const prevKana = textSplited[index - 1]; const prevSyllableTranscription = fromKanaEN(prevKana); if (!prevSyllableTranscription) return ''; const prevLetter = prevSyllableTranscription.slice(-1); const prevKanaLetter = getKanaVowel(prevLetter); return prevKanaLetter; // расширенная кана case 'ぁ': case 'ァ': return toKana === 'hiragana' ? 'ぁ' : 'ァ'; case 'ぃ': case 'ィ': return toKana === 'hiragana' ? 'ぃ' : 'ィ'; case 'ぅ': case 'ゥ': return toKana === 'hiragana' ? 'ぅ' : 'ゥ'; case 'ぇ': case 'ェ': return toKana === 'hiragana' ? 'ぇ' : 'ェ'; case 'ぉ': case 'ォ': return toKana === 'hiragana' ? 'ぉ' : 'ォ'; case 'ゎ': case 'ヮ': return toKana === 'hiragana' ? 'ゎ' : 'ヮ'; case 'ゕ': case 'ヵ': return toKana === 'hiragana' ? 'ゕ' : 'ヵ'; case 'ゖ': case 'ヶ': return toKana === 'hiragana' ? 'ゖ' : 'ヶ'; case 'ゔ': case 'ヴ': return toKana === 'hiragana' ? 'ゔ' : 'ヴ'; case 'か゚': case 'カ゚': return toKana === 'hiragana' ? 'か゚' : 'カ゚'; case 'き゚': case 'キ゚': return toKana === 'hiragana' ? 'き゚' : 'キ゚'; case 'く゚': case 'ク゚': return toKana === 'hiragana' ? 'く゚' : 'ク゚'; case 'け゚': case 'ケ゚': return toKana === 'hiragana' ? 'け゚' : 'ケ゚'; case 'こ゚': case 'コ゚': return toKana === 'hiragana' ? 'こ゚' : 'コ゚'; case 'ら゚': case 'ラ゚': return toKana === 'hiragana' ? 'ら゚' : 'ラ゚'; case 'り゚': case 'リ゚': return toKana === 'hiragana' ? 'り゚' : 'リ゚'; case 'る゚': case 'ル゚': return toKana === 'hiragana' ? 'る゚' : 'ル゚'; case 'れ゚': case 'レ゚': return toKana === 'hiragana' ? 'れ゚' : 'レ゚'; case 'ろ゚': case 'ロ゚': return toKana === 'hiragana' ? 'ろ゚' : 'ロ゚'; case 'わ゙': case 'ヷ': return toKana === 'hiragana' ? 'わ゙' : 'ヷ'; case 'ゐ': case 'ヰ': return toKana === 'hiragana' ? 'ゐ' : 'ヰ'; case 'ゐ゙': case 'ヸ': return toKana === 'hiragana' ? 'ゐ゙' : 'ヸ'; case 'ゑ': case 'ヱ': return toKana === 'hiragana' ? 'ゑ' : 'ヱ'; case 'ゑ゙': case 'ヹ': return toKana === 'hiragana' ? 'ゑ゙' : 'ヹ'; case 'を゙': case 'ヺ': return toKana === 'hiragana' ? 'を゙' : 'ヺ'; default: return kana; } }); const transformedKanaWord = transformedkanaSplited.join(''); kanaTransformedArr.push(transformedKanaWord); } const kanaTransformedString = kanaTransformedArr.join(' '); return kanaTransformedString; } function getKanaVowel(syllable) { switch (syllable) { case 'a': return 'あ'; case 'i': return 'い'; case 'u': return 'う'; case 'e': return 'え'; case 'o': return 'お'; default: return ''; } } function toKanaEN(text, options) { const { toKana = 'hiragana', system = 'hepburn', guess = false } = options ? options : {}; const splitRegExp = (guess ? /[kszgtfdnhbpmrwjvl]?y?[aiueo]|ts?y?[aiueo]?|ch[aiueo]?|sh[aiueo]?|n'|./g : /[kszgtfdnhbpmrwjvl]?w?y?[aiueo]|ts?y?[aiueo]?|ch[aiueo]?|sh[aiueo]?|n'|./g); const splitedSentence = text.toLowerCase().split(spacesRegExp); const isThereOnlyOneWord = splitedSentence.length === 1; const hiraganaWords = []; for (const word of splitedSentence) { const splitedWord = word.match(splitRegExp); if (!splitedWord) continue; const isItTheOnlySyllable = splitedWord.length === 1; let hiraganaWord = ''; splitedWord.forEach(((syllable, index) => { const prevSyllable = splitedWord[index - 1]; let kana = syllable; switch (syllable) { case 'a': kana = 'あ'; break; case 'i': kana = 'い'; break; case 'u': kana = 'う'; break; case 'e': kana = (system !== 'nihon-shiki' && isItTheOnlySyllable && !isThereOnlyOneWord) ? 'へ' : 'え'; //также может быть 絵 break; case 'o': kana = (system !== 'nihon-shiki' && isItTheOnlySyllable && !isThereOnlyOneWord) ? 'を' : 'お'; //если длина слова 1, и единственный звук в нём - [о] - видимо, речь идёт об を break; case 'ka': kana = 'か'; break; case 'ki': kana = 'き'; break; case 'ku': kana = 'く'; break; case 'ke': kana = 'け'; break; case 'ko': kana = 'こ'; break; case 'ga': kana = 'が'; break; case 'gi': kana = 'ぎ'; break; case 'gu': kana = 'ぐ'; break; case 'ge': kana = 'げ'; break; case 'go': kana = 'ご'; break; case 'sa': kana = 'さ'; break; case 'shi': if (system === 'hepburn') kana = 'し'; break; case 'su': kana = 'す'; break; case 'se': kana = 'せ'; break; case 'so': kana = 'そ'; break; case 'za': kana = 'ざ'; break; case 'ji': if (system === 'hepburn') kana = prevSyllable && prevSyllable === 'chi' ? 'ぢ' : 'じ'; break; case 'zu': kana = prevSyllable && ((system === 'hepburn' && prevSyllable === 'tsu') || (system !== 'hepburn' && prevSyllable === 'tu')) ? 'づ' : 'ず'; break; case 'ze': kana = 'ぜ'; break; case 'zo': kana = 'ぞ'; break; case 'ta': kana = 'た'; break; case 'chi': if (system === 'hepburn') kana = 'ち'; break; case 'tsu': if (system === 'hepburn') kana = 'つ'; break; case 'te': kana = 'て'; break; case 'to': kana = 'と'; break; case 'da': kana = 'だ'; break; case 'de': kana = 'で'; break; case 'do': kana = 'ど'; break; case 'na': kana = 'な'; break; case 'ni': kana = 'に'; break; case 'nu': kana = 'ぬ'; break; case 'ne': kana = 'ね'; break; case 'no': kana = 'の'; break; case 'ha': kana = 'は'; break; case 'hi': kana = 'ひ'; break; case 'fu': if (system === 'hepburn') kana = 'ふ'; break; case 'he': kana = 'へ'; break; case 'ho': kana = 'ほ'; break; case 'ba': kana = 'ば'; break; case 'bi': kana = 'び'; break; case 'bu': kana = 'ぶ'; break; case 'be': kana = 'べ'; break; case 'bo': kana = 'ぼ'; break; case 'pa': kana = 'ぱ'; break; case 'pi': kana = 'ぴ'; break; case 'pu': kana = 'ぷ'; break; case 'pe': kana = 'ぺ'; break; case 'po': kana = 'ぽ'; break; case 'ma': kana = 'ま'; break; case 'mi': kana = 'み'; break; case 'mu': kana = 'む'; break; case 'me': kana = 'め'; break; case 'mo': kana = 'も'; break; case 'ya': kana = 'や'; break; case 'yu': kana = 'ゆ'; break; case 'yo': kana = 'よ'; break; case 'ra': kana = 'ら'; break; case 'ri': kana = 'り'; break; case 'ru': kana = 'る'; break; case 're': kana = 'れ'; break; case 'ro': kana = 'ろ'; break; case 'wa': kana = (system !== 'nihon-shiki' && isItTheOnlySyllable && !isThereOnlyOneWord) ? 'は' : 'わ'; break; case 'wo': kana = 'を'; break; case 'n': case "n'": //перед гласными такая запись обязательна - иначе не отличить от ряда "на" case 'm': kana = 'ん'; break; case 'kya': kana = 'きゃ'; break; case 'кyu': kana = 'きゅ'; break; case 'kyo': kana = 'きょ'; break; case 'sha': if (system === 'hepburn') kana = 'しゃ'; break; case 'shu': if (system === 'hepburn') kana = 'しゅ'; break; case 'sho': if (system === 'hepburn') kana = 'しょ'; break; case 'cha': if (system === 'hepburn') kana = 'ちゃ'; break; case 'chu': if (system === 'hepburn') kana = 'ちゅ'; break; case 'cho': if (system === 'hepburn') kana = 'ちょ'; break; case 'nya': kana = 'にゃ'; break; case 'nyu': kana = 'にゅ'; break; case 'nyo': kana = 'にょ'; break; case 'hya': kana = 'ひゃ'; break; case 'hyu': kana = 'ひゅ'; break; case 'hyo': kana = 'ひょ'; break; case 'mya': kana = 'みゃ'; break; case 'myu': kana = 'みゅ'; break; case 'myo': kana = 'みょ'; break; case 'rya': kana = 'りゃ'; break; case 'ryu': kana = 'りゅ'; break; case 'ryo': kana = 'りょ'; break; case 'gya': kana = 'ぎゃ'; break; case 'gyu': kana = 'ぎゅ'; break; case 'gyo': kana = 'ぎょ'; break; case 'ja': if (system === 'hepburn') kana = 'じゃ'; break; case 'ju': if (system === 'hepburn') kana = 'じゅ'; break; case 'jo': if (system === 'hepburn') kana = 'じょ'; break; case 'bya': kana = 'びゃ'; break; case 'byu': kana = 'びゅ'; break; case 'byo': kana = 'びょ'; break; case 'pya': kana = 'ぴゃ'; break; case 'pyu': kana = 'ぴゅ'; break; case 'pyo': kana = 'ぴょ'; break; //расширенная кана и системы не по Хёпберну case 'yi': kana = 'いぃ'; break; case 'ye': kana = 'いぇ'; break; case 'wi': kana = 'うぃ'; break; case 'wu': kana = 'うぅ'; break; case 'we': kana = 'うぇ'; break; case 'wya': kana = 'うゃ'; break; case 'wyu': kana = 'うゅ'; break; case 'wye': kana = 'うぃぇ'; break; case 'wyo': kana = 'うょ'; break; case 'va': kana = 'ゔぁ'; break; case 'vi': kana = 'ゔぃ'; break; case 'vu': kana = 'ゔ'; break; case 've': kana = 'ゔぇ'; break; case 'vo': kana = 'ゔぉ'; break; case 'vya': kana = 'ゔゃ'; break; case 'vyu': kana = 'ゔゅ'; break; case 'vye': kana = 'ゔぃぇ'; break; case 'vyo': kana = 'ゔょ'; break; case 'kye': kana = 'きぇ'; break; case 'gye': kana = 'ぎぇ'; break; case 'kwa': kana = 'くぁ'; break; case 'kwi': kana = 'くぃ'; break; case 'kwe': kana = 'くぇ'; break; case 'kwo': kana = 'くぉ'; break; case 'gwa': kana = 'ぐぁ'; break; case 'gwi': kana = 'ぐぃ'; break; case 'gwe': kana = 'ぐぇ'; break; case 'gwo': kana = 'ぐぉ'; break; case 'sya': if (system !== 'hepburn') kana = 'しゃ'; break; case 'syu': if (system !== 'hepburn') kana = 'しゅ'; break; case 'syo': if (system !== 'hepburn') kana = 'しょ'; break; case 'she': if (system === 'hepburn') kana = 'しぇ'; break; case 'je': if (system === 'hepburn') kana = 'じぇ'; break; case 'si': kana = system === 'hepburn' ? 'すぃ' : 'し'; break; case 'che': if (system === 'hepburn') kana = 'ちぇ'; break; case 'tya': if (system !== 'hepburn') kana = 'ちゃ'; break; case 'tyu': if (system !== 'hepburn') kana = 'ちゅ'; break; case 'tyo': if (system !== 'hepburn') kana = 'ちょ'; break; case 'tsa': if (system === 'hepburn') kana = 'つぁ'; break; case 'tsi': if (system === 'hepburn') kana = 'つぃ'; break; case 'tse': if (system === 'hepburn') kana = 'つぇ'; break; case 'tso': if (system === 'hepburn') kana = 'つぉ'; break; case 'tsya': if (system === 'hepburn') kana = 'つゃ'; break; case 'tsyu': if (system === 'hepburn') kana = 'つゅ'; break; case 'tsye': if (system === 'hepburn') kana = 'つぃぇ'; break; case 'tsyo': if (system === 'hepburn') kana = 'つョ'; break; case 'zi': if (system === 'hepburn') { kana = 'づぃ'; } else { kana = prevSyllable && prevSyllable === 'ti' ? 'ぢ' : 'じ'; } break; case 'zya': kana = system === 'hepburn' ? 'づゃ' : 'じゃ'; break; case 'zyu': kana = system === 'hepburn' ? 'づゅ' : 'じゅ'; break; case 'zye': kana = 'づぃぇ'; break; case 'zyo': kana = system === 'hepburn' ? 'づョ' : 'じョ'; break; case 'ti': kana = system === 'hepburn' ? 'てぃ' : 'ち'; break; case 'tu': kana = system === 'hepburn' ? 'とぅ' : 'つ'; break; case 'twi': kana = 'とぃ'; break; case 'tyu': kana = system === 'hepburn' ? kana = 'てゅ' : 'ちゅ'; break; case 'di': kana = 'でぃ'; break; case 'du': kana = 'どぅ'; break; case 'dwi': kana = 'どぃ'; break; case 'dyu': kana = 'でゅ'; break; case 'nwi': kana = 'ぬぃ'; break; case 'nye': kana = 'にぇ'; break; case 'hyu': kana = 'ひぇ'; break; case 'bwi': kana = 'ぶぃ'; break; case 'bye': kana = 'びぇ'; break; case 'pwi': kana = 'ぷぃ'; break; case 'pye': kana = 'ぴぇ'; break; case 'fa': if (system === 'hepburn') kana = 'ふぁ'; break; case 'fi': if (system === 'hepburn') kana = 'ふぃ'; break; case 'fe': if (system === 'hepburn') kana = 'ふぇ'; break; case 'fo': if (system === 'hepburn') kana = 'ふぉ'; break; case 'fya': if (system === 'hepburn') kana = 'ふゃ'; break; case 'fyu': if (system === 'hepburn') kana = 'ふゅ'; break; case 'fye': if (system === 'hepburn') kana = 'ふぃぇ'; break; case 'fyo': if (system === 'hepburn') kana = 'ふょ'; break; case 'hu': kana = system === 'hepburn' ? 'ほぅ' : 'ふ'; break; case 'mwi': kana = 'むぃ'; break; case 'mye': kana = 'みぇ'; break; case 'rwi': kana = 'るぃ'; break; case 'rye': kana = 'りぇ'; break; case 'la': kana = 'ら゚'; break; case 'li': kana = 'り゚'; break; case 'lu': kana = 'る゚'; break; case 'le': kana = 'れ゚'; break; case 'lo': kana = 'ろ゚'; break; case 'lya': kana = 'り゚ゃ'; break; case 'lyu': kana = 'り゚ゅ'; break; case 'lye': kana = 'り゚ぇ'; break; case 'lyo': kana = 'り゚ょ'; break; } const nextSyllable = splitedWord[index + 1]; const followingLetter = nextSyllable ? nextSyllable[0] : undefined; const currentSyllableLettersAmount = syllable.length; if (isItLongConsonant()) kana = 'っ'; if (guess && isItDevoicedVowel()) { let correctedSyllable = syllable; if (isItDevoicedI()) { correctedSyllable += 'i'; } else { correctedSyllable += 'u'; //так как в английском нет мягкого знака, отличить неслышную "u" от "i" не получается } const correctedKana = toKanaEN(correctedSyllable, options); kana = correctedKana ? correctedKana : ''; } hiraganaWord += kana; function isItLongConsonant() { if (!nextSyllable || !followingLetter) return false; if (currentSyllableLettersAmount !== 1) return false; const consideringLetter = syllable; if (!hasOnlyConsonants$1(consideringLetter)) return false; if (consideringLetter === 'n' || consideringLetter === 'm') return false; if (!hasOnlyConsonants$1(followingLetter)) return false; if (consideringLetter === 't' && nextSyllable.slice(0, 2) === 'ch') return true; //っち по системе Хепбёрна пишется как tchi if (consideringLetter !== followingLetter) return false; return true; } function isItDevoicedVowel() { if (!hasOnlyConsonants$1(syllable)) return false; const consonants = getConsonants(syllable); if (!consonants) return false; if (consonants.length > 2) return false; if (consonants.length === 2 && !(consonants === 'ts' || consonants === 'sh' || consonants === 'ch')) return false; if (!isItVoicelessConsonants(consonants)) return false; const nextSyllableConsonants = nextSyllable ? getConsonants(nextSyllable) : undefined; if (nextSyllableConsonants && !isItVoicelessConsonants(nextSyllableConsonants)) return false; if (consonants.length === 1 && consonants === followingLetter) return false; //Вынужденная мера, иначе каждый случай идущих двух подряд согласных будет интерпретироваться и как неслышный гласный, и как двойной согласный одовременно if (consonants === 't' && nextSyllableConsonants === 'ch') return false; return true; } function isItDevoicedI() { if (syllable === 'sh' || syllable === 'ch' || syllable === 'j') return true; //эти звуки не употребляются с /u/ return false; } })); hiraganaWords.push(hiraganaWord); } const hiraganaText = hiraganaWords.join(' '); const kanaText = toKana === 'hiragana' ? hiraganaText : convertKana(hiraganaText, "katakana"); return kanaText; } function isItVoicelessConsonants(letters) { const voicelessConsonantRegExp = /ch|sh|ts|[fkpst]/; return letters.match(voicelessConsonantRegExp); } function hasOnlyConsonants$1(str) { const nonConsonantRegExp = /[^ksztcgfdnhbpmrwjvl]/; return !str.match(nonConsonantRegExp); } function getConsonants(str) { var _a; const consonantRegExp = /[ksztcgfdnhbpmrwjvl]/g; const consonants = (_a = str.match(consonantRegExp)) === null || _a === void 0 ? void 0 : _a.join(''); return consonants; } function toKanaRU(text, options) { const { toKana = 'hiragana', system = 'polivanov', guess = false } = options ? options : {}; const splitRegExp = (guess ? /[цкнгшщзхфвпрлджчсмтб]?й?[аеёийоуэюя]|дз[аеёийоуэюя]|дж[аеёийоуэюя]|нъ|[цкнгшщзхфвпрлджчсмтб]ь|./g : /[цкнгшщзхфвпрлджчсмтб]?в?й?[аеёийоуэюя]|дз[аеёийоуэюя]|дж[аеёийоуэюя]|нъ|[цкнгшщзхфвпрлджчсмтб]ь|./g); const splitedSentence = text.toLowerCase().split(spacesRegExp); const isThereOnlyOneWord = splitedSentence.length === 1; const hiraganaWords = []; for (const word of splitedSentence) { const splitedWord = word.match(splitRegExp); if (!splitedWord) continue; const isItTheOnlySyllable = splitedWord.length === 1; let hiraganaWord = ''; splitedWord.forEach(((syllable, index) => { const prevSyllable = splitedWord[index - 1]; let kana = syllable; switch (syllable) { case 'а': kana = 'あ'; break; case 'и': case 'й': kana = 'い'; break; case 'у': kana = 'う'; break; case 'э': kana = (system !== 'static-ru' && isItTheOnlySyllable && !isThereOnlyOneWord) ? 'へ' : 'え'; //также может быть 絵 break; case 'о': kana = (system !== 'static-ru' && isItTheOnlySyllable && !isThereOnlyOneWord) ? 'を' : 'お'; //если длина слова 1, и единственный звук в нём - [о] - видимо, речь идёт об を break; case 'ка': kana = 'か'; break; case 'ки': kana = 'き'; break; case 'ку': kana = 'く'; break; case 'кэ': kana = 'け'; break; case 'ко': kana = 'こ'; break; case 'га': kana = 'が'; break; case 'ги': kana = 'ぎ'; break; case 'гу': kana = 'ぐ'; break; case 'гэ': kana = 'げ'; break; case 'го': kana = 'ご'; break; case 'са': kana = 'さ'; break; case 'си': kana = system !== 'nonstandard-ru' ? 'し' : 'すぃ'; break; case 'су': kana = 'す'; break; case 'сэ': kana = 'せ'; break; case 'со': kana = 'そ'; break; case 'дза': kana = 'ざ'; break; case 'дзи': if (system !== 'nonstandard-ru') { kana = prevSyllable && prevSyllable === 'ти' ? 'ぢ' : 'じ'; } else { kana = 'づぃ'; } break; case 'дзу': kana = prevSyllable && prevSyllable === 'цу' ? 'づ' : 'ず'; break; case 'дзэ': kana = 'ぜ'; break; case 'дзо': kana = 'ぞ'; break; case 'та': kana = 'た'; break; case 'ти': kana = system !== 'nonstandard-ru' ? 'ち' : 'てぃ'; break; case 'цу': kana = 'つ'; break; case 'тэ': kana = 'て'; break; case 'то': kana = 'と'; break; case 'да': kana = 'だ'; break; case 'дэ': kana = 'で'; break; case 'до': kana = 'ど'; break; case 'на': kana = 'な'; break; case 'ни': kana = 'に'; break; case 'ну': kana = 'ぬ'; break; case 'нэ': kana = 'ね'; break; case 'но': kana = 'の'; break; case 'ха': kana = 'は'; break; case 'хи': kana = 'ひ'; break; case 'фу': kana = 'ふ'; break; case 'хэ': kana = 'へ'; break; case 'хо': kana = 'ほ'; break; case 'ба': kana = 'ば'; break; case 'би': kana = 'び'; break; case 'бу': kana = 'ぶ'; break; case 'бэ': kana = 'べ'; break; case 'бо': kana = 'ぼ'; break; case 'па': kana = 'ぱ'; break; case 'пи': kana = 'ぴ'; break; case 'пу': kana = 'ぷ'; break; case 'пэ': kana = 'ぺ'; break; case 'по': kana = 'ぽ'; break; case 'ма': kana = 'ま'; break; case 'ми': kana = 'み'; break; case 'му': kana = 'む'; break; case 'мэ': kana = 'め'; break; case 'мо': kana = 'も'; break; case 'я': kana = 'や'; break; case 'ю': kana = 'ゆ'; break; case 'ё': kana = 'よ'; break; case 'ра': kana = 'ら'; break; case 'ри': kana = 'り'; break; case 'ру': kana = 'る'; break; case 'рэ': kana = 'れ'; break; case 'ро': kana = 'ろ'; break; case 'ва': kana = (system !== 'static-ru' && isItTheOnlySyllable && !isThereOnlyOneWord) ? 'は' : 'わ'; break; case 'во': kana = 'を'; break; case 'н': case 'нъ': //перед гласными такая запись обязательна - иначе не отличить от ряда "на" case 'м': kana = 'ん'; break; case 'кя': kana = 'きゃ'; break; case 'кю': kana = 'きゅ'; break; case 'кё': kana = 'きょ'; break; case 'ся': if (system !== 'nonstandard-ru') kana = 'しゃ'; break; case 'сю': if (system !== 'nonstandard-ru') kana = 'しゅ'; break; case 'сё': if (system !== 'nonstandard-ru') kana = 'しょ'; break; case 'тя': if (system !== 'nonstandard-ru') kana = 'ちゃ'; break; case 'тю': kana = system !== 'nonstandard-ru' ? 'ちゅ' : 'てゅ'; break; case 'тё': if (system !== 'nonstandard-ru') kana = 'ちょ'; break; case 'ня': kana = 'にゃ'; break; case 'ню': kana = 'にゅ'; break; case 'нё': kana = 'にょ'; break; case 'хя': kana = 'ひゃ'; break; case 'хю': kana = 'ひゅ'; break; case 'хё': kana = 'ひょ'; break; case 'мя': kana = 'みゃ'; break; case 'мю': kana = 'みゅ'; break; case 'мё': kana = 'みょ'; break; case 'ря': kana = 'りゃ'; break; case 'рю': kana = 'りゅ'; break; case 'рё': kana = 'りょ'; break; case 'гя': kana = 'ぎゃ'; break; case 'гю': kana = 'ぎゅ'; break; case 'гё': kana = 'ぎょ'; break; case 'дзя': kana = system !== 'nonstandard-ru' ? 'じゃ' : 'づゃ'; break; case 'дзю': kana = system !== 'nonstandard-ru' ? 'じゅ' : 'づゅ'; break; case 'дзё': kana = system !== 'nonstandard-ru' ? 'じょ' : 'づょ'; break; case 'бя': kana = 'びゃ'; break; case 'бю': kana = 'びゅ'; break; case 'бё': kana = 'びょ'; break; case 'пя': kana = 'ぴゃ'; break; case 'пю': kana = 'ぴゅ'; break; case 'пё': kana = 'ぴょ'; break; //расширенная кана и системы не по Поливанову case 'щи': if (system === 'nonstandard-ru') kana = 'し'; break; case 'джи': if (system === 'nonstandard-ru') kana = prevSyllable && prevSyllable === 'чи' ? 'ぢ' : 'じ'; break; case 'чи': if (system === 'nonstandard-ru') kana = 'ち'; break; case 'щя': if (system === 'nonstandard-ru') kana = 'しゃ'; break; case 'щю': if (system === 'nonstandard-ru') kana = 'しゅ'; break; case 'щё': if (system === 'nonstandard-ru') kana = 'しょ'; break; case 'чя': if (system === 'nonstandard-ru') kana = 'ちゃ'; break; case 'чю': if (system === 'nonstandard-ru') kana = 'ちゅ'; break; case 'чё': if (system === 'nonstandard-ru') kana = 'ちょ'; break; case 'джя': if (system === 'nonstandard-ru') kana = 'じゃ'; break; case 'джю': if (system === 'nonstandard-ru') kana = 'じゅ'; break; case 'джё': if (system === 'nonstandard-ru') kana = 'じょ'; break; case 'йи': kana = 'いぃ'; break; case 'йэ': kana = 'いぇ'; break; case 'ви': kana = 'ゔぃ'; break; case 'ву': kana = 'ゔ'; break; case 'вэ': kana = 'ゔぇ'; break; case 'во': kana = 'ゔぉ'; break; case 'вя': kana = 'ゔゃ'; break; case 'вю': kana = 'ゔゅ'; break; case 'вйэ': kana = 'ゔぃぇ'; break; case 'вё': kana = 'ゔょ'; break; case 'кйэ': kana = 'きぇ'; break; case 'гйэ': kana = 'ぎぇ'; break; case 'ква': kana = 'くぁ'; break; case 'кви': kana = 'くぃ'; break; case 'квэ': kana = 'くぇ'; break; case 'кво': kana = 'くぉ'; break; case 'гва': kana = 'ぐぁ'; break; case 'гви': kana = 'ぐぃ'; break; case 'гвэ': kana = 'ぐぇ'; break; case 'гво': kana = 'ぐぉ'; break; case 'щэ': if (system === 'nonstandard-ru') kana = 'しぇ'; break; case 'джэ': if (system === 'nonstandard-ru') kana = 'じぇ'; break; case 'чэ': if (system === 'nonstandard-ru') kana = 'ちぇ'; break; case 'ца': kana = 'つぁ'; break; case 'ци': kana = 'つぃ'; break; case 'цэ': kana = 'つぇ'; break; case 'цо': kana = 'つぉ'; break; case 'ця': kana = 'つゃ'; break; case 'цю': kana = 'つゅ'; break; case 'цйэ': kana = 'つぃぇ'; break; case 'цё': kana = 'つョ'; break; case 'дзйэ': kana = 'づぃぇ'; break; case 'ту': kana = 'とぅ'; break; case 'тви': kana = 'とぃ'; break; case 'ди': kana = 'でぃ'; break; case 'ду': kana = 'どぅ'; break; case 'дви': kana = 'どぃ'; break; case 'дю': kana = 'でゅ'; break; case 'нви': kana = 'ぬぃ'; break; case 'нйэ': kana = 'にぇ'; break; case 'хйэ': kana = 'ひぇ'; break; case 'бви': kana = 'ぶぃ'; break; case 'бйэ': kana = 'びぇ'; break; case 'пви': kana = 'ぷぃ'; break; case 'пйэ': kana = 'ぴぇ'; break; case 'фа': kana = 'ふぁ'; break; case 'фи': kana = 'ふぃ'; break; case 'фэ': kana = 'ふぇ'; break; case 'фо': kana = 'ふぉ'; break; case 'фя': kana = 'ふゃ'; break; case 'фю': kana = 'ふゅ'; break; case 'фйэ': kana = 'ふぃぇ'; break; case 'фё': kana = 'ふょ'; break; case 'ху': kana = 'ほぅ'; break; case 'мви': kana = 'むぃ'; break; case 'мйэ': kana = 'みぇ'; break; case 'рви': kana = 'るぃ'; break; case 'рйэ': kana = 'りぇ'; break; case 'ла': kana = 'ら゚'; break; case 'ли': kana = 'り゚'; break; case 'лу': kana = 'る゚'; break; case 'лэ': kana = 'れ゚'; break; case 'ло': kana = 'ろ゚'; break; case 'ля': kana = 'り゚ゃ'; break; case 'лю': kana = 'り゚ゅ'; break; case 'лйэ': kana = 'り゚ぇ'; break; case 'лё': kana = 'り゚ょ'; break; } const nextSyllable = splitedWord[index + 1]; const followingLetter = nextSyllable ? nextSyllable[0] : undefined; const isThereSoftSign = syllable[1] === 'ь'; const currentSyllableLettersAmount = syllable.length; if (isItLongConsonant()) kana = 'っ'; if (guess && isItDevoicedVowel()) { let correctedSyllable = syllable; if (isThereSoftSign || isItAlwaysSoftConsonant(correctedSyllable)) { correctedSyllable += 'и'; } else { correctedSyllable += 'у'; } const correctedKana = toKanaRU(correctedSyllable, options); kana = correctedKana ? correctedKana : ''; } hiraganaWord += kana; function isItLongConsonant() { if (!nextSyllable || !followingLetter) return false; if (currentSyllableLettersAmount !== 1) return false; const consideringLetter = syllable; if (!hasOnlyConsonants(consideringLetter)) return false; if (consideringLetter === 'н' || consideringLetter === 'м') return false; //Долгота согласных со слогами рядов な и ま выражается ん if (!hasOnlyConsonants(followingLetter)) return false; if (consideringLetter === 'т' && followingLetter === 'ц') return true; //っつ по системе Поливанова пишется как тцу if (consideringLetter !== followingLetter) return false; return true; } function isItDevoicedVowel() { if (currentSyllableLettersAmount !== 1 && !isThereSoftSign) return false; const consideringLetter = syllable[0]; if (!isItVoicelessConsonant(consideringLetter)) return false; if (followingLetter && !isItVoicelessConsonant(followingLetter)) return false; if (consideringLetter === followingLetter) return false; //Вынужденная мера, иначе каждый случай идущих двух подряд согласных будет интерпретироваться и как неслышный гласный, и как двойной согласный одовременно return true; } })); hiraganaWords.push(hiraganaWord); } const hiraganaText = hiraganaWords.join(' '); const kanaText = toKana === 'hiragana' ? hiraganaText : convertKana(hiraganaText, "katakana"); return kanaText; } function isItAlwaysSoftConsonant(letter) { const alwSoftConRegExp = /[чщ]/; return letter.match(alwSoftConRegExp); } function isItVoicelessConsonant(letter) { const voicelessConsonantRegExp = /[пфктсшхцшщ]/; return letter.match(voicelessConsonantRegExp); } function hasOnlyConsonants(str) { const consonantRegExp = /[^цкнгшщзхфвпрлджчсмтб]/; return !str.match(consonantRegExp); } /* Подробнее об ограничениях работы функций и о рекомендациях при их использовании написано в /readme.md и /docs/explanation.md Нежелательные результаты работы функций отражены в ./tests */ function fromKana(kanaText, options) { fromKanaErrorCheck(kanaText, options); //На этом этапе конфликт между выставленным языком и системой исключён const optionsModified = (typeof options === 'object' ? Object.assign(Object.assign({}, options), { fromLang: getLang(options.toLang ? options.toLang : options.system) }) : { toLang: getLang(options) }); if (typeof options === 'string') { if (options === 'hepburn' || options === 'kunrei-shiki' || options === 'nihon-shiki' || options === 'nonstandard-ru' || options === 'polivanov' || options === 'static-ru') { optionsModified.system = options; } } if (optionsModified.toLang === 'ru') return fromKanaRU(kanaText, optionsModified.system); return fromKanaEN(kanaText, optionsModified.system); } function toKana(text, options) { toKanaErrorCheck(text, options); //На этом этапе конфликт между выставленным языком и системой исключён const optionsModified = (typeof options === 'object' ? Object.assign(Object.assign({}, options), { fromLang: getLang(options.fromLang ? options.fromLang : options.system) }) : { fromLang: getLang(options) }); if (typeof options === 'boolean') { optionsModified.guess = options; } else if (typeof options === 'string') { switch (options) { case 'hiragana': case 'katakana': optionsModified.toKana = options; break; case 'hepburn': case 'kunrei-shiki': case 'nihon-shiki': case 'polivanov': case 'nonstandard-ru': case 'static-ru': optionsModified.system = options; break; } } if (optionsModified.fromLang === 'ru') return toKanaRU(text, optionsModified); return toKanaEN(text, optionsModified); } function getLang(option) { if (option === 'en' || option === 'ru') return option; if (option === 'hepburn' || option === 'kunrei-shiki' || option === 'nihon-shiki' || option === 'nonstandard-ru' || option === 'polivanov' || option === 'static-ru') { return getLangFromSystem(option); } return 'en'; } exports.convertKana = convertKana; exports.fromKana = fromKana; exports.toKana = toKana;