pub fn hiragana_to_romaji(hiragana: &str) -> String {
let mut romaji = String::new();
let lower_input = hiragana.to_lowercase();
let chars: Vec<char> = lower_input.chars().collect();
let mut i = 0;
let kana_map: [(&str, &str); 119] = [
("あ", "a"), ("い", "i"), ("う", "u"), ("え", "e"), ("お", "o"),
("か", "ka"), ("き", "ki"), ("く", "ku"), ("け", "ke"), ("こ", "ko"),
("さ", "sa"), ("し", "shi"), ("す", "su"), ("せ", "se"), ("そ", "so"),
("た", "ta"), ("ち", "chi"), ("つ", "tsu"), ("て", "te"), ("と", "to"),
("な", "na"), ("に", "ni"), ("ぬ", "nu"), ("ね", "ne"), ("の", "no"),
("は", "ha"), ("ひ", "hi"), ("ふ", "fu"), ("へ", "he"), ("ほ", "ho"),
("ま", "ma"), ("み", "mi"), ("む", "mu"), ("め", "me"), ("も", "mo"),
("や", "ya"), ("ゆ", "yu"), ("よ", "yo"),
("ら", "ra"), ("り", "ri"), ("る", "ru"), ("れ", "re"), ("ろ", "ro"),
("わ", "wa"), ("を", "wo"), ("ん", "n"),
("が", "ga"), ("ぎ", "gi"), ("ぐ", "gu"), ("げ", "ge"), ("ご", "go"),
("ざ", "za"), ("じ", "ji"), ("ず", "zu"), ("ぜ", "ze"), ("ぞ", "zo"),
("だ", "da"), ("ぢ", "ji"), ("づ", "zu"), ("で", "de"), ("ど", "do"),
("ば", "ba"), ("び", "bi"), ("ぶ", "bu"), ("べ", "be"), ("ぼ", "bo"),
("ぱ", "pa"), ("ぴ", "pi"), ("ぷ", "pu"), ("ぺ", "pe"), ("ぽ", "po"),
("きゃ", "kya"), ("きゅ", "kyu"), ("きょ", "kyo"),
("しゃ", "sha"), ("しゅ", "shu"), ("しょ", "sho"),
("ちゃ", "cha"), ("ちゅ", "chu"), ("ちょ", "cho"),
("にゃ", "nya"), ("にゅ", "nyu"), ("にょ", "nyo"),
("ひゃ", "hya"), ("ひゅ", "hyu"), ("ひょ", "hyo"),
("みゃ", "mya"), ("みゅ", "myu"), ("みょ", "myo"),
("りゃ", "rya"), ("りゅ", "ryu"), ("りょ", "ro"),
("ぎゃ", "gya"), ("ぎゅ", "gyu"), ("ぎょ", "gyo"),
("じゃ", "ja"), ("じゅ", "ju"), ("じょ", "jo"),
("びゃ", "bya"), ("びゅ", "byu"), ("びょ", "byo"),
("ぴゃ", "pya"), ("ぴゅ", "pyu"), ("ぴょ", "pyo"),
("ぁ", "a"), ("ぃ", "i"), ("ぅ", "u"), ("ぇ", "e"), ("ぉ", "o"),
("。", ""), ("、", ""), ("ー", "-"), (" ", " "),
("!", ""), ("?", ""),
("ふぁ", "fa"), ("ふぃ", "fi"), ("ふぇ", "fe"), ("ふぉ", "fo"),
];
let to_hiragana = |c: char| -> char {
if c >= 'ァ' && c <= 'ヶ' {
(c as u32 - 0x60) as u8 as char
} else {
c
}
};
while i < chars.len() {
let current_char = to_hiragana(chars[i]);
if i + 1 < chars.len() {
let next_char = to_hiragana(chars[i+1]);
let mut two_chars = String::new();
two_chars.push(current_char);
two_chars.push(next_char);
if let Some((_, romaji_str)) = kana_map.iter().find(|(k, _)| **k == two_chars) {
romaji.push_str(romaji_str);
i += 2;
continue;
}
}
if current_char == 'っ' || current_char == 'ッ' {
if i + 1 < chars.len() {
let next_char = to_hiragana(chars[i+1]);
let next_kana_str = next_char.to_string();
if let Some((_, romaji_str)) = kana_map.iter().find(|(k, _)| *k == next_kana_str) {
if let Some(first_char) = romaji_str.chars().next() {
if !['a', 'i', 'u', 'e', 'o'].contains(&first_char) {
romaji.push(first_char);
}
}
}
}
i += 1;
continue;
}
let current_kana_str = current_char.to_string();
if let Some((_, romaji_str)) = kana_map.iter().find(|(k, _)| **k == current_kana_str) {
romaji.push_str(romaji_str);
i += 1;
continue;
}
i += 1;
}
let processed_romaji = romaji.replace("a-", "aa")
.replace("i-", "ii")
.replace("u-", "uu")
.replace("e-", "ee")
.replace("o-", "oo");
processed_romaji
.chars()
.filter(|c| c.is_ascii_alphabetic() || c.is_whitespace())
.collect::<String>()
}
pub fn split_romaji(input: &str) -> Vec<String> {
let mut result = Vec::new();
let lower_input = input.to_lowercase();
let chars: Vec<char> = lower_input.chars().collect();
let mut i = 0;
let two_syllables = [
"a", "i", "u", "e", "o", "ka", "ki", "ku", "ke", "ko",
"sa", "shi", "su", "se", "so", "ta", "chi", "tsu", "te", "to", "na", "ni", "nu", "ne", "no",
"ha", "hi", "fu", "he", "ho", "ma", "mi", "mu", "me", "mo",
"ya", "yu", "yo", "ra", "ri", "ru", "re", "ro",
"wa", "wo",
"ga", "gi", "gu", "ge", "go",
"za", "ji", "zu", "ze", "zo", "da", "de", "do",
"ba", "bi", "bu", "be", "bo",
"pa", "pi", "pu", "pe", "po",
"fa", "fi", "fe", "fo", "fu", "va", "vi", "vu", "ve", "vo",
];
let three_syllables = [
"kya", "kyu", "kyo", "sha", "shu", "sho", "cha", "chu", "cho",
"nya", "nyu", "nyo", "hya", "hyu", "hyo", "mya", "myu", "myo",
"rya", "ryu", "ryo", "gya", "gyu", "gyo", "ja", "ju", "jo",
"bya", "byu", "byo", "pya", "pyu", "pyo", "she", "che", "tsa",
"tsi", "tse", "tso", "fya", "fyu", "fyo"
];
let all_syllables: Vec<&str> = two_syllables.iter()
.chain(three_syllables.iter())
.cloned()
.collect();
while i < chars.len() {
let rest = &chars[i..];
let current_char = rest[0];
if !current_char.is_ascii_alphabetic() {
i += 1;
continue;
}
if rest.len() >= 2 {
let next_char = rest[1];
if next_char.is_ascii_alphabetic() && current_char == next_char && current_char != 'n' {
result.push(current_char.to_string());
i += 1; continue;
}
}
if rest.len() >= 3 {
let tri: String = rest[0..3].iter().collect();
if all_syllables.contains(&tri.as_str()) {
result.push(tri);
i += 3;
continue;
}
}
if rest.len() >= 2 {
let bi: String = rest[0..2].iter().collect();
if all_syllables.contains(&bi.as_str()) {
result.push(bi);
i += 2;
continue;
}
}
let single_syllable = current_char.to_string();
if all_syllables.contains(&single_syllable.as_str()) {
result.push(single_syllable);
i += 1;
continue;
}
eprintln!("Warning: Unrecognized Romaji character sequence at index {}: {}", i, current_char);
result.push(current_char.to_string());
i += 1;
}
result
}