33using System . Globalization ;
44using System . IO ;
55using System . Linq ;
6- using System . Reflection ;
76using System . Threading ;
87using System . Threading . Tasks ;
98using System . Windows ;
@@ -28,54 +27,46 @@ public class Internationalization
2827 private const string DefaultFile = "en.xaml" ;
2928 private const string Extension = ".xaml" ;
3029 private readonly Settings _settings ;
31- private readonly List < string > _languageDirectories = new ( ) ;
32- private readonly List < ResourceDictionary > _oldResources = new ( ) ;
30+ private readonly List < string > _languageDirectories = [ ] ;
31+ private readonly List < ResourceDictionary > _oldResources = [ ] ;
3332 private static string SystemLanguageCode ;
3433
3534 public Internationalization ( Settings settings )
3635 {
3736 _settings = settings ;
38- AddFlowLauncherLanguageDirectory ( ) ;
3937 }
4038
41- private void AddFlowLauncherLanguageDirectory ( )
42- {
43- var directory = Path . Combine ( Constant . ProgramDirectory , Folder ) ;
44- _languageDirectories . Add ( directory ) ;
45- }
39+ #region Initialization
4640
41+ /// <summary>
42+ /// Initialize the system language code based on the current culture.
43+ /// </summary>
4744 public static void InitSystemLanguageCode ( )
4845 {
49- SystemLanguageCode = DefaultLanguageCode ;
50- }
46+ var availableLanguages = AvailableLanguages . GetAvailableLanguages ( ) ;
5147
52- private void AddPluginLanguageDirectories ( )
53- {
54- foreach ( var plugin in PluginManager . GetTranslationPlugins ( ) )
48+ // Retrieve the language identifiers for the current culture.
49+ // ChangeLanguage method overrides the CultureInfo.CurrentCulture, so this needs to
50+ // be called at startup in order to get the correct lang code of system.
51+ var currentCulture = CultureInfo . CurrentCulture ;
52+ var twoLetterCode = currentCulture . TwoLetterISOLanguageName ;
53+ var threeLetterCode = currentCulture . ThreeLetterISOLanguageName ;
54+ var fullName = currentCulture . Name ;
55+
56+ // Try to find a match in the available languages list
57+ foreach ( var language in availableLanguages )
5558 {
56- var location = Assembly . GetAssembly ( plugin . Plugin . GetType ( ) ) . Location ;
57- var dir = Path . GetDirectoryName ( location ) ;
58- if ( dir != null )
59- {
60- var pluginThemeDirectory = Path . Combine ( dir , Folder ) ;
61- _languageDirectories . Add ( pluginThemeDirectory ) ;
62- }
63- else
59+ var languageCode = language . LanguageCode ;
60+
61+ if ( string . Equals ( languageCode , twoLetterCode , StringComparison . OrdinalIgnoreCase ) ||
62+ string . Equals ( languageCode , threeLetterCode , StringComparison . OrdinalIgnoreCase ) ||
63+ string . Equals ( languageCode , fullName , StringComparison . OrdinalIgnoreCase ) )
6464 {
65- API . LogError ( ClassName , $ "Can't find plugin path < { location } > for < { plugin . Metadata . Name } >" ) ;
65+ SystemLanguageCode = languageCode ;
6666 }
6767 }
6868
69- LoadDefaultLanguage ( ) ;
70- }
71-
72- private void LoadDefaultLanguage ( )
73- {
74- // Removes language files loaded before any plugins were loaded.
75- // Prevents the language Flow started in from overwriting English if the user switches back to English
76- RemoveOldLanguageFiles ( ) ;
77- LoadLanguage ( AvailableLanguages . English ) ;
78- _oldResources . Clear ( ) ;
69+ SystemLanguageCode = DefaultLanguageCode ;
7970 }
8071
8172 /// <summary>
@@ -93,13 +84,64 @@ public async Task InitializeLanguageAsync()
9384 // Get language by language code and change language
9485 var language = GetLanguageByLanguageCode ( languageCode ) ;
9586
87+ // Add Flow Launcher language directory
88+ AddFlowLauncherLanguageDirectory ( ) ;
89+
9690 // Add plugin language directories first so that we can load language files from plugins
9791 AddPluginLanguageDirectories ( ) ;
9892
93+ // Load default language resources
94+ LoadDefaultLanguage ( ) ;
95+
9996 // Change language
100- await ChangeLanguageAsync ( language ) ;
97+ await ChangeLanguageAsync ( language , false ) ;
10198 }
10299
100+ private void AddFlowLauncherLanguageDirectory ( )
101+ {
102+ // Check if Flow Launcher language directory exists
103+ var directory = Path . Combine ( Constant . ProgramDirectory , Folder ) ;
104+ if ( ! Directory . Exists ( directory ) )
105+ {
106+ API . LogError ( ClassName , $ "Flow Launcher language directory can't be found <{ directory } >") ;
107+ return ;
108+ }
109+
110+ _languageDirectories . Add ( directory ) ;
111+ }
112+
113+ private void AddPluginLanguageDirectories ( )
114+ {
115+ foreach ( var pluginsDir in PluginManager . Directories )
116+ {
117+ if ( ! Directory . Exists ( pluginsDir ) ) continue ;
118+
119+ // Enumerate all top directories in the plugin directory
120+ foreach ( var dir in Directory . GetDirectories ( pluginsDir ) )
121+ {
122+ // Check if the directory contains a language folder
123+ var pluginLanguageDir = Path . Combine ( dir , Folder ) ;
124+ if ( ! Directory . Exists ( pluginLanguageDir ) ) continue ;
125+
126+ // Check if the language directory contains default language file since it will be checked later
127+ _languageDirectories . Add ( pluginLanguageDir ) ;
128+ }
129+ }
130+ }
131+
132+ private void LoadDefaultLanguage ( )
133+ {
134+ // Removes language files loaded before any plugins were loaded.
135+ // Prevents the language Flow started in from overwriting English if the user switches back to English
136+ RemoveOldLanguageFiles ( ) ;
137+ LoadLanguage ( AvailableLanguages . English ) ;
138+ _oldResources . Clear ( ) ;
139+ }
140+
141+ #endregion
142+
143+ #region Change Language
144+
103145 /// <summary>
104146 /// Change language during runtime. Will change app language and plugin language & save settings.
105147 /// </summary>
@@ -128,8 +170,8 @@ public void ChangeLanguage(string languageCode)
128170
129171 private static Language GetLanguageByLanguageCode ( string languageCode )
130172 {
131- var lowercase = languageCode . ToLower ( ) ;
132- var language = AvailableLanguages . GetAvailableLanguages ( ) . FirstOrDefault ( o => o . LanguageCode . ToLower ( ) == lowercase ) ;
173+ var language = AvailableLanguages . GetAvailableLanguages ( ) .
174+ FirstOrDefault ( o => o . LanguageCode . Equals ( languageCode , StringComparison . OrdinalIgnoreCase ) ) ;
133175 if ( language == null )
134176 {
135177 API . LogError ( ClassName , $ "Language code can't be found <{ languageCode } >") ;
@@ -141,7 +183,7 @@ private static Language GetLanguageByLanguageCode(string languageCode)
141183 }
142184 }
143185
144- private async Task ChangeLanguageAsync ( Language language )
186+ private async Task ChangeLanguageAsync ( Language language , bool updateMetadata = true )
145187 {
146188 // Remove old language files and load language
147189 RemoveOldLanguageFiles ( ) ;
@@ -153,8 +195,11 @@ private async Task ChangeLanguageAsync(Language language)
153195 // Change culture info
154196 ChangeCultureInfo ( language . LanguageCode ) ;
155197
156- // Raise event for plugins after culture is set
157- await Task . Run ( UpdatePluginMetadataTranslations ) ;
198+ if ( updateMetadata )
199+ {
200+ // Raise event for plugins after culture is set
201+ await Task . Run ( UpdatePluginMetadataTranslations ) ;
202+ }
158203 }
159204
160205 public static void ChangeCultureInfo ( string languageCode )
@@ -177,6 +222,10 @@ public static void ChangeCultureInfo(string languageCode)
177222 thread . CurrentUICulture = currentCulture ;
178223 }
179224
225+ #endregion
226+
227+ #region Language Resources Management
228+
180229 private void RemoveOldLanguageFiles ( )
181230 {
182231 var dicts = Application . Current . Resources . MergedDictionaries ;
@@ -212,6 +261,49 @@ private void LoadLanguage(Language language)
212261 }
213262 }
214263
264+ private static string LanguageFile ( string folder , string language )
265+ {
266+ if ( Directory . Exists ( folder ) )
267+ {
268+ var path = Path . Combine ( folder , language ) ;
269+ if ( File . Exists ( path ) )
270+ {
271+ return path ;
272+ }
273+ else
274+ {
275+ API . LogError ( ClassName , $ "Language path can't be found <{ path } >") ;
276+ var english = Path . Combine ( folder , DefaultFile ) ;
277+ if ( File . Exists ( english ) )
278+ {
279+ return english ;
280+ }
281+ else
282+ {
283+ API . LogError ( ClassName , $ "Default English Language path can't be found <{ path } >") ;
284+ return string . Empty ;
285+ }
286+ }
287+ }
288+ else
289+ {
290+ return string . Empty ;
291+ }
292+ }
293+
294+ #endregion
295+
296+ #region Available Languages
297+
298+ public List < Language > LoadAvailableLanguages ( )
299+ {
300+ return AvailableLanguages . GetAvailableLanguages ( ) ;
301+ }
302+
303+ #endregion
304+
305+ #region Get Translations
306+
215307 public static string GetTranslation ( string key )
216308 {
217309 var translation = Application . Current . TryFindResource ( key ) ;
@@ -226,7 +318,11 @@ public static string GetTranslation(string key)
226318 }
227319 }
228320
229- private void UpdatePluginMetadataTranslations ( )
321+ #endregion
322+
323+ #region Update Metadata
324+
325+ public static void UpdatePluginMetadataTranslations ( )
230326 {
231327 // Update plugin metadata name & description
232328 foreach ( var p in PluginManager . GetTranslationPlugins ( ) )
@@ -245,34 +341,6 @@ private void UpdatePluginMetadataTranslations()
245341 }
246342 }
247343
248- private static string LanguageFile ( string folder , string language )
249- {
250- if ( Directory . Exists ( folder ) )
251- {
252- var path = Path . Combine ( folder , language ) ;
253- if ( File . Exists ( path ) )
254- {
255- return path ;
256- }
257- else
258- {
259- API . LogError ( ClassName , $ "Language path can't be found <{ path } >") ;
260- var english = Path . Combine ( folder , DefaultFile ) ;
261- if ( File . Exists ( english ) )
262- {
263- return english ;
264- }
265- else
266- {
267- API . LogError ( ClassName , $ "Default English Language path can't be found <{ path } >") ;
268- return string . Empty ;
269- }
270- }
271- }
272- else
273- {
274- return string . Empty ;
275- }
276- }
344+ #endregion
277345 }
278346}
0 commit comments