using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.UserSettings; using ToolGood.Words.Pinyin; namespace Flow.Launcher.Infrastructure { public class PinyinAlphabet : IAlphabet { private readonly ConcurrentDictionary _pinyinCache = new(); private readonly Settings _settings; public PinyinAlphabet() { _settings = Ioc.Default.GetRequiredService(); } public bool ShouldTranslate(string stringToTranslate) { return _settings.UseDoublePinyin ? (!WordsHelper.HasChinese(stringToTranslate) && stringToTranslate.Length % 2 == 0) : !WordsHelper.HasChinese(stringToTranslate); } public (string translation, TranslationMapping map) Translate(string content) { if (_settings.ShouldUsePinyin) { if (!_pinyinCache.TryGetValue(content, out var value)) { return BuildCacheFromContent(content); } else { return value; } } return (content, null); } private (string translation, TranslationMapping map) BuildCacheFromContent(string content) { if (!WordsHelper.HasChinese(content)) { return (content, null); } var resultList = WordsHelper.GetPinyinList(content); var resultBuilder = new StringBuilder(); var map = new TranslationMapping(); var pre = false; for (var i = 0; i < resultList.Length; i++) { if (content[i] >= 0x3400 && content[i] <= 0x9FD5) { string dp = _settings.UseDoublePinyin ? ToDoublePin(resultList[i]) : resultList[i]; map.AddNewIndex(i, resultBuilder.Length, dp.Length + 1); resultBuilder.Append(' '); resultBuilder.Append(dp); pre = true; } else { if (pre) { pre = false; resultBuilder.Append(' '); } resultBuilder.Append(resultList[i]); } } map.endConstruct(); var key = resultBuilder.ToString(); return _pinyinCache[content] = (key, map); } #region Double Pinyin private static readonly ReadOnlyDictionary special = new(new Dictionary(){ {"A", "aa"}, {"Ai", "ai"}, {"An", "an"}, {"Ang", "ah"}, {"Ao", "ao"}, {"E", "ee"}, {"Ei", "ei"}, {"En", "en"}, {"Er", "er"}, {"O", "oo"}, {"Ou", "ou"} }); private static readonly ReadOnlyDictionary first = new(new Dictionary(){ {"Ch", "i"}, {"Sh", "u"}, {"Zh", "v"} }); private static readonly ReadOnlyDictionary second = new(new Dictionary() { {"ua", "x"}, {"ei", "w"}, {"e", "e"}, {"ou", "z"}, {"iu", "q"}, {"ve", "t"}, {"ue", "t"}, {"u", "u"}, {"i", "i"}, {"o", "o"}, {"uo", "o"}, {"ie", "p"}, {"a", "a"}, {"ong", "s"}, {"iong", "s"}, {"ai", "d"}, {"ing", "k"}, {"uai", "k"}, {"ang", "h"}, {"uan", "r"}, {"an", "j"}, {"en", "f"}, {"ia", "x"}, {"iang", "l"}, {"uang", "l"}, {"eng", "g"}, {"in", "b"}, {"ao", "c"}, {"v", "v"}, {"ui", "v"}, {"un", "y"}, {"iao", "n"}, {"ian", "m"} }); private static string ToDoublePin(string fullPinyin) { // Assuming s is valid var fullPinyinSpan = fullPinyin.AsSpan(); var doublePin = new StringBuilder(); // Handle special cases (a, o, e) if (fullPinyin.Length <= 3 && (fullPinyinSpan[0] == 'a' || fullPinyinSpan[0] == 'e' || fullPinyinSpan[0] == 'o')) { if (special.TryGetValue(fullPinyin, out var value)) { return value; } } // Check for initials that are two characters long (zh, ch, sh) if (fullPinyin.Length >= 2) { var firstTwo = fullPinyinSpan[..2]; var firstTwoString = firstTwo.ToString(); if (first.ContainsKey(firstTwoString)) { doublePin.Append(firstTwoString); var lastTwo = fullPinyinSpan[2..]; var lastTwoString = lastTwo.ToString(); if (second.TryGetValue(lastTwoString, out var tmp)) { doublePin.Append(tmp); } else { doublePin.Append(lastTwo); } } } // Handle single-character initials else { doublePin.Append(fullPinyinSpan[0]); var lastOne = fullPinyinSpan[1..]; var lastOneString = lastOne.ToString(); if (second.TryGetValue(lastOneString, out var tmp)) { doublePin.Append(tmp); } else { doublePin.Append(lastOne); } } return doublePin.ToString(); } #endregion } }