From a933cfdc0780f618bb1ae60930a82a2af954e3cd Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 17 Jul 2021 12:20:11 -0400 Subject: [PATCH] Pronunciation nasal improvement (#1834) * Organize * Add utility to get diacritic information about a character * Show mora without diacritic * Add a hidden handakuten for copy-paste purposes --- ext/css/display-pronunciation.css | 6 ++++++ ext/js/display/pronunciation-generator.js | 25 ++++++++++++++++------- ext/js/language/japanese-util.js | 20 ++++++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/ext/css/display-pronunciation.css b/ext/css/display-pronunciation.css index 4ea15baf..e26c5952 100644 --- a/ext/css/display-pronunciation.css +++ b/ext/css/display-pronunciation.css @@ -77,6 +77,12 @@ box-sizing: border-box; z-index: 1; } +.pitch-accent-character-nasal-diacritic { + position: absolute; + width: 0; + height: 0; + opacity: 0; +} .pitch-accent-position::before { content: ' ['; diff --git a/ext/js/display/pronunciation-generator.js b/ext/js/display/pronunciation-generator.js index eb5eb035..13e3db3e 100644 --- a/ext/js/display/pronunciation-generator.js +++ b/ext/js/display/pronunciation-generator.js @@ -35,16 +35,14 @@ class PronunciationGenerator { const n1 = document.createElement('span'); n1.className = 'pitch-accent-character'; - - const n2 = document.createElement('span'); - n2.className = 'pitch-accent-character-inner'; - - n1.appendChild(n2); - n1.dataset.position = `${i}`; n1.dataset.pitch = highPitch ? 'high' : 'low'; n1.dataset.pitchNext = highPitchNext ? 'high' : 'low'; + + const n2 = document.createElement('span'); + n2.className = 'pitch-accent-character-inner'; n2.textContent = mora; + n1.appendChild(n2); if (devoice) { n1.dataset.devoice = 'true'; @@ -54,7 +52,13 @@ class PronunciationGenerator { } if (nasal) { n1.dataset.nasal = 'true'; - const n3 = document.createElement('span'); + n1.dataset.originalText = mora; + n2.textContent = this._getPlainMora(mora); + let n3 = document.createElement('span'); + n3.className = 'pitch-accent-character-nasal-diacritic'; + n3.textContent = '\u309a'; // Combining handakuten + n1.appendChild(n3); + n3 = document.createElement('span'); n3.className = 'pitch-accent-character-nasal-indicator'; n1.appendChild(n3); } @@ -142,4 +146,11 @@ class PronunciationGenerator { node.setAttribute('r', radius); return node; } + + _getPlainMora(mora) { + const first = mora[0]; + const info = this._japaneseUtil.getKanaDiacriticInfo(first); + if (info === null) { return mora; } + return `${info.character}${mora.substring(1)}`; + } } diff --git a/ext/js/language/japanese-util.js b/ext/js/language/japanese-util.js index 9d7ad0d8..c7f79751 100644 --- a/ext/js/language/japanese-util.js +++ b/ext/js/language/japanese-util.js @@ -154,6 +154,21 @@ const JapaneseUtil = (() => { return map; })(); + const DIACRITIC_MAPPING = (() => { + const kana = 'うゔ-かが-きぎ-くぐ-けげ-こご-さざ-しじ-すず-せぜ-そぞ-ただ-ちぢ-つづ-てで-とど-はばぱひびぴふぶぷへべぺほぼぽワヷ-ヰヸ-ウヴ-ヱヹ-ヲヺ-カガ-キギ-クグ-ケゲ-コゴ-サザ-シジ-スズ-セゼ-ソゾ-タダ-チヂ-ツヅ-テデ-トド-ハバパヒビピフブプヘベペホボポ'; + const map = new Map(); + for (let i = 0, ii = kana.length; i < ii; i += 3) { + const character = kana[i]; + const dakuten = kana[i + 1]; + const handakuten = kana[i + 2]; + map.set(dakuten, {character, type: 'dakuten'}); + if (handakuten !== '-') { + map.set(handakuten, {character, type: 'handakuten'}); + } + } + return map; + })(); + function isCodePointInRange(codePoint, [min, max]) { return (codePoint >= min && codePoint <= max); @@ -417,6 +432,11 @@ const JapaneseUtil = (() => { return this._wanakana !== null; } + getKanaDiacriticInfo(character) { + const info = DIACRITIC_MAPPING.get(character); + return typeof info !== 'undefined' ? {character: info.character, type: info.type} : null; + } + // Furigana distribution distributeFurigana(term, reading) {