-
-
Notifications
You must be signed in to change notification settings - Fork 456
Double pinyin query #2427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+327
−169
Merged
Double pinyin query #2427
Changes from 52 commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
3abd05f
Implement double pinyin
VictoriousRaptor fb66353
Test double pinyin
VictoriousRaptor f6ae71a
Only convert to double pinyin when meeting Chinese
VictoriousRaptor e5285b1
Temp: compatibility with full pinyin option
VictoriousRaptor 99ff3b2
Fix wrong condition
VictoriousRaptor 46d49d8
Only translate when string is double pinyin
VictoriousRaptor b1cb852
Extract classes
VictoriousRaptor 6807afb
Remove unused alphabet arg in PublicAPIInstance
VictoriousRaptor a2efa11
Merge DoublePinAlphabet logic
VictoriousRaptor 12c4e37
Developing
VictoriousRaptor f673000
Fix ShouldTranslate()
VictoriousRaptor b10a6e1
Capitalize first letter
VictoriousRaptor b816d1b
Remove unused key in pinyin alphabet
VictoriousRaptor c8a9e5e
Merge branch 'dev' into double-pin
Jack251970 9e8a950
Fix build issue & Improve code quality
Jack251970 a8a305f
Improve code quality
Jack251970 5be732d
Use var when neccessary
Jack251970 1f458d3
Fix typos & Code quality
Jack251970 3f45c6a
Merge branch 'dev' into double-pin
Jack251970 4b7db3c
Make function static
Jack251970 f5fd6b5
Use ReadOnlySpan instead
Jack251970 2cf6f81
Merge branch 'dev' into double-pin
Jack251970 e07b33c
Merge branch 'dev' into double-pin
VictoriousRaptor 672649c
Merge branch 'dev' into double-pin
VictoriousRaptor 74d5499
Delete Flow.Launcher/Properties/Resources.fr-FR.resx
VictoriousRaptor 78ffeb8
Delete Flow.Launcher/Properties/Resources.he-IL.resx
VictoriousRaptor 3eb5fea
remove on tag deployment & change NuGet publish to on master push
jjw24 8f43de6
Support Msix FireFox bookmarks
Jack251970 281e042
Fix IsRelative logic.
Jack251970 ceb05e8
Add error handling for directory operation
Jack251970 aaa8e4d
Use AddRange
Jack251970 44304f2
Change code comments
Jack251970 16fd256
Fix typos
Jack251970 fba42ff
Fix transaltion logic
VictoriousRaptor b31a740
Simple refactor
VictoriousRaptor 818aac7
Use lookup table to translate full pinyin to double pinyin
VictoriousRaptor 31cd894
Compress json
VictoriousRaptor 3c2581d
Merge branch 'dev' into double-pin
VictoriousRaptor 4fb2e3d
Fix translation mapping logic
VictoriousRaptor 4b6231b
Extract methods for readability
VictoriousRaptor 64a3aa5
Fix Off-by-one in index mapping when consecutive Chinese chars
VictoriousRaptor b189595
Add OnPropertyChanged() for double pinyin properties
VictoriousRaptor d2dc307
Fix logic of ShouldTranslate()
VictoriousRaptor f064a81
Merge branch 'dev' into double-pin
VictoriousRaptor 1bc80d5
Fix translated length
VictoriousRaptor 64f3738
Merge branch 'dev' into double-pin
Jack251970 100f753
Fix ShouldTranslate() logic
VictoriousRaptor d9e89ad
Refactor TranslationMapping class
VictoriousRaptor d537ce2
Fix init issue
VictoriousRaptor fdbb183
Remove readonly to reload correctly
VictoriousRaptor ebcd7d5
Fix ShouldTranslate() logic
VictoriousRaptor 1302021
Add UI for double pinyin options
VictoriousRaptor 3f18627
Fix zero boundary condition in MapToOriginalIndex
VictoriousRaptor 5cd7ae7
Fix typo
VictoriousRaptor 4c56021
More specific exception types for better error handling
VictoriousRaptor 50de9ba
Merge branch 'dev' into double-pin
VictoriousRaptor 27002c5
Update doc
VictoriousRaptor 43f7cec
Improve code quality
Jack251970 44d9eb8
Change default to false BEFORE RELEASE
Jack251970 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| namespace Flow.Launcher.Infrastructure | ||
| { | ||
| /// <summary> | ||
| /// Translate a language to English letters using a given rule. | ||
| /// </summary> | ||
| public interface IAlphabet | ||
| { | ||
| /// <summary> | ||
| /// Translate a string to English letters, using a given rule. | ||
| /// </summary> | ||
| /// <param name="stringToTranslate">String to translate.</param> | ||
| /// <returns></returns> | ||
| public (string translation, TranslationMapping map) Translate(string stringToTranslate); | ||
|
|
||
| /// <summary> | ||
| /// Determine if a string can be translated to English letter with this Alphabet. | ||
| /// </summary> | ||
| /// <param name="stringToTranslate">String to translate.</param> | ||
| /// <returns></returns> | ||
| public bool ShouldTranslate(string stringToTranslate); | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,209 +1,148 @@ | ||
| using System; | ||
| using System.Collections.Concurrent; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Collections.ObjectModel; | ||
| using System.IO; | ||
| using System.Text; | ||
| using JetBrains.Annotations; | ||
| using System.Text.Json; | ||
| using CommunityToolkit.Mvvm.DependencyInjection; | ||
| using Flow.Launcher.Infrastructure.UserSettings; | ||
| using ToolGood.Words.Pinyin; | ||
| using CommunityToolkit.Mvvm.DependencyInjection; | ||
| using Flow.Launcher.Infrastructure.Logger; | ||
|
|
||
| namespace Flow.Launcher.Infrastructure | ||
| { | ||
| public class TranslationMapping | ||
| public class PinyinAlphabet : IAlphabet | ||
| { | ||
| private bool constructed; | ||
| private ConcurrentDictionary<string, (string translation, TranslationMapping map)> _pinyinCache = | ||
| new(); | ||
|
|
||
| private List<int> originalIndexs = new List<int>(); | ||
| private List<int> translatedIndexs = new List<int>(); | ||
| private int translatedLength = 0; | ||
| private readonly Settings _settings; | ||
|
|
||
| public string key { get; private set; } | ||
| private ReadOnlyDictionary<string, string> currentDoublePinyinTable; | ||
|
|
||
| public void setKey(string key) | ||
| public PinyinAlphabet() | ||
| { | ||
| this.key = key; | ||
| _settings = Ioc.Default.GetRequiredService<Settings>(); | ||
| LoadDoublePinyinTable(); | ||
|
|
||
| _settings.PropertyChanged += (sender, e) => | ||
| { | ||
| if (e.PropertyName == nameof(Settings.UseDoublePinyin) || | ||
| e.PropertyName == nameof(Settings.DoublePinyinSchema)) | ||
| { | ||
| Reload(); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| public void AddNewIndex(int originalIndex, int translatedIndex, int length) | ||
| public void Reload() | ||
| { | ||
| if (constructed) | ||
| throw new InvalidOperationException("Mapping shouldn't be changed after constructed"); | ||
|
|
||
| originalIndexs.Add(originalIndex); | ||
| translatedIndexs.Add(translatedIndex); | ||
| translatedIndexs.Add(translatedIndex + length); | ||
| translatedLength += length - 1; | ||
| LoadDoublePinyinTable(); | ||
| _pinyinCache.Clear(); | ||
| } | ||
|
|
||
| public int MapToOriginalIndex(int translatedIndex) | ||
| private void CreateDoublePinyinTableFromStream(Stream jsonStream) | ||
| { | ||
| if (translatedIndex > translatedIndexs.Last()) | ||
| return translatedIndex - translatedLength - 1; | ||
|
|
||
| int lowerBound = 0; | ||
| int upperBound = originalIndexs.Count - 1; | ||
|
|
||
| int count = 0; | ||
|
|
||
| // Corner case handle | ||
| if (translatedIndex < translatedIndexs[0]) | ||
| return translatedIndex; | ||
| if (translatedIndex > translatedIndexs.Last()) | ||
| Dictionary<string, Dictionary<string, string>> table = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(jsonStream); | ||
| string schemaKey = _settings.DoublePinyinSchema.ToString(); // Convert enum to string | ||
| if (!table.TryGetValue(schemaKey, out var value)) | ||
| { | ||
| int indexDef = 0; | ||
| for (int k = 0; k < originalIndexs.Count; k++) | ||
| { | ||
| indexDef += translatedIndexs[k * 2 + 1] - translatedIndexs[k * 2]; | ||
| } | ||
|
|
||
| return translatedIndex - indexDef - 1; | ||
| throw new InvalidOperationException("DoublePinyinSchema is invalid or double pinyin table is broken."); | ||
| } | ||
| currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(value); | ||
| } | ||
|
|
||
| // Binary Search with Range | ||
| for (int i = originalIndexs.Count / 2;; count++) | ||
| private void LoadDoublePinyinTable() | ||
| { | ||
| if (_settings.UseDoublePinyin) | ||
| { | ||
| if (translatedIndex < translatedIndexs[i * 2]) | ||
| var tablePath = Path.Join(AppContext.BaseDirectory, "Resources", "double_pinyin.json"); | ||
| try | ||
| { | ||
| // move to lower middle | ||
| upperBound = i; | ||
| i = (i + lowerBound) / 2; | ||
| using var fs = File.OpenRead(tablePath); | ||
| CreateDoublePinyinTableFromStream(fs); | ||
| } | ||
| else if (translatedIndex > translatedIndexs[i * 2 + 1] - 1) | ||
| catch (System.Exception e) | ||
| { | ||
| lowerBound = i; | ||
| // move to upper middle | ||
| // due to floor of integer division, move one up on corner case | ||
| i = (i + upperBound + 1) / 2; | ||
| } | ||
| else | ||
| return originalIndexs[i]; | ||
|
|
||
| if (upperBound - lowerBound <= 1 && | ||
| translatedIndex > translatedIndexs[lowerBound * 2 + 1] && | ||
| translatedIndex < translatedIndexs[upperBound * 2]) | ||
| { | ||
| int indexDef = 0; | ||
|
|
||
| for (int j = 0; j < upperBound; j++) | ||
| { | ||
| indexDef += translatedIndexs[j * 2 + 1] - translatedIndexs[j * 2]; | ||
| } | ||
|
|
||
| return translatedIndex - indexDef - 1; | ||
| Log.Exception(nameof(PinyinAlphabet), "Failed to load double pinyin table from file: " + tablePath, e); | ||
| currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>()); | ||
| } | ||
| } | ||
|
|
||
| public void endConstruct() | ||
| { | ||
| if (constructed) | ||
| throw new InvalidOperationException("Mapping has already been constructed"); | ||
| constructed = true; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Translate a language to English letters using a given rule. | ||
| /// </summary> | ||
| public interface IAlphabet | ||
| { | ||
| /// <summary> | ||
| /// Translate a string to English letters, using a given rule. | ||
| /// </summary> | ||
| /// <param name="stringToTranslate">String to translate.</param> | ||
| /// <returns></returns> | ||
| public (string translation, TranslationMapping map) Translate(string stringToTranslate); | ||
|
|
||
| /// <summary> | ||
| /// Determine if a string can be translated to English letter with this Alphabet. | ||
| /// </summary> | ||
| /// <param name="stringToTranslate">String to translate.</param> | ||
| /// <returns></returns> | ||
| public bool CanBeTranslated(string stringToTranslate); | ||
| } | ||
|
|
||
| public class PinyinAlphabet : IAlphabet | ||
| { | ||
| private ConcurrentDictionary<string, (string translation, TranslationMapping map)> _pinyinCache = | ||
| new ConcurrentDictionary<string, (string translation, TranslationMapping map)>(); | ||
|
|
||
| private Settings _settings; | ||
|
|
||
| public PinyinAlphabet() | ||
| { | ||
| Initialize(Ioc.Default.GetRequiredService<Settings>()); | ||
| } | ||
|
|
||
| private void Initialize([NotNull] Settings settings) | ||
| { | ||
| _settings = settings ?? throw new ArgumentNullException(nameof(settings)); | ||
| } | ||
|
|
||
| public bool CanBeTranslated(string stringToTranslate) | ||
| public bool ShouldTranslate(string stringToTranslate) | ||
| { | ||
| return WordsHelper.HasChinese(stringToTranslate); | ||
| // If a string has Chinese characters, we don't need to translate it to pinyin. | ||
| return _settings.ShouldUsePinyin && !WordsHelper.HasChinese(stringToTranslate); | ||
| } | ||
|
|
||
| public (string translation, TranslationMapping map) Translate(string content) | ||
| { | ||
| if (_settings.ShouldUsePinyin) | ||
| { | ||
| if (!_pinyinCache.ContainsKey(content)) | ||
| { | ||
| return BuildCacheFromContent(content); | ||
| } | ||
| else | ||
| { | ||
| return _pinyinCache[content]; | ||
| } | ||
| } | ||
| return (content, null); | ||
| if (!_settings.ShouldUsePinyin || !WordsHelper.HasChinese(content)) | ||
| return (content, null); | ||
|
|
||
| return _pinyinCache.TryGetValue(content, out var value) | ||
| ? value | ||
| : BuildCacheFromContent(content); | ||
| } | ||
|
|
||
| private (string translation, TranslationMapping map) BuildCacheFromContent(string content) | ||
| { | ||
| if (WordsHelper.HasChinese(content)) | ||
| { | ||
| var resultList = WordsHelper.GetPinyinList(content); | ||
| var resultList = WordsHelper.GetPinyinList(content); | ||
|
|
||
| StringBuilder resultBuilder = new StringBuilder(); | ||
| TranslationMapping map = new TranslationMapping(); | ||
| var resultBuilder = new StringBuilder(); | ||
| var map = new TranslationMapping(); | ||
|
|
||
| bool pre = false; | ||
| var previousIsChinese = false; | ||
VictoriousRaptor marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| for (int i = 0; i < resultList.Length; i++) | ||
| for (var i = 0; i < resultList.Length; i++) | ||
| { | ||
| if (content[i] >= 0x3400 && content[i] <= 0x9FD5) | ||
| { | ||
| if (content[i] >= 0x3400 && content[i] <= 0x9FD5) | ||
| string translated = _settings.UseDoublePinyin ? ToDoublePin(resultList[i]) : resultList[i]; | ||
| if (i > 0) | ||
| { | ||
| map.AddNewIndex(i, resultBuilder.Length, resultList[i].Length + 1); | ||
| resultBuilder.Append(' '); | ||
| resultBuilder.Append(resultList[i]); | ||
| pre = true; | ||
| } | ||
| else | ||
| map.AddNewIndex(resultBuilder.Length, translated.Length); | ||
| resultBuilder.Append(translated); | ||
| previousIsChinese = true; | ||
| } | ||
| else | ||
| { | ||
| if (previousIsChinese) | ||
| { | ||
| if (pre) | ||
| { | ||
| pre = false; | ||
| resultBuilder.Append(' '); | ||
| } | ||
|
|
||
| resultBuilder.Append(resultList[i]); | ||
| previousIsChinese = false; | ||
| resultBuilder.Append(' '); | ||
| } | ||
| map.AddNewIndex(resultBuilder.Length, resultList[i].Length); | ||
| resultBuilder.Append(resultList[i]); | ||
| } | ||
| } | ||
|
|
||
| map.endConstruct(); | ||
| map.endConstruct(); | ||
|
|
||
| var key = resultBuilder.ToString(); | ||
| map.setKey(key); | ||
| var key = resultBuilder.ToString(); | ||
|
|
||
| return _pinyinCache[content] = (key, map); | ||
| } | ||
| else | ||
| return _pinyinCache[content] = (key, map); | ||
| } | ||
|
|
||
| #region Double Pinyin | ||
|
|
||
| private string ToDoublePin(string fullPinyin) | ||
| { | ||
| if (currentDoublePinyinTable.TryGetValue(fullPinyin, out var doublePinyinValue)) | ||
| { | ||
| return (content, null); | ||
| return doublePinyinValue; | ||
| } | ||
| return fullPinyin; | ||
| } | ||
|
|
||
| #endregion | ||
| } | ||
| } | ||
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
|
|
||
| namespace Flow.Launcher.Infrastructure | ||
| { | ||
| public class TranslationMapping | ||
| { | ||
| private bool constructed; | ||
|
|
||
| // Asssuming one original item maps to multi translated items | ||
VictoriousRaptor marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // list[i] is the last translated index + 1 of original index i | ||
| private readonly List<int> originalToTranslated = new List<int>(); | ||
|
|
||
| public void AddNewIndex(int translatedIndex, int length) | ||
| { | ||
| if (constructed) | ||
| throw new InvalidOperationException("Mapping shouldn't be changed after constructed"); | ||
|
|
||
| originalToTranslated.Add(translatedIndex + length); | ||
| } | ||
|
|
||
| public int MapToOriginalIndex(int translatedIndex) | ||
| { | ||
| int loc = originalToTranslated.BinarySearch(translatedIndex); | ||
|
|
||
| return loc > 0 ? loc : ~loc; | ||
| } | ||
VictoriousRaptor marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| public void endConstruct() | ||
| { | ||
| if (constructed) | ||
| throw new InvalidOperationException("Mapping has already been constructed"); | ||
| constructed = true; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.