Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Flow.Launcher.Core/Plugin/PluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
/// <summary>
/// Directories that will hold Flow Launcher plugin directory
/// </summary>
private static readonly string[] Directories =
public static readonly string[] Directories =
{
Constant.PreinstalledDirectory, DataLocation.PluginsDirectory
};
Expand Down Expand Up @@ -173,7 +173,7 @@

/// <summary>
/// because InitializePlugins needs API, so LoadPlugins needs to be called first
/// todo happlebao The API should be removed

Check warning on line 176 in Flow.Launcher.Core/Plugin/PluginManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`happlebao` is not a recognized word. (unrecognized-spelling)
/// </summary>
/// <param name="settings"></param>
public static void LoadPlugins(PluginsSettings settings)
Expand Down Expand Up @@ -453,7 +453,7 @@
r.OriginQuery = query;

// ActionKeywordAssigned is used for constructing MainViewModel's query text auto-complete suggestions
// Plugins may have multi-actionkeywords eg. WebSearches. In this scenario it needs to be overriden on the plugin level

Check warning on line 456 in Flow.Launcher.Core/Plugin/PluginManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`overriden` is not a recognized word. (unrecognized-spelling)
if (metadata.ActionKeywords.Count == 1)
r.ActionKeywordAssigned = query.ActionKeyword;
}
Expand Down Expand Up @@ -716,7 +716,7 @@
"CEA08895D2544B019B2E9C5009600DF4", // Sys
"0308FD86DE0A4DEE8D62B9B535370992", // URL
"565B73353DBF4806919830B9202EE3BF", // WebSearch
"5043CETYU6A748679OPA02D27D99677A" // WindowsSettings

Check warning on line 719 in Flow.Launcher.Core/Plugin/PluginManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`CETYU` is not a recognized word. (unrecognized-spelling)
};

// Treat default plugin differently, it needs to be removable along with each flow release
Expand Down
194 changes: 119 additions & 75 deletions Flow.Launcher.Core/Resource/Internationalization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
Expand All @@ -28,22 +27,20 @@ public class Internationalization
private const string DefaultFile = "en.xaml";
private const string Extension = ".xaml";
private readonly Settings _settings;
private readonly List<string> _languageDirectories = new();
private readonly List<ResourceDictionary> _oldResources = new();
private readonly List<string> _languageDirectories = [];
private readonly List<ResourceDictionary> _oldResources = [];
private static string SystemLanguageCode;

public Internationalization(Settings settings)
{
_settings = settings;
AddFlowLauncherLanguageDirectory();
}

private void AddFlowLauncherLanguageDirectory()
{
var directory = Path.Combine(Constant.ProgramDirectory, Folder);
_languageDirectories.Add(directory);
}
#region Initialization

/// <summary>
/// Initialize the system language code based on the current culture.
/// </summary>
public static void InitSystemLanguageCode()
{
var availableLanguages = AvailableLanguages.GetAvailableLanguages();
Expand Down Expand Up @@ -72,35 +69,6 @@ public static void InitSystemLanguageCode()
SystemLanguageCode = DefaultLanguageCode;
}

private void AddPluginLanguageDirectories()
{
foreach (var plugin in PluginManager.GetTranslationPlugins())
{
var location = Assembly.GetAssembly(plugin.Plugin.GetType()).Location;
var dir = Path.GetDirectoryName(location);
if (dir != null)
{
var pluginThemeDirectory = Path.Combine(dir, Folder);
_languageDirectories.Add(pluginThemeDirectory);
}
else
{
API.LogError(ClassName, $"Can't find plugin path <{location}> for <{plugin.Metadata.Name}>");
}
}

LoadDefaultLanguage();
}

private void LoadDefaultLanguage()
{
// Removes language files loaded before any plugins were loaded.
// Prevents the language Flow started in from overwriting English if the user switches back to English
RemoveOldLanguageFiles();
LoadLanguage(AvailableLanguages.English);
_oldResources.Clear();
}

