henkan 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +1 -1
  2. package/dist/index.cjs.js +54 -33
  3. package/dist/index.cjs.js.map +3 -3
  4. package/dist/index.mjs +54 -35
  5. package/dist/index.mjs.map +2 -2
  6. package/dist/types/types.d.ts +21 -0
  7. package/dist/types/types.d.ts.map +1 -1
  8. package/dist/types/utils.d.ts +13 -10
  9. package/dist/types/utils.d.ts.map +1 -1
  10. package/docs/api/README.md +1 -0
  11. package/docs/api/functions/capitalizeString.md +1 -1
  12. package/docs/api/functions/convertJMdict.md +1 -1
  13. package/docs/api/functions/convertKanjiDic.md +1 -1
  14. package/docs/api/functions/convertKradFile.md +3 -3
  15. package/docs/api/functions/convertRadkFile.md +3 -3
  16. package/docs/api/functions/convertTanakaCorpus.md +1 -1
  17. package/docs/api/functions/generateAnkiNote.md +1 -1
  18. package/docs/api/functions/generateAnkiNotesFile.md +1 -1
  19. package/docs/api/functions/getKanji.md +1 -1
  20. package/docs/api/functions/getKanjiExtended.md +1 -1
  21. package/docs/api/functions/getWord.md +1 -1
  22. package/docs/api/functions/isStringArray.md +1 -1
  23. package/docs/api/functions/isValidArray.md +1 -1
  24. package/docs/api/functions/isValidArrayWithFirstElement.md +1 -1
  25. package/docs/api/functions/makeSSML.md +1 -1
  26. package/docs/api/functions/shuffleArray.md +1 -1
  27. package/docs/api/functions/synthesizeSpeech.md +25 -13
  28. package/docs/api/interfaces/DictKanji.md +5 -5
  29. package/docs/api/interfaces/DictKanjiForm.md +4 -4
  30. package/docs/api/interfaces/DictKanjiMisc.md +5 -5
  31. package/docs/api/interfaces/DictKanjiReading.md +3 -3
  32. package/docs/api/interfaces/DictKanjiReadingMeaning.md +3 -3
  33. package/docs/api/interfaces/DictKanjiReadingMeaningGroup.md +3 -3
  34. package/docs/api/interfaces/DictKanjiWithRadicals.md +3 -3
  35. package/docs/api/interfaces/DictMeaning.md +11 -11
  36. package/docs/api/interfaces/DictRadical.md +4 -4
  37. package/docs/api/interfaces/DictReading.md +5 -5
  38. package/docs/api/interfaces/DictWord.md +8 -8
  39. package/docs/api/interfaces/ExamplePart.md +7 -7
  40. package/docs/api/interfaces/GlossSpecificNumber.md +31 -0
  41. package/docs/api/interfaces/Grammar.md +15 -15
  42. package/docs/api/interfaces/GrammarMeaning.md +3 -3
  43. package/docs/api/interfaces/Kana.md +11 -11
  44. package/docs/api/interfaces/Kanji.md +22 -22
  45. package/docs/api/interfaces/KanjiComponent.md +3 -3
  46. package/docs/api/interfaces/KanjiForm.md +4 -4
  47. package/docs/api/interfaces/NoteAndTag.md +3 -3
  48. package/docs/api/interfaces/Phrase.md +16 -4
  49. package/docs/api/interfaces/Radical.md +16 -16
  50. package/docs/api/interfaces/Reading.md +5 -5
  51. package/docs/api/interfaces/ResultEntry.md +7 -7
  52. package/docs/api/interfaces/TanakaExample.md +16 -6
  53. package/docs/api/interfaces/Translation.md +3 -3
  54. package/docs/api/interfaces/UsefulRegExps.md +9 -9
  55. package/docs/api/interfaces/Word.md +14 -14
  56. package/docs/api/type-aliases/Dict.md +1 -1
  57. package/docs/api/type-aliases/DictName.md +1 -1
  58. package/docs/api/type-aliases/EntryType.md +1 -1
  59. package/docs/api/type-aliases/JLPT.md +1 -1
  60. package/docs/api/type-aliases/Result.md +1 -1
  61. package/package.json +5 -5
package/dist/index.mjs CHANGED
@@ -1131,9 +1131,7 @@ var noteMap = /* @__PURE__ */ new Map([
1131
1131
  import libxml from "libxmljs2";
1132
1132
  import xml from "xml2js";
1133
1133
  import iconv from "iconv-lite";
1134
- import {
1135
- SynthesizeSpeechCommand
1136
- } from "@aws-sdk/client-polly";
1134
+ import fetch from "node-fetch";
1137
1135
  var Kuroshiro = __require("kuroshiro");
1138
1136
  var KuromojiAnalyzer = __require("kuroshiro-analyzer-kuromoji");
1139
1137
  function capitalizeString(value) {
@@ -1279,11 +1277,9 @@ function convertJMdict(xmlString, examples) {
1279
1277
  ).map((reading) => reading.reading)
1280
1278
  );
1281
1279
  const kanjiForms2 = entryObj.kanjiForms ? new Set(
1282
- entryObj.kanjiForms.filter(
1283
- (kanjiForm) => (!kanjiForm.notes || !kanjiForm.notes.some(
1284
- (note) => notSearchedForms.has(note)
1285
- )) && (entryObj.isCommon === void 0 || kanjiForm.commonness && kanjiForm.commonness.length > 0)
1286
- ).map((kanjiForm) => kanjiForm.form)
1280
+ entryObj.kanjiForms.map(
1281
+ (kanjiForm) => kanjiForm.form
1282
+ )
1287
1283
  ) : void 0;
1288
1284
  let existsExample = false;
1289
1285
  if (kanjiForms2 && kanjiForms2.size > 0 && tanakaParts) {
@@ -1465,13 +1461,13 @@ function convertRadkFile(radkBuffer, kanjiDic) {
1465
1461
  try {
1466
1462
  const fileParsed = iconv.decode(radkBuffer, "euc-jp").split("\n").filter((line) => !line.startsWith("#"));
1467
1463
  const radicals = [];
1468
- for (let i = 0; i <= fileParsed.length; i++) {
1464
+ for (let i = 0; i < fileParsed.length; i++) {
1469
1465
  const line = fileParsed[i];
1470
1466
  if (!line) continue;
1471
1467
  if (line.startsWith("$ ")) {
1472
1468
  const radical = {
1473
- radical: line.charAt(2),
1474
- strokes: line.substring(4)
1469
+ radical: line.charAt(2).trim(),
1470
+ strokes: line.substring(4).trim()
1475
1471
  };
1476
1472
  let j = i + 1;
1477
1473
  let kanjiLine = fileParsed[j];
@@ -1484,6 +1480,7 @@ function convertRadkFile(radkBuffer, kanjiDic) {
1484
1480
  (dictKanji) => dictKanji.kanji === kanji
1485
1481
  );
1486
1482
  if (foundKanji) kanjiList.push(foundKanji);
1483
+ else kanjiList.push({ kanji, readingMeaning: [] });
1487
1484
  }
1488
1485
  j++;
1489
1486
  kanjiLine = fileParsed[j];
@@ -1510,7 +1507,7 @@ function convertKradFile(kradBuffer, kanjiDic, katakanaList) {
1510
1507
  const split = line.split(" : ");
1511
1508
  const kanjiChar = split[0];
1512
1509
  const radicalsRow = split[1];
1513
- if (!kanjiChar || !radicalsRow) throw new Error("Invalid KRAD entry");
1510
+ if (!kanjiChar || !radicalsRow) continue;
1514
1511
  const kanji = {
1515
1512
  ...kanjiChar && radicalsRow && kanjiChar.length === 1 && radicalsRow.length > 0 ? { kanji: kanjiChar } : { kanji: "" },
1516
1513
  radicals: []
@@ -1721,11 +1718,9 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1721
1718
  ).map((reading) => reading.reading)
1722
1719
  );
1723
1720
  const kanjiForms = word.kanjiForms ? new Set(
1724
- word.kanjiForms.filter(
1725
- (kanjiForm) => (!kanjiForm.notes || !kanjiForm.notes.some(
1726
- (note) => notSearchedForms.has(note)
1727
- )) && (word.common === void 0 || kanjiForm.common === true)
1728
- ).map((kanjiForm) => kanjiForm.kanjiForm)
1721
+ word.kanjiForms.map(
1722
+ (kanjiForm) => kanjiForm.kanjiForm
1723
+ )
1729
1724
  ) : void 0;
1730
1725
  const kanjiFormExamples = [];
1731
1726
  const readingMatchingKanjiFormExamples = [];
@@ -1733,7 +1728,7 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1733
1728
  const partParts = /* @__PURE__ */ new Set();
1734
1729
  for (const example of examples)
1735
1730
  for (const part of example.parts) {
1736
- const readingAsReadingMatch = part.reading !== void 0 && readings.has(part.reading);
1731
+ const readingAsReadingMatch = part.reading !== void 0 && readings.has(part.reading) || part.inflectedForm !== void 0 && readings.has(part.inflectedForm);
1737
1732
  if (kanjiForms && kanjiForms.size > 0 && kanjiForms.has(part.baseForm)) {
1738
1733
  if (readingAsReadingMatch) {
1739
1734
  readingMatchingKanjiFormExamples.push(example);
@@ -1746,17 +1741,20 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1746
1741
  }
1747
1742
  const readingAsBaseFormMatch = readings.has(part.baseForm);
1748
1743
  const referenceIDMatch = part.referenceID !== void 0 && word.id !== void 0 && part.referenceID === word.id;
1749
- if (readingAsReadingMatch || readingAsBaseFormMatch || referenceIDMatch) {
1744
+ if (readingAsBaseFormMatch || referenceIDMatch) {
1750
1745
  readingExamples.push(example);
1751
- if (readingAsReadingMatch) partParts.add(part.reading);
1752
1746
  if (readingAsBaseFormMatch) partParts.add(part.baseForm);
1753
1747
  if (referenceIDMatch) partParts.add(part.referenceID);
1754
1748
  break;
1755
1749
  }
1756
1750
  }
1757
1751
  const exampleSize = readingMatchingKanjiFormExamples.length + kanjiFormExamples.length + readingExamples.length;
1758
- const includeKanjiFormExamples = readingMatchingKanjiFormExamples.length < Math.max(2, Math.round(exampleSize * 0.05));
1759
- const includeReadingExamples = word.usuallyInKana === void 0 && includeKanjiFormExamples && readingExamples.length >= Math.max(10, Math.round(exampleSize * 0.15)) || word.usuallyInKana === true && readingExamples.length >= Math.max(2, Math.round(exampleSize * 0.5));
1752
+ const includeReadingThreshold = Math.max(
1753
+ 10,
1754
+ Math.round(exampleSize * 0.5)
1755
+ );
1756
+ const includeKanjiFormExamples = word.kanjiForms !== void 0;
1757
+ const includeReadingExamples = readingExamples.length >= includeReadingThreshold && readingExamples.length >= readingMatchingKanjiFormExamples.length && readingExamples.length >= kanjiFormExamples.length || readingExamples.length >= includeReadingThreshold && word.usuallyInKana === true || word.kanjiForms === void 0;
1760
1758
  let wordExamples = [
1761
1759
  ...readingMatchingKanjiFormExamples,
1762
1760
  ...includeKanjiFormExamples ? kanjiFormExamples : [],
@@ -1768,7 +1766,11 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1768
1766
  outer: for (const example of wordExamples) {
1769
1767
  if (seenPhrases.has(example.phrase)) continue;
1770
1768
  for (const part of example.parts)
1771
- if (part.glossNumber === i + 1 && (partParts.has(part.baseForm) || part.reading && partParts.has(part.reading) || part.referenceID && partParts.has(part.referenceID))) {
1769
+ if (part.glossNumber === i + 1 && (partParts.has(part.baseForm) || includeReadingExamples && (part.reading && partParts.has(part.reading) || part.inflectedForm && partParts.has(part.inflectedForm) || part.referenceID && partParts.has(part.referenceID)))) {
1770
+ example.glossNumber = {
1771
+ wordId: word.id,
1772
+ glossNumber: i + 1
1773
+ };
1772
1774
  glossSpecificExamples.push(example);
1773
1775
  seenPhrases.add(example.phrase);
1774
1776
  break outer;
@@ -1787,7 +1789,8 @@ function getWord(dict, id, kanjiDic, examples, dictWord, noteTypeName, deckPath)
1787
1789
  word.phrases = (wordExamples.length > 5 ? wordExamples.slice(0, 5) : wordExamples).map((ex) => ({
1788
1790
  phrase: ex.furigana ?? ex.phrase,
1789
1791
  translation: ex.translation,
1790
- originalPhrase: ex.phrase
1792
+ originalPhrase: ex.phrase,
1793
+ ...ex.glossNumber ? { glossNumber: ex.glossNumber } : {}
1791
1794
  }));
1792
1795
  }
1793
1796
  return word;
@@ -2065,18 +2068,34 @@ function makeSSML(formText, fullReading) {
2065
2068
  }
2066
2069
  return ssml;
2067
2070
  }
2068
- async function synthesizeSpeech(client, ssmlText, options) {
2071
+ async function synthesizeSpeech(ssmlText, apiKey, options) {
2069
2072
  return await new Promise(
2070
2073
  async (resolve, reject) => {
2071
2074
  try {
2072
- const command = new SynthesizeSpeechCommand({
2073
- Text: ssmlText,
2074
- TextType: "ssml",
2075
- ...options
2075
+ const res = await fetch("https://ttsfree.com/api/v1/tts", {
2076
+ method: "POST",
2077
+ body: JSON.stringify({
2078
+ text: ssmlText,
2079
+ ...options
2080
+ }),
2081
+ headers: {
2082
+ "Content-Type": "application/json",
2083
+ apikey: apiKey
2084
+ }
2076
2085
  });
2077
- const response = await client.send(command);
2078
- const stream = response.AudioStream ? Buffer.from(await response.AudioStream.transformToByteArray()) : null;
2079
- resolve(stream);
2086
+ if (!res.ok)
2087
+ throw new Error(
2088
+ `TTS request failed:
2089
+ ${res.status}: ${res.statusText}`
2090
+ );
2091
+ const data = await res.json();
2092
+ if (data.status !== "success" || data.mess !== "success" || data.audioData.length === 0)
2093
+ throw new Error("Invalid TTS response data");
2094
+ const mp3Buffer = Buffer.from(
2095
+ data.audioData,
2096
+ "base64"
2097
+ );
2098
+ resolve(mp3Buffer);
2080
2099
  } catch (err) {
2081
2100
  reject(err);
2082
2101
  }
@@ -2123,7 +2142,7 @@ function generateAnkiNote(entry) {
2123
2142
  ).join("") : noKanjiForms
2124
2143
  ],
2125
2144
  entry.translations.map(
2126
- (translationEntry, index) => `${index > 2 ? "<details><summary>Show translation</summary>" : ""}${createEntry(`<span class="word word-translation">${translationEntry.translation}</span>`, translationEntry.notes)}${index > 2 ? "</details>" : ""}`
2145
+ (translationEntry, index) => `<span class="word word-index${entry.phrases && entry.phrases.some((phrase, index2) => index === index2 && phrase.glossNumber && phrase.glossNumber.wordId === entry.id && phrase.glossNumber.glossNumber === index + 1) ? " gloss-specific" : ""}">${index + 1}</span>${index > 2 ? "<details><summary>Show translation</summary>" : ""}${createEntry(`<span class="word word-translation">${translationEntry.translation}</span>`, translationEntry.notes)}${index > 2 ? "</details>" : ""}`
2127
2146
  ).join(""),
2128
2147
  entry.kanji ? entry.kanji.map(
2129
2148
  (kanjiEntry) => createEntry(
@@ -2132,11 +2151,11 @@ function generateAnkiNote(entry) {
2132
2151
  )
2133
2152
  ).join("") : '<span class="word word-kanji">(no kanji)</span>',
2134
2153
  entry.phrases ? entry.phrases.map(
2135
- (phraseEntry) => createEntry(
2154
+ (phraseEntry, index) => `<span class="word word-index${entry.translations.some((_translation, index2) => index === index2 && phraseEntry.glossNumber && phraseEntry.glossNumber.wordId === entry.id && phraseEntry.glossNumber.glossNumber === index2 + 1) ? " gloss-specific" : ""}">${index + 1}</span>${createEntry(
2136
2155
  `<span class="word word-phrase"><span class="word word-phrase-original">${phraseEntry.originalPhrase}</span><span class="word word-phrase-furigana">${phraseEntry.phrase}</span></span>`,
2137
2156
  [phraseEntry.translation],
2138
2157
  true
2139
- )
2158
+ )}`
2140
2159
  ).join("") : '<span class="word word-phrase">(no phrases) (Search on dictionaries!)</span>',
2141
2160
  ...entry.tags && entry.tags.length > 0 ? [
2142
2161
  entry.tags.map(