@@ -14,11 +14,8 @@ namespace Flow.Launcher.Infrastructure
14
14
{
15
15
public class PinyinAlphabet : IAlphabet
16
16
{
17
- private ConcurrentDictionary < string , ( string translation , TranslationMapping map ) > _pinyinCache =
18
- new ( ) ;
19
-
17
+ private readonly ConcurrentDictionary < string , ( string translation , TranslationMapping map ) > _pinyinCache = new ( ) ;
20
18
private readonly Settings _settings ;
21
-
22
19
private ReadOnlyDictionary < string , string > currentDoublePinyinTable ;
23
20
24
21
public PinyinAlphabet ( )
@@ -44,105 +41,145 @@ public void Reload()
44
41
45
42
private void CreateDoublePinyinTableFromStream ( Stream jsonStream )
46
43
{
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 ) )
50
52
{
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.") ;
52
54
}
53
- currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( value ) ;
55
+
56
+ currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( schemaDict ) ;
54
57
}
55
58
56
59
private void LoadDoublePinyinTable ( )
57
60
{
58
- if ( _settings . UseDoublePinyin )
61
+ if ( ! _settings . UseDoublePinyin )
59
62
{
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 ) ;
71
72
}
72
- else
73
+ catch ( FileNotFoundException e )
73
74
{
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 ) ;
74
91
currentDoublePinyinTable = new ReadOnlyDictionary < string , string > ( new Dictionary < string , string > ( ) ) ;
75
92
}
76
93
}
77
94
78
95
public bool ShouldTranslate ( string stringToTranslate )
79
96
{
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 ) ;
82
100
}
83
101
84
102
public ( string translation , TranslationMapping map ) Translate ( string content )
85
103
{
86
- if ( ! _settings . ShouldUsePinyin || ! WordsHelper . HasChinese ( content ) )
104
+ if ( ! _settings . ShouldUsePinyin || ! ContainsChinese ( content ) )
87
105
return ( content , null ) ;
88
106
89
- return _pinyinCache . TryGetValue ( content , out var value )
90
- ? value
91
- : BuildCacheFromContent ( content ) ;
107
+ return _pinyinCache . TryGetValue ( content , out var cached ) ? cached : BuildCacheFromContent ( content ) ;
92
108
}
93
109
94
110
private ( string translation , TranslationMapping map ) BuildCacheFromContent ( string content )
95
111
{
96
112
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
99
114
var map = new TranslationMapping ( ) ;
100
115
101
116
var previousIsChinese = false ;
102
117
103
118
for ( var i = 0 ; i < resultList . Length ; i ++ )
104
119
{
105
- if ( content [ i ] >= 0x3400 && content [ i ] <= 0x9FD5 )
120
+ if ( IsChineseCharacter ( content [ i ] ) )
106
121
{
107
- string translated = _settings . UseDoublePinyin ? ToDoublePin ( resultList [ i ] ) : resultList [ i ] ;
122
+ var translated = _settings . UseDoublePinyin ? ToDoublePinyin ( resultList [ i ] ) : resultList [ i ] ;
123
+
108
124
if ( i > 0 )
109
125
{
110
126
resultBuilder . Append ( ' ' ) ;
111
127
}
128
+
112
129
map . AddNewIndex ( resultBuilder . Length , translated . Length ) ;
113
130
resultBuilder . Append ( translated ) ;
114
131
previousIsChinese = true ;
115
132
}
116
133
else
117
134
{
135
+ // Add space after Chinese characters before non-Chinese characters
118
136
if ( previousIsChinese )
119
137
{
120
138
previousIsChinese = false ;
121
139
resultBuilder . Append ( ' ' ) ;
122
140
}
141
+
123
142
map . AddNewIndex ( resultBuilder . Length , resultList [ i ] . Length ) ;
124
143
resultBuilder . Append ( resultList [ i ] ) ;
125
144
}
126
145
}
127
146
128
- map . endConstruct ( ) ;
147
+ map . EndConstruct ( ) ;
129
148
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 ;
133
153
}
134
154
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 )
138
159
{
139
- if ( currentDoublePinyinTable . TryGetValue ( fullPinyin , out var doublePinyinValue ) )
160
+ foreach ( var c in text )
140
161
{
141
- return doublePinyinValue ;
162
+ if ( IsChineseCharacter ( c ) )
163
+ return true ;
142
164
}
143
- return fullPinyin ;
165
+ return false ;
144
166
}
145
167
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
+ }
147
184
}
148
185
}
0 commit comments