@@ -14,11 +14,8 @@ namespace Flow.Launcher.Infrastructure
1414{
1515 public class PinyinAlphabet : IAlphabet
1616 {
17- private ConcurrentDictionary < string , ( string translation , TranslationMapping map ) > _pinyinCache =
18- new ( ) ;
19-
17+ private readonly ConcurrentDictionary < string , ( string translation , TranslationMapping map ) > _pinyinCache = new ( ) ;
2018 private readonly Settings _settings ;
21-
2219 private ReadOnlyDictionary < string , string > currentDoublePinyinTable ;
2320
2421 public PinyinAlphabet ( )
@@ -44,105 +41,145 @@ public void Reload()
4441
4542 private void CreateDoublePinyinTableFromStream ( Stream jsonStream )
4643 {
47- Dictionary < string , Dictionary < string , string > > table = JsonSerializer . Deserialize < Dictionary < string , Dictionary < string , string > > > ( jsonStream ) ;
48- string schemaKey = _settings . DoublePinyinSchema . ToString ( ) ; // Convert enum to string
49- if ( ! table . TryGetValue ( schemaKey , out var value ) )
44+ var table = JsonSerializer . Deserialize < Dictionary < string , Dictionary < string , string > > > ( jsonStream ) ;
45+ if ( table == null )
46+ {
47+ throw new InvalidOperationException ( "Failed to deserialize double pinyin table: result is null" ) ;
48+ }
49+
50+ var schemaKey = _settings . DoublePinyinSchema . ToString ( ) ;
51+ if ( ! table . TryGetValue ( schemaKey , out var schemaDict ) )
5052 {
51- throw new ArgumentException ( "DoublePinyinSchema is invalid or double pinyin table is broken." ) ;
53+ throw new ArgumentException ( $ "DoublePinyinSchema ' { schemaKey } ' is invalid or double pinyin table is broken.") ;
5254 }
53- currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( value ) ;
55+
56+ currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( schemaDict ) ;
5457 }
5558
5659 private void LoadDoublePinyinTable ( )
5760 {
58- if ( _settings . UseDoublePinyin )
61+ if ( ! _settings . UseDoublePinyin )
5962 {
60- var tablePath = Path . Join ( AppContext . BaseDirectory , "Resources" , "double_pinyin.json" ) ;
61- try
62- {
63- using var fs = File . OpenRead ( tablePath ) ;
64- CreateDoublePinyinTableFromStream ( fs ) ;
65- }
66- catch ( System . Exception e )
67- {
68- Log . Exception ( nameof ( PinyinAlphabet ) , "Failed to load double pinyin table from file: " + tablePath , e ) ;
69- currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( new Dictionary < string , string > ( ) ) ;
70- }
63+ currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( new Dictionary < string , string > ( ) ) ;
64+ return ;
65+ }
66+
67+ var tablePath = Path . Combine ( AppContext . BaseDirectory , "Resources" , "double_pinyin.json" ) ;
68+ try
69+ {
70+ using var fs = File . OpenRead ( tablePath ) ;
71+ CreateDoublePinyinTableFromStream ( fs ) ;
7172 }
72- else
73+ catch ( FileNotFoundException e )
7374 {
75+ Log . Exception ( nameof ( PinyinAlphabet ) , $ "Double pinyin table file not found: { tablePath } ", e ) ;
76+ currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( new Dictionary < string , string > ( ) ) ;
77+ }
78+ catch ( DirectoryNotFoundException e )
79+ {
80+ Log . Exception ( nameof ( PinyinAlphabet ) , $ "Directory not found for double pinyin table: { tablePath } ", e ) ;
81+ currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( new Dictionary < string , string > ( ) ) ;
82+ }
83+ catch ( UnauthorizedAccessException e )
84+ {
85+ Log . Exception ( nameof ( PinyinAlphabet ) , $ "Access denied to double pinyin table: { tablePath } ", e ) ;
86+ currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( new Dictionary < string , string > ( ) ) ;
87+ }
88+ catch ( System . Exception e )
89+ {
90+ Log . Exception ( nameof ( PinyinAlphabet ) , $ "Failed to load double pinyin table from file: { tablePath } ", e ) ;
7491 currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( new Dictionary < string , string > ( ) ) ;
7592 }
7693 }
7794
7895 public bool ShouldTranslate ( string stringToTranslate )
7996 {
80- // If a string has Chinese characters, we don't need to translate it to pinyin.
81- return _settings . ShouldUsePinyin && ! WordsHelper . HasChinese ( stringToTranslate ) ;
97+ // If the query (stringToTranslate) does NOT contain Chinese characters,
98+ // we should translate the target string to pinyin for matching
99+ return _settings . ShouldUsePinyin && ! ContainsChinese ( stringToTranslate ) ;
82100 }
83101
84102 public ( string translation , TranslationMapping map ) Translate ( string content )
85103 {
86- if ( ! _settings . ShouldUsePinyin || ! WordsHelper . HasChinese ( content ) )
104+ if ( ! _settings . ShouldUsePinyin || ! ContainsChinese ( content ) )
87105 return ( content , null ) ;
88106
89- return _pinyinCache . TryGetValue ( content , out var value )
90- ? value
91- : BuildCacheFromContent ( content ) ;
107+ return _pinyinCache . TryGetValue ( content , out var cached ) ? cached : BuildCacheFromContent ( content ) ;
92108 }
93109
94110 private ( string translation , TranslationMapping map ) BuildCacheFromContent ( string content )
95111 {
96112 var resultList = WordsHelper . GetPinyinList ( content ) ;
97-
98- var resultBuilder = new StringBuilder ( ) ;
113+ var resultBuilder = new StringBuilder ( _settings . UseDoublePinyin ? 3 : 4 ) ; // Pre-allocate with estimated capacity
99114 var map = new TranslationMapping ( ) ;
100115
101116 var previousIsChinese = false ;
102117
103118 for ( var i = 0 ; i < resultList . Length ; i ++ )
104119 {
105- if ( content [ i ] >= 0x3400 && content [ i ] <= 0x9FD5 )
120+ if ( IsChineseCharacter ( content [ i ] ) )
106121 {
107- string translated = _settings . UseDoublePinyin ? ToDoublePin ( resultList [ i ] ) : resultList [ i ] ;
122+ var translated = _settings . UseDoublePinyin ? ToDoublePinyin ( resultList [ i ] ) : resultList [ i ] ;
123+
108124 if ( i > 0 )
109125 {
110126 resultBuilder . Append ( ' ' ) ;
111127 }
128+
112129 map . AddNewIndex ( resultBuilder . Length , translated . Length ) ;
113130 resultBuilder . Append ( translated ) ;
114131 previousIsChinese = true ;
115132 }
116133 else
117134 {
135+ // Add space after Chinese characters before non-Chinese characters
118136 if ( previousIsChinese )
119137 {
120138 previousIsChinese = false ;
121139 resultBuilder . Append ( ' ' ) ;
122140 }
141+
123142 map . AddNewIndex ( resultBuilder . Length , resultList [ i ] . Length ) ;
124143 resultBuilder . Append ( resultList [ i ] ) ;
125144 }
126145 }
127146
128- map . endConstruct ( ) ;
147+ map . EndConstruct ( ) ;
129148
130- var key = resultBuilder . ToString ( ) ;
131-
132- return _pinyinCache [ content ] = ( key , map ) ;
149+ var translation = resultBuilder . ToString ( ) ;
150+ var result = ( translation , map ) ;
151+
152+ return _pinyinCache [ content ] = result ;
133153 }
134154
135- #region Double Pinyin
136-
137- private string ToDoublePin ( string fullPinyin )
155+ /// <summary>
156+ /// Optimized Chinese character detection using the comprehensive CJK Unicode ranges
157+ /// </summary>
158+ private static bool ContainsChinese ( ReadOnlySpan < char > text )
138159 {
139- if ( currentDoublePinyinTable . TryGetValue ( fullPinyin , out var doublePinyinValue ) )
160+ foreach ( var c in text )
140161 {
141- return doublePinyinValue ;
162+ if ( IsChineseCharacter ( c ) )
163+ return true ;
142164 }
143- return fullPinyin ;
165+ return false ;
144166 }
145167
146- #endregion
168+ /// <summary>
169+ /// Check if a character is a Chinese character using comprehensive Unicode ranges
170+ /// Covers CJK Unified Ideographs, Extension A
171+ /// </summary>
172+ private static bool IsChineseCharacter ( char c )
173+ {
174+ return ( c >= 0x4E00 && c <= 0x9FFF ) || // CJK Unified Ideographs
175+ ( c >= 0x3400 && c <= 0x4DBF ) ; // CJK Extension A
176+ }
177+
178+ private string ToDoublePinyin ( string fullPinyin )
179+ {
180+ return currentDoublePinyinTable . TryGetValue ( fullPinyin , out var doublePinyinValue )
181+ ? doublePinyinValue
182+ : fullPinyin ;
183+ }
147184 }
148185}
0 commit comments