| 1 | /// Windows LCIDs keyed by BCP-47 language, `language-Script` or `language-REGION`; a bare |
| 2 | /// language maps to the locale Windows picks for it. |
| 3 | const LCIDS: &[(&str, u32)] = &[ |
| 4 | ("af", 1078), |
| 5 | ("am", 1118), |
| 6 | ("ar", 1025), |
| 7 | ("ar-EG", 3073), |
| 8 | ("az", 1068), |
| 9 | ("be", 1059), |
| 10 | ("bg", 1026), |
| 11 | ("bn", 1093), |
| 12 | ("bn-BD", 2117), |
| 13 | ("ca", 1027), |
| 14 | ("cs", 1029), |
| 15 | ("cy", 1106), |
| 16 | ("da", 1030), |
| 17 | ("de", 1031), |
| 18 | ("de-AT", 3079), |
| 19 | ("de-CH", 2055), |
| 20 | ("de-LI", 5127), |
| 21 | ("de-LU", 4103), |
| 22 | ("el", 1032), |
| 23 | ("en", 1033), |
| 24 | ("en-AU", 3081), |
| 25 | ("en-CA", 4105), |
| 26 | ("en-GB", 2057), |
| 27 | ("en-IE", 6153), |
| 28 | ("en-IN", 16393), |
| 29 | ("en-NZ", 5129), |
| 30 | ("en-SG", 18441), |
| 31 | ("en-ZA", 7177), |
| 32 | ("es", 3082), |
| 33 | ("es-MX", 2058), |
| 34 | ("es-US", 21514), |
| 35 | ("et", 1061), |
| 36 | ("eu", 1069), |
| 37 | ("fa", 1065), |
| 38 | ("fi", 1035), |
| 39 | ("fil", 1124), |
| 40 | ("fr", 1036), |
| 41 | ("fr-BE", 2060), |
| 42 | ("fr-CA", 3084), |
| 43 | ("fr-CH", 4108), |
| 44 | ("gl", 1110), |
| 45 | ("gu", 1095), |
| 46 | ("he", 1037), |
| 47 | ("hi", 1081), |
| 48 | ("hr", 1050), |
| 49 | ("hu", 1038), |
| 50 | ("hy", 1067), |
| 51 | ("id", 1057), |
| 52 | ("is", 1039), |
| 53 | ("it", 1040), |
| 54 | ("it-CH", 2064), |
| 55 | ("ja", 1041), |
| 56 | ("ka", 1079), |
| 57 | ("kk", 1087), |
| 58 | ("km", 1107), |
| 59 | ("kn", 1099), |
| 60 | ("ko", 1042), |
| 61 | ("lo", 1108), |
| 62 | ("lt", 1063), |
| 63 | ("lv", 1062), |
| 64 | ("mk", 1071), |
| 65 | ("ml", 1100), |
| 66 | ("mn", 1104), |
| 67 | ("mr", 1102), |
| 68 | ("ms", 1086), |
| 69 | ("my", 1109), |
| 70 | ("nb", 1044), |
| 71 | ("ne", 1121), |
| 72 | ("nl", 1043), |
| 73 | ("nl-BE", 2067), |
| 74 | ("nn", 2068), |
| 75 | ("no", 1044), |
| 76 | ("pa", 1094), |
| 77 | ("pl", 1045), |
| 78 | ("pt", 1046), |
| 79 | ("pt-PT", 2070), |
| 80 | ("ro", 1048), |
| 81 | ("ru", 1049), |
| 82 | ("si", 1115), |
| 83 | ("sk", 1051), |
| 84 | ("sl", 1060), |
| 85 | ("sq", 1052), |
| 86 | ("sr", 10266), |
| 87 | ("sr-Latn", 9242), |
| 88 | ("sv", 1053), |
| 89 | ("sv-FI", 2077), |
| 90 | ("sw", 1089), |
| 91 | ("ta", 1097), |
| 92 | ("te", 1098), |
| 93 | ("th", 1054), |
| 94 | ("tr", 1055), |
| 95 | ("uk", 1058), |
| 96 | ("ur", 1056), |
| 97 | ("vi", 1066), |
| 98 | ("zh", 2052), |
| 99 | ("zh-HK", 3076), |
| 100 | ("zh-Hant", 1028), |
| 101 | ("zh-MO", 5124), |
| 102 | ("zh-SG", 4100), |
| 103 | ("zh-TW", 1028), |
| 104 | ]; |
| 105 | |
| 106 | /// en-US, what OneNote records when nothing better is known. |
| 107 | pub(crate) const EN_US: u32 = 1033; |
| 108 | |
| 109 | /// The Windows LCID of a BCP-47 tag such as an input source's language (`de`, `en-GB`, |
| 110 | /// `zh-Hans`), matched by region, then script, then language, else en-US. |
| 111 | pub fn lcid(tag: &str) -> u32 { |
| 112 | let mut subtags = tag.split(['-', '_']); |
| 113 | let language = subtags.next().unwrap_or_default(); |
| 114 | let (mut script, mut region) = (None, None); |
| 115 | for subtag in subtags { |
| 116 | match subtag.len() { |
| 117 | 4 => script = Some(subtag), |
| 118 | 2 | 3 => region = Some(subtag), |
| 119 | _ => {} |
| 120 | } |
| 121 | } |
| 122 | [region, script] |
| 123 | .into_iter() |
| 124 | .flatten() |
| 125 | .map(|subtag| format!("{language}-{subtag}")) |
| 126 | .chain([language.to_owned()]) |
| 127 | .find_map(|key| { |
| 128 | LCIDS |
| 129 | .iter() |
| 130 | .find(|(tag, _)| tag.eq_ignore_ascii_case(&key)) |
| 131 | .map(|(_, lcid)| *lcid) |
| 132 | }) |
| 133 | .unwrap_or(EN_US) |
| 134 | } |
| 135 | |
| 136 | /// The BCP-47 tag of a Windows LCID; a bare language stands for the locale Windows picks |
| 137 | /// for it. None for LCIDs outside the table, such as math's. |
| 138 | pub(crate) fn tag(lcid: u32) -> Option<&'static str> { |
| 139 | LCIDS |
| 140 | .iter() |
| 141 | .find(|(_, known)| *known == lcid) |
| 142 | .map(|(tag, _)| *tag) |
| 143 | } |
| 144 | |
| 145 | #[cfg(test)] |
| 146 | mod tests { |
| 147 | use super::*; |
| 148 | |
| 149 | #[test] |
| 150 | fn tags_map_to_windows_lcids() { |
| 151 | for (tag, lcid) in [ |
| 152 | ("en", 1033), |
| 153 | ("en-US", 1033), |
| 154 | ("en_GB", 2057), |
| 155 | ("de", 1031), |
| 156 | ("DE-at", 3079), |
| 157 | ("fr-FR", 1036), |
| 158 | ("es-419", 3082), |
| 159 | ("ja-JP", 1041), |
| 160 | ("zh-Hans", 2052), |
| 161 | ("zh-Hant", 1028), |
| 162 | ("zh-Hant-HK", 3076), |
| 163 | ("zh-Hans-SG", 4100), |
| 164 | ("sr-Latn-RS", 9242), |
| 165 | ("emoji", EN_US), |
| 166 | ("", EN_US), |
| 167 | ] { |
| 168 | assert_eq!(super::lcid(tag), lcid, "{tag}"); |
| 169 | } |
| 170 | for (lcid, tag) in [(1033, Some("en")), (2057, Some("en-GB")), (0x1007f, None)] { |
| 171 | assert_eq!(super::tag(lcid), tag); |
| 172 | } |
| 173 | } |
| 174 | } |