/// <summary>
/// Initialize language. Will change app language and plugin language based on settings.
/// </summary>
Expand All @@ -116,13 +84,64 @@ public async Task InitializeLanguageAsync()
// Get language by language code and change language
var language = GetLanguageByLanguageCode(languageCode);

// Add Flow Launcher language directory
AddFlowLauncherLanguageDirectory();

// Add plugin language directories first so that we can load language files from plugins
AddPluginLanguageDirectories();

// Load default language resources
LoadDefaultLanguage();

// Change language
await ChangeLanguageAsync(language);
await ChangeLanguageAsync(language, false);
}

private void AddFlowLauncherLanguageDirectory()
{
// Check if Flow Launcher language directory exists
var directory = Path.Combine(Constant.ProgramDirectory, Folder);
if (!Directory.Exists(directory))
{
API.LogError(ClassName, $"Flow Launcher language directory can't be found <{directory}>");
return;
}

_languageDirectories.Add(directory);
}

private void AddPluginLanguageDirectories()
{
foreach (var pluginsDir in PluginManager.Directories)
{
if (!Directory.Exists(pluginsDir)) continue;

// Enumerate all top directories in the plugin directory
foreach (var dir in Directory.GetDirectories(pluginsDir))
{
// Check if the directory contains a language folder
var pluginLanguageDir = Path.Combine(dir, Folder);
if (!Directory.Exists(pluginLanguageDir)) continue;

// Check if the language directory contains default language file since it will be checked later
_languageDirectories.Add(pluginLanguageDir);
}
}
}

private void LoadDefaultLanguage()
{
// Removes language files loaded before any plugins were loaded.
// Prevents the language Flow started in from overwriting English if the user switches back to English
RemoveOldLanguageFiles();
LoadLanguage(AvailableLanguages.English);
_oldResources.Clear();
}

#endregion

#region Change Language

/// <summary>
/// Change language during runtime. Will change app language and plugin language & save settings.
/// </summary>
Expand Down Expand Up @@ -151,8 +170,8 @@ public void ChangeLanguage(string languageCode)

private static Language GetLanguageByLanguageCode(string languageCode)
{
var lowercase = languageCode.ToLower();
var language = AvailableLanguages.GetAvailableLanguages().FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase);
var language = AvailableLanguages.GetAvailableLanguages().
FirstOrDefault(o => o.LanguageCode.Equals(languageCode, StringComparison.OrdinalIgnoreCase));
if (language == null)
{
API.LogError(ClassName, $"Language code can't be found <{languageCode}>");
Expand All @@ -164,7 +183,7 @@ private static Language GetLanguageByLanguageCode(string languageCode)
}
}

private async Task ChangeLanguageAsync(Language language)
private async Task ChangeLanguageAsync(Language language, bool updateMetadata = true)
{
// Remove old language files and load language
RemoveOldLanguageFiles();
Expand All @@ -176,8 +195,11 @@ private async Task ChangeLanguageAsync(Language language)
// Change culture info
ChangeCultureInfo(language.LanguageCode);

// Raise event for plugins after culture is set
await Task.Run(UpdatePluginMetadataTranslations);
if (updateMetadata)
{
// Raise event for plugins after culture is set
await Task.Run(UpdatePluginMetadataTranslations);
}
}

public static void ChangeCultureInfo(string languageCode)
Expand All @@ -200,6 +222,10 @@ public static void ChangeCultureInfo(string languageCode)
thread.CurrentUICulture = currentCulture;
}

#endregion

#region Prompt Pinyin

public bool PromptShouldUsePinyin(string languageCodeToSet)
{
var languageToSet = GetLanguageByLanguageCode(languageCodeToSet);
Expand All @@ -212,14 +238,18 @@ public bool PromptShouldUsePinyin(string languageCodeToSet)

// No other languages should show the following text so just make it hard-coded
// "Do you want to search with pinyin?"
string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?" ;
string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?";

if (API.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
return false;

return true;
}

#endregion

#region Language Resources Management

private void RemoveOldLanguageFiles()
{
var dicts = Application.Current.Resources.MergedDictionaries;
Expand Down Expand Up @@ -255,13 +285,51 @@ private void LoadLanguage(Language language)
}
}

private static string LanguageFile(string folder, string language)
{
if (Directory.Exists(folder))
{
var path = Path.Combine(folder, language);
if (File.Exists(path))
{
return path;
}
else
{
API.LogError(ClassName, $"Language path can't be found <{path}>");
var english = Path.Combine(folder, DefaultFile);
if (File.Exists(english))
{
return english;
}
else
{
API.LogError(ClassName, $"Default English Language path can't be found <{path}>");
return string.Empty;
}
}
}
else
{
return string.Empty;
}
}

#endregion

#region Available Languages

public List<Language> LoadAvailableLanguages()
{
var list = AvailableLanguages.GetAvailableLanguages();
list.Insert(0, new Language(Constant.SystemLanguageCode, AvailableLanguages.GetSystemTranslation(SystemLanguageCode)));
return list;
}

#endregion

#region Get Translations

public static string GetTranslation(string key)
{
var translation = Application.Current.TryFindResource(key);
Expand All @@ -276,7 +344,11 @@ public static string GetTranslation(string key)
}
}

private void UpdatePluginMetadataTranslations()
#endregion

#region Update Metadata

public static void UpdatePluginMetadataTranslations()
{
// Update plugin metadata name & description
foreach (var p in PluginManager.GetTranslationPlugins())
Expand All @@ -295,34 +367,6 @@ private void UpdatePluginMetadataTranslations()
}
}

private static string LanguageFile(string folder, string language)
{
if (Directory.Exists(folder))
{
var path = Path.Combine(folder, language);
if (File.Exists(path))
{
return path;
}
else
{
API.LogError(ClassName, $"Language path can't be found <{path}>");
var english = Path.Combine(folder, DefaultFile);
if (File.Exists(english))
{
return english;
}
else
{
API.LogError(ClassName, $"Default English Language path can't be found <{path}>");
return string.Empty;
}
}
}
else
{
return string.Empty;
}
}
#endregion
}
}
7 changes: 5 additions & 2 deletions Flow.Launcher/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@
// Enable Win32 dark mode if the system is in dark mode before creating all windows
Win32Helper.EnableWin32DarkMode(_settings.ColorScheme);

// Initialize language before portable clean up since it needs translations
await Ioc.Default.GetRequiredService<Internationalization>().InitializeLanguageAsync();

Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();

API.LogInfo(ClassName, "Begin Flow Launcher startup ----------------------------------------------------");
Expand All @@ -201,7 +204,7 @@
RegisterDispatcherUnhandledException();
RegisterTaskSchedulerUnhandledException();

var imageLoadertask = ImageLoader.InitializeAsync();

Check warning on line 207 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Loadertask` is not a recognized word. (unrecognized-spelling)

AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);

Expand All @@ -217,10 +220,10 @@

await PluginManager.InitializePluginsAsync();

// Change language after all plugins are initialized because we need to update plugin title based on their api
await Ioc.Default.GetRequiredService<Internationalization>().InitializeLanguageAsync();
// Update plugin titles after plugins are initialized with their api instances
Internationalization.UpdatePluginMetadataTranslations();

await imageLoadertask;

Check warning on line 226 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Loadertask` is not a recognized word. (unrecognized-spelling)

Check warning on line 226 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`Loadertask` is not a recognized word. (unrecognized-spelling)

_mainWindow = new MainWindow();

Expand Down Expand Up @@ -397,7 +400,7 @@

// If we call Environment.Exit(0), the application dispose will be called before _mainWindow.Close()
// Accessing _mainWindow?.Dispatcher will cause the application stuck
// So here we need to check it and just return so that we will not acees _mainWindow?.Dispatcher

Check warning on line 403 in Flow.Launcher/App.xaml.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`acees` is not a recognized word. (unrecognized-spelling)
if (!_mainWindow.CanClose)
{
return;
Expand Down
Loading