Skip to main content

yuru_core/
normalize.rs

1use unicode_normalization::UnicodeNormalization;
2
3/// Applies NFKC normalization, lowercasing, and dash-width folding.
4pub fn normalize(text: &str) -> String {
5    text.nfkc()
6        .flat_map(char::to_lowercase)
7        .map(fold_width_compatible_char)
8        .collect()
9}
10
11/// Folds width-compatible dash and prolonged-sound variants to ASCII `-`.
12pub fn fold_width_compatible_char(ch: char) -> char {
13    match ch {
14        '-' | '\u{2010}' | '\u{2011}' | '\u{2012}' | '\u{2013}' | '\u{2014}' | '\u{2015}'
15        | '\u{2212}' | '\u{30a0}' | '\u{30fc}' | '\u{fe58}' | '\u{fe63}' | '\u{ff0d}'
16        | '\u{ff70}' => '-',
17        _ => ch,
18    }
19}
20
21/// Converts katakana characters in `text` to hiragana.
22pub fn katakana_to_hiragana(text: &str) -> String {
23    text.chars()
24        .map(|ch| {
25            if ('ァ'..='ヶ').contains(&ch) {
26                char::from_u32(ch as u32 - 0x60).unwrap_or(ch)
27            } else {
28                ch
29            }
30        })
31        .collect()
32}
33
34/// Converts hiragana characters in `text` to katakana.
35pub fn hiragana_to_katakana(text: &str) -> String {
36    text.chars()
37        .map(|ch| {
38            if ('ぁ'..='ゖ').contains(&ch) {
39                char::from_u32(ch as u32 + 0x60).unwrap_or(ch)
40            } else {
41                ch
42            }
43        })
44        .collect()
45}
46
47/// Returns true when `text` contains hiragana or katakana.
48pub fn contains_kana(text: &str) -> bool {
49    text.chars()
50        .any(|ch| ('ぁ'..='ゖ').contains(&ch) || ('ァ'..='ヶ').contains(&ch))
51}
52
53#[cfg(test)]
54mod tests;