diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/css/display.css | 25 | ||||
| -rw-r--r-- | ext/data/schemas/dictionary-term-meta-bank-v3-schema.json | 34 | ||||
| -rw-r--r-- | ext/js/display/display-generator.js | 34 | ||||
| -rw-r--r-- | ext/js/language/dictionary-data-util.js | 19 | ||||
| -rw-r--r-- | ext/js/language/translator.js | 10 | 
5 files changed, 113 insertions, 9 deletions
| diff --git a/ext/css/display.css b/ext/css/display.css index c6a39526..cd9391fe 100644 --- a/ext/css/display.css +++ b/ext/css/display.css @@ -1512,6 +1512,31 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con      padding-right: 0.1em;      margin-right: 0.1em;  } +.pitch-accent-character-devoice-indicator { +    display: block; +    position: absolute; +    left: 50%; +    top: 50%; +    width: 1.125em; +    height: 1.125em; +    border: calc(1.5em / var(--font-size-no-units)) dotted var(--danger-color); +    border-radius: 50%; +    box-sizing: border-box; +    z-index: 1; +    transform: translate(-50%, -50%); +} +.pitch-accent-character-nasal-indicator { +    display: block; +    position: absolute; +    right: -0.125em; +    top: 0.125em; +    width: 0.375em; +    height: 0.375em; +    border: calc(1.5em / var(--font-size-no-units)) solid var(--danger-color); +    border-radius: 50%; +    box-sizing: border-box; +    z-index: 1; +}  .pitch-accent-position::before {      content: ' [';  } diff --git a/ext/data/schemas/dictionary-term-meta-bank-v3-schema.json b/ext/data/schemas/dictionary-term-meta-bank-v3-schema.json index 6483ce01..8eb9d343 100644 --- a/ext/data/schemas/dictionary-term-meta-bank-v3-schema.json +++ b/ext/data/schemas/dictionary-term-meta-bank-v3-schema.json @@ -85,6 +85,40 @@                                              "description": "Mora position of the pitch accent downstep. A value of 0 indicates that the word does not have a downstep (heiban).",                                              "minimum": 0                                          }, +                                        "nasal": { +                                            "oneOf": [ +                                                { +                                                    "type": "integer", +                                                    "description": "Position of a mora with nasal sound.", +                                                    "minimum": 0 +                                                }, +                                                { +                                                    "type": "array", +                                                    "description": "Positions of morae with nasal sound.", +                                                    "items": { +                                                        "type": "integer", +                                                        "minimum": 0 +                                                    } +                                                } +                                            ] +                                        }, +                                        "devoice": { +                                            "oneOf": [ +                                                { +                                                    "type": "integer", +                                                    "description": "Position of a mora with devoiced sound.", +                                                    "minimum": 0 +                                                }, +                                                { +                                                    "type": "array", +                                                    "description": "Positions of morae with devoiced sound.", +                                                    "items": { +                                                        "type": "integer", +                                                        "minimum": 0 +                                                    } +                                                } +                                            ] +                                        },                                          "tags": {                                              "type": "array",                                              "description": "List of tags for this pitch accent.", diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js index 737fa72a..d7ae3bd9 100644 --- a/ext/js/display/display-generator.js +++ b/ext/js/display/display-generator.js @@ -468,12 +468,17 @@ class DisplayGenerator {      _createPitch(details) {          const jp = this._japaneseUtil; -        const {reading, position, tags, exclusiveTerms, exclusiveReadings} = details; +        const {reading, position, nasalPositions, devoicePositions, tags, exclusiveTerms, exclusiveReadings} = details;          const morae = jp.getKanaMorae(reading); +        const nasalPositionsSet = nasalPositions.length > 0 ? new Set(nasalPositions) : null; +        const devoicePositionsSet = devoicePositions.length > 0 ? new Set(devoicePositions) : null; +          const node = this._templates.instantiate('pitch-accent');          node.dataset.pitchAccentPosition = `${position}`; +        if (nasalPositions.length > 0) { node.dataset.nasalMoraPosition = nasalPositions.join(' '); } +        if (devoicePositions.length > 0) { node.dataset.devoiceMoraPosition = devoicePositions.join(' '); }          node.dataset.tagCount = `${tags.length}`;          let n = node.querySelector('.pitch-accent-position'); @@ -487,18 +492,39 @@ class DisplayGenerator {          n = node.querySelector('.pitch-accent-characters');          for (let i = 0, ii = morae.length; i < ii; ++i) { +            const i1 = i + 1;              const mora = morae[i];              const highPitch = jp.isMoraPitchHigh(i, position); -            const highPitchNext = jp.isMoraPitchHigh(i + 1, position); +            const highPitchNext = jp.isMoraPitchHigh(i1, position); +            const nasal = nasalPositionsSet !== null && nasalPositionsSet.has(i1); +            const devoice = devoicePositionsSet !== null && devoicePositionsSet.has(i1); + +            const n1 = document.createElement('span'); +            n1.className = 'pitch-accent-character'; + +            const n2 = document.createElement('span'); +            n2.className = 'pitch-accent-character-inner'; -            const n1 = this._templates.instantiate('pitch-accent-character'); -            const n2 = n1.querySelector('.pitch-accent-character-inner'); +            n1.appendChild(n2);              n1.dataset.position = `${i}`;              n1.dataset.pitch = highPitch ? 'high' : 'low';              n1.dataset.pitchNext = highPitchNext ? 'high' : 'low';              this._setTextContent(n2, mora, 'ja'); +            if (devoice) { +                n1.dataset.devoice = 'true'; +                const n3 = document.createElement('span'); +                n3.className = 'pitch-accent-character-devoice-indicator'; +                n1.appendChild(n3); +            } +            if (nasal) { +                n1.dataset.nasal = 'true'; +                const n3 = document.createElement('span'); +                n3.className = 'pitch-accent-character-nasal-indicator'; +                n1.appendChild(n3); +            } +              n.appendChild(n1);          } diff --git a/ext/js/language/dictionary-data-util.js b/ext/js/language/dictionary-data-util.js index 81d1c290..951e10ff 100644 --- a/ext/js/language/dictionary-data-util.js +++ b/ext/js/language/dictionary-data-util.js @@ -108,13 +108,15 @@ class DictionaryDataUtil {                  dictionaryPitchAccentInfoList = [];                  pitchAccentInfoMap.set(dictionary, dictionaryPitchAccentInfoList);              } -            for (const {position, tags} of pitches) { -                let pitchAccentInfo = this._findExistingPitchAccentInfo(reading, position, tags, dictionaryPitchAccentInfoList); +            for (const {position, nasalPositions, devoicePositions, tags} of pitches) { +                let pitchAccentInfo = this._findExistingPitchAccentInfo(reading, position, nasalPositions, devoicePositions, tags, dictionaryPitchAccentInfoList);                  if (pitchAccentInfo === null) {                      pitchAccentInfo = {                          terms: new Set(),                          reading,                          position, +                        nasalPositions, +                        devoicePositions,                          tags,                          exclusiveTerms: [],                          exclusiveReadings: [] @@ -228,11 +230,13 @@ class DictionaryDataUtil {          return results;      } -    static _findExistingPitchAccentInfo(reading, position, tags, pitchAccentInfoList) { +    static _findExistingPitchAccentInfo(reading, position, nasalPositions, devoicePositions, tags, pitchAccentInfoList) {          for (const pitchInfo of pitchAccentInfoList) {              if (                  pitchInfo.reading === reading &&                  pitchInfo.position === position && +                this._areArraysEqual(pitchInfo.nasalPositions, nasalPositions) && +                this._areArraysEqual(pitchInfo.devoicePositions, devoicePositions) &&                  this._areTagListsEqual(pitchInfo.tags, tags)              ) {                  return pitchInfo; @@ -241,6 +245,15 @@ class DictionaryDataUtil {          return null;      } +    static _areArraysEqual(array1, array2) { +        const ii = array1.length; +        if (ii !== array2.length) { return false; } +        for (let i = 0; i < ii; ++i) { +            if (array1[i] !== array2[i]) { return false; } +        } +        return true; +    } +      static _areTagListsEqual(tagList1, tagList2) {          const ii = tagList1.length;          if (tagList2.length !== ii) { return false; } diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js index 8519b728..c35d2203 100644 --- a/ext/js/language/translator.js +++ b/ext/js/language/translator.js @@ -828,12 +828,14 @@ class Translator {                          {                              if (data.reading !== reading) { continue; }                              const pitches = []; -                            for (const {position, tags} of data.pitches) { +                            for (const {position, tags, nasal, devoice} of data.pitches) {                                  const tags2 = [];                                  if (Array.isArray(tags) && tags.length > 0) {                                      tags2.push(this._createTagGroup(dictionary, tags));                                  } -                                pitches.push({position, tags: tags2}); +                                const nasalPositions = this._toNumberArray(nasal); +                                const devoicePositions = this._toNumberArray(devoice); +                                pitches.push({position, nasalPositions, devoicePositions, tags: tags2});                              }                              for (const {pronunciations, headwordIndex} of targets) {                                  pronunciations.push(this._createTermPronunciation( @@ -968,6 +970,10 @@ class Translator {          return JSON.stringify(array);      } +    _toNumberArray(value) { +        return Array.isArray(value) ? value : (typeof value === 'number' ? [value] : []); +    } +      // Kanji data      _createKanjiStat(name, value, databaseInfo, dictionary) { |