Created
December 26, 2025 04:12
-
-
Save max-mapper/ef8f4e163943e734bbd6d8f3f64cf822 to your computer and use it in GitHub Desktop.
hiragana/katakana to man'yougana converter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // js port of https://qiita.com/zakur00/items/0f5c6d03d565c1b2ae33 | |
| const MANYOU_RAW = ` | |
| あいうえお かきくけこ さしすせそ | |
| 安以宇衣於 加幾久計己 左之寸世曽 | |
| たちつてと なにぬねの はひふへほ | |
| 太知川天止 奈仁奴祢乃 波比不部保 | |
| まみむめも やゆよ らりるれろ | |
| 末美武女毛 也由与 良利留礼呂 | |
| わゐゑを ん | |
| 和為恵遠 无 | |
| アイウエオ カキクケコ サシスセソ | |
| 阿伊宇江於 加幾久介己 散之須世曽 | |
| タチツテト ナニヌネノ ハヒフヘホ | |
| 多千川天止 奈二奴禰乃 八比不部保 | |
| マミムメモ ヤユヨ ラリルレロ | |
| 万三牟女毛 也由与 良利流礼呂 | |
| ワヰヱヲ ン | |
| 和井慧乎 尓 | |
| `; | |
| const NORM_RAW = ` | |
| あぁ いぃ うゔぅ えぇ おぉ | |
| かがゕ きぎ くぐ けげゖ こご | |
| さざ しじ すず せぜ そぞ | |
| ただ ちぢ つづっ てで とど | |
| はばぱ ひびぴ ふぶぷ へべぺ ほぼぽ | |
| やゃ ゆゅ よょ | |
| わゎ | |
| アァ イィ ウヴゥ エェ オォ | |
| カガヵ キギ クグ ケゲヶ コゴ | |
| サザ シジ スズ セゼ ソゾ | |
| タダ チヂ ツヅッ テデ トド | |
| ハバパ ヒビピ フブプ ヘベペ ホボポ | |
| ヤャ ユュ ヨョ | |
| ワヷヮ ヰヸ ヱヹ ヲヺ | |
| `; | |
| // --- Data Preparation (Porting the ruby map/split/transpose logic) --- | |
| // 1. Prepare MANYOU_DICT | |
| // Ruby: removes spaces, splits lines, pairs them, transposes characters | |
| const manyouDict = {}; | |
| const manyouLines = MANYOU_RAW.replace(/ /g, "").trim().split(/\n+/); | |
| for (let i = 0; i < manyouLines.length; i += 2) { | |
| const kanaLine = manyouLines[i]; | |
| const kanjiLine = manyouLines[i + 1]; | |
| // Zip the two lines together | |
| for (let j = 0; j < kanaLine.length; j++) { | |
| if (kanjiLine[j]) { | |
| manyouDict[kanaLine[j]] = kanjiLine[j]; | |
| } | |
| } | |
| } | |
| // 2. Prepare NORM_DICT | |
| // Ruby: splits lines, maps index 1..end chars to index 0 char | |
| const normDict = {}; | |
| const normLines = NORM_RAW.trim().split(/\n+/); | |
| normLines.forEach(line => { | |
| // Remove spaces just in case, though the ruby logic splits by whitespace then handles chars | |
| // The ruby line: <<EOS.split.map... effectively handles line breaks and spaces implicitly if using default split | |
| // But strictly looking at the structure: "あぁ" is one token in Ruby if no spaces, but the heredoc has spaces: "あぁ いぃ..." | |
| // Let's replicate strict logic: split by whitespace to get groups like "あぁ", "いぃ" | |
| const groups = line.trim().split(/\s+/); | |
| groups.forEach(group => { | |
| const baseChar = group[0]; | |
| const variants = group.slice(1); | |
| for (const char of variants) { | |
| normDict[char] = baseChar; | |
| } | |
| }); | |
| }); | |
| // 3. Prepare Inverted Dictionary for reverse conversion | |
| const manyouDictInvert = Object.fromEntries( | |
| Object.entries(manyouDict).map(([k, v]) => [v, k]) | |
| ); | |
| // --- Optimization: Pre-compile Regexes --- | |
| // Ruby logic uses /[keys]/ which creates a character class. | |
| const normRegex = new RegExp(`[${Object.keys(normDict).join('')}]`, 'g'); | |
| const manyouRegex = new RegExp(`[${Object.keys(manyouDict).join('')}]`, 'g'); | |
| const manyouInvertRegex = new RegExp(`[${Object.keys(manyouDictInvert).join('')}]`, 'g'); | |
| // --- Main Functions --- | |
| /** | |
| * Converts text to Manyougana. | |
| * Equivalent to Ruby: NKF.nkf("-w -X", str).gsub(...).gsub(...) | |
| */ | |
| function toManyougana(str) { | |
| return str | |
| // Equivalent to NKF -X (Half-width Katakana to Full-width) | |
| .normalize('NFKC') | |
| // Normalize variants (e.g., ぁ -> あ) | |
| .replace(normRegex, (match) => normDict[match] || match) | |
| // Convert to Manyougana (e.g., あ -> 安) | |
| .replace(manyouRegex, (match) => manyouDict[match] || match); | |
| } | |
| /** | |
| * Converts Manyougana back to standard Japanese. | |
| * Equivalent to Ruby: str.gsub(/[values]/, invert_dict) | |
| */ | |
| function fromManyougana(str) { | |
| return str.replace(manyouInvertRegex, (match) => manyouDictInvert[match] || match); | |
| } | |
| // --- Test Execution --- | |
| const input = ` | |
| ルイス!ルイス!ルイス!ルイスウウウウウワあああああああああああああああああああああああん!!! | |
| ああああああ…ああ…あツあツー!ああああああああ!!!ルイスルイスルイスウウウあワあああああ!!! | |
| ああクンカクンカ!クンカクンカ!スーハースーハー!スーハースーハー!いい匂いたナあ…クんクん | |
| んはあツ!ルイス・フランソワースたんノ桃色フロントノ髪をクンカクンカシたいオ!クンカクンカ!あああ!! | |
| 間違えた!モフモフシたいオ!モフモフ!モフモフ!髪髪モフモフ!カリカリモフモフ…キユんキユんキユい!! | |
| 小説11巻ノルイスたんカワいカツたヨウ!!あああああ…あああ…あツああああああ!!フあああああんんツ!! | |
| アニメ2期決まツテラカツたねルイスたん!あああああああ!カワいい!ルイスたん!カワいい!あツあああああ! | |
| コミツク2巻モ発売さレテ嬉シ…いヤあああああああ!!!にヤああああああああん!!キヤああああああああ!! | |
| クあああああああああああ!!!コミツクナんテ現実シヤナい!!!!あ…小説モアニメモヨク考えたラ… | |
| ルイスちヤんは現実シヤナい?にヤあああああああああああああん!!ウあああああああああああ!! | |
| ソんナあああああああ!!いヤああああああああああああ!!はあああああああん!!ハルケキニアあああああ!! | |
| コノ!ちキシヨー!ヤメテヤる!!現実ナんカヤメ…テ…え!?見…テる?表紙絵ノルイスちヤんカ僕を見テる? | |
| 表紙絵ノルイスちヤんカ僕を見テるソ!ルイスちヤんカ僕を見テるソ!挿絵ノルイスちヤんカ僕を見テるソ!! | |
| アニメノルイスちヤんカ僕に話シカけテるソ!!!ヨカツた…セノ中またまた捨テたモンシヤナいんたねツ! | |
| いヤツホオオオオオオオオ!!!僕にはルイスちヤんカいる!!ヤツたヨケテイ!!ヒトリテテキるモん!!! | |
| あ、コミツクノルイスちヤああああああああああああああん!!いヤああああああああああああああああ!!!! | |
| あツあんああツああんあアン様ああ!!セ、セイハー!!シヤナあああああああ!!!ウイルヘルミナああああ!! | |
| ウウツウウウウ!!俺ノ想いヨルイスヘ届け!!ハルケキニアノルイスヘ届け! | |
| `.trim(); // Trim to remove initial/trailing newline from template literal | |
| console.log("--- Conversion Output ---"); | |
| const result = toManyougana(input); | |
| console.log(result); | |
| // Optional: verify correct output structure against expectations | |
| // console.log("\n--- Reverse Check (Optional) ---"); | |
| // console.log(fromManyougana(result)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment