Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
2028cc3
build: conditional TFM + LinuxDebug config skeleton, exclude shell/au…
clansty Jun 17, 2026
145a1be
build: add System.Resources.Extensions + System.Drawing.Common for Li…
clansty Jun 17, 2026
51a1427
build(linux): Mono.Cecil nuget, #if WINDOWS for AssemblyInfo/IAP, BCL…
clansty Jun 17, 2026
dc40923
refactor: decode jacket textures via AssetsTools.NET.Texture (drop As…
clansty Jun 17, 2026
df78aaa
feat(platform): add cross-platform abstraction layer (dialog/taskbar/…
clansty Jun 17, 2026
d4374ca
feat(linux): exclude audio tool, migrate dialog/taskbar call sites of…
clansty Jun 17, 2026
cc00ec0
feat(linux): wire shell-nav/IAP abstractions + headless entry; backen…
clansty Jun 17, 2026
e40dbc5
feat(platform): cancellable IProgressController (Vanara on Windows, h…
clansty Jun 17, 2026
80d40d4
fix: cross-platform path separators (segments + ContainsSegment helper)
clansty Jun 17, 2026
ef12896
fix build
clansty Jun 18, 2026
061db08
fix submodule
clansty Jun 18, 2026
446fa31
fix: 用 SetPictureDataFromBundle 解析 bundle 内部 .resS 贴图数据,修复原始封面 GetJac…
clansty Jun 18, 2026
d31d2bd
docs: 注释统一改为中文
clansty Jun 18, 2026
4f0e46b
feat(linux): Photino 宿主——进程内 Kestrel 同源伺服 SPA + 原生窗口加载
clansty Jun 18, 2026
00dd114
feat(linux): 用 Photino 原生对话框实现文件/文件夹/消息框(替换 headless stub),OOBE 选目录可用
clansty Jun 18, 2026
9bb69de
fix(linux): OOBE 路由 + 单窗口导航 + GamePath 空值兜底,修复未配置游戏目录时的异常
clansty Jun 18, 2026
0ebc92f
fix(linux): 迁移所有 VisualBasic.FileIO 文件操作到跨平台 PlatformFile(修复 Linux 删除…
clansty Jun 18, 2026
09a685a
fix(linux): 版本/流派扫描大小写不敏感 + 补提交 PlatformFile(修复 0ebc92f 漏提交导致的编译缺失)
clansty Jun 18, 2026
4989992
fix(linux/web): WebKitGTK 兼容——单文件选择改用 input[type=file],目录操作按 isLocalH…
clansty Jun 18, 2026
1032e66
feat(linux): 新增 RequestExportMaidata —— 原生选目录 + 服务端写 maidata,支持 WebKi…
clansty Jun 18, 2026
c385e40
feat(linux/web): maidata 导出走后端 RequestExportMaidata、导入用 webkitdirecto…
clansty Jun 18, 2026
3f72f50
fix(linux): 配置 Xabe.FFmpeg 使用系统 ffmpeg(PATH 解析 + 显式可执行名),修复导出时找 ffmpe…
clansty Jun 18, 2026
26530b1
feat(linux): maidata 导入改用后端原生选目录 + HTTP 提供目录内容(WebKitGTK 无 showDirect…
clansty Jun 18, 2026
3969f59
feat(linux): 预览谱面用 Photino 内置 webview 子窗口(前端 sendMessage→宿主开子窗口)
clansty Jun 18, 2026
b343aaa
fix(web): getUrl 返回绝对地址(location.origin 回退),避免相对地址在 WebKitGTK 下被 new …
clansty Jun 18, 2026
3c594af
fix(linux): 导入选目录在 Photino 下优先走后端,绕开 WebKitGTK 坏掉的 showDirectoryPicke…
clansty Jun 19, 2026
b9d872d
debug(import): 打印 fetch URL 与失败步骤的字符串日志(WebKitGTK console 无法转换异常对象)
clansty Jun 19, 2026
2abe59a
feat: 文件夹选择对话框记住上次目录(Config.LastDialogFolder,Photino + WinForms)
clansty Jun 19, 2026
fb33bd7
debug(import): pickViaBackend 细粒度日志定位 SyntaxError;撤销 WinForms 记忆目录(Wi…
clansty Jun 19, 2026
2a32f30
fix(import): PickImportFolder 返回的是 text/plain 裸路径,前端改用 res.text() 而非 …
clansty Jun 19, 2026
4d250b8
feat(linux): 音频管道接入 Linux —— 引 AcbCore + SonicAudioLib(netstandard2.0…
clansty Jun 19, 2026
aabcd62
fix(linux): 音频 ffmpeg 解码改用 Process+ArgumentList,规避 Xabe 在 Linux 把引号字面…
clansty Jun 19, 2026
124ae0c
fix(linux): 封面 AB 创建返回小写文件名路径,匹配实际写出的小写文件(Linux 大小写敏感)
clansty Jun 19, 2026
546f23a
将 ffmpeg 封装从 Xabe.FFmpeg 迁移到 FFMpegCore 并移除 Xabe 依赖
clansty Jun 19, 2026
f1227e2
修复 Linux 视频转换两个问题:ffprobe 路径与进度恒为 0
clansty Jun 19, 2026
a1a63ed
Linux 构建不再复制内置 ffmpeg.exe/ffprobe.exe 到输出目录
clansty Jun 19, 2026
f541c93
feat: 加视频编码器 profile descriptor 类型
clansty Jun 19, 2026
92ecb39
feat: 加带设备初始化的视频编码器探测器
clansty Jun 19, 2026
d01ba81
feat: 加 ForceSoftwareVideo 配置作为硬件编码逃生口
clansty Jun 19, 2026
b64b248
feat: VideoConvert/concat 接入编码器 profile
clansty Jun 19, 2026
f8c6aed
去掉 Windows 不再需要的 AssetStudio 依赖
clansty Jun 19, 2026
631999c
删掉 Libs 下剩余孤儿二进制(WinBlur/K4os/Mono.Cecil.*)
clansty Jun 19, 2026
4b11ade
替换 PV 转换页去掉重复的百分比(进度条已自带),仅保留 100% 后的处理提示
clansty Jun 19, 2026
4ec006a
Linux 改框架依赖发布 + 修 resx 区域名大小写
clansty Jun 19, 2026
f68ebc1
加 Arch Linux PKGBUILD(框架依赖打包)
clansty Jun 19, 2026
f7a8d6e
Linux 构建注入 git 派生的版本号(对齐 Build.ps1)
clansty Jun 19, 2026
43977d3
让 MuConvert 与 MaiChartManager CLI 在 Linux 原生可用 + 重开 R2R
clansty Jun 19, 2026
16f9f82
重新加入 Directory.Build.props(条件化 MuConvert 自包含,仅 Windows)
clansty Jun 19, 2026
2efba1d
修复 GUI 启动卡 25 秒:显式指定 ContentRoot 为 exeDir
clansty Jun 19, 2026
7b00474
PKGBUILD: 更新 pkgver 缓存值(makepkg 自动)
clansty Jun 19, 2026
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 .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
url = git@github.com:clansty/SonicAudioTools.git
[submodule "XV2-Tools"]
path = XV2-Tools
url = git@github.com:clansty/XV2-Tools.git
url = git@github.com:MuNET-OSS/XV2-Tools.git
[submodule "AquaMai"]
path = AquaMai
url = git@github.com:MewoLab/AquaMai.git
Expand Down
9 changes: 5 additions & 4 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<Project>
<PropertyGroup Condition="'$(MSBuildProjectName)' == 'MuConvert'">
<!-- 仅在 Windows 上构建时,让随 MaiChartManager 一起发布的 MuConvert 为自包含 win-x64,
使打包进 Windows 版的 MuConvert.exe 可独立运行(不依赖单独的 dotnet 运行时)。
Linux 原生构建则不强制,MuConvert 随主项目按 linux-x64 框架依赖构建。
(原先无条件强制 win-x64 是为了在 Linux 上用 Wine 测 Windows 版,已不需要。) -->
<PropertyGroup Condition="'$(MSBuildProjectName)' == 'MuConvert' and $([MSBuild]::IsOSPlatform('Windows'))">
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup>
<PropertyGroup>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>
</Project>
4 changes: 3 additions & 1 deletion MaiChartManager.CLI/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@
[assembly: AssemblyProduct("MaiChartManager CLI")]
[assembly: AssemblyTitle("MaiChartManager CLI")]
[assembly: AssemblyVersion(AppMain.Version)]
#if WINDOWS
[assembly: System.Runtime.Versioning.TargetPlatformAttribute("Windows10.0.17763.0")]
[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows10.0.17134.0")]
[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows10.0.17134.0")]
#endif
6 changes: 6 additions & 0 deletions MaiChartManager.CLI/Commands/DebugCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public class DebugCommand : Command
{
public override int Execute(CommandContext context, CancellationToken cancellationToken)
{
#if WINDOWS
// WebView2 (COM) 要求 STA 线程;CLI 的 async 入口运行在 MTA 上下文中,
// 而 Program.Main 上的 [STAThread] 作为普通方法调用时不生效,需手动建 STA 线程
Exception? exception = null;
Expand All @@ -30,5 +31,10 @@ public override int Execute(CommandContext context, CancellationToken cancellati
throw exception;
}
return 0;
#else
// Linux:直接启动 Photino 主程序(无 COM/STA 限制),控制台可见日志
MaiChartManager.LinuxProgram.Main([]);
return 0;
#endif
}
}
26 changes: 19 additions & 7 deletions MaiChartManager.CLI/MaiChartManager.CLI.csproj
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<IsLinuxBuild Condition="$(Configuration.StartsWith('Linux'))">true</IsLinuxBuild>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0-windows10.0.17763.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<ValidateExecutableReferencesMatchSelfContained>False</ValidateExecutableReferencesMatchSelfContained>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
<CETCompat>false</CETCompat>
<PublishTrimmed>False</PublishTrimmed>
<PublishAot>False</PublishAot>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishDir>..\Packaging\Pack</PublishDir>
<Configurations>Debug;Release;Crack</Configurations>
<Configurations>Debug;Release;Crack;LinuxDebug;LinuxRelease</Configurations>
<Platforms>AnyCPU</Platforms>
<CETCompat>false</CETCompat>
<ApplicationIcon>icon.ico</ApplicationIcon>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
</PropertyGroup>

<!-- Windows:net*-windows + 自包含 win-x64(与原行为一致) -->
<PropertyGroup Condition="'$(IsLinuxBuild)' != 'true'">
<TargetFramework>net10.0-windows10.0.17763.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishDir>..\Packaging\Pack</PublishDir>
<ApplicationIcon>icon.ico</ApplicationIcon>
<DefineConstants>$(DefineConstants);WINDOWS</DefineConstants>
</PropertyGroup>

<!-- Linux:net10.0 + 框架依赖 linux-x64(依赖系统 aspnet-runtime) -->
<PropertyGroup Condition="'$(IsLinuxBuild)' == 'true'">
<TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Crack|x64' ">
<DefineConstants>CRACK</DefineConstants>
<Optimize>true</Optimize>
Expand Down
7 changes: 7 additions & 0 deletions MaiChartManager.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,15 @@
#endif
});

#if WINDOWS
AppMain.InitConfiguration(true);
await IapManager.Init();
#else
// Linux:复用 LinuxProgram 的无头初始化(加载配置 + 配置系统 ffmpeg),
// 不走 Windows-only 的 AppMain/IapManager。
LinuxProgram.InitConfiguration();
LinuxProgram.ConfigureFfmpeg();
#endif

var app = new CommandApp();

Expand Down
10 changes: 8 additions & 2 deletions MaiChartManager/AppMain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using Microsoft.Web.WebView2.Core;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Xabe.FFmpeg;
using FFMpegCore;

namespace MaiChartManager;

Expand Down Expand Up @@ -75,7 +75,13 @@ public void Run()
ApplicationConfiguration.Initialize();
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
UiContext = SynchronizationContext.Current;
FFmpeg.SetExecutablesPath(StaticSettings.exeDir);
// FFMpegCore:Windows 内置 ffmpeg.exe/ffprobe.exe 在 exeDir,临时文件用 tempPath。
// FFMpegCore 会按 OS 自动给可执行名补 .exe 后缀。
GlobalFFOptions.Configure(o =>
{
o.BinaryFolder = StaticSettings.exeDir;
o.TemporaryFilesFolder = StaticSettings.tempPath;
});
VideoConvert.CheckHardwareAcceleration();

Directory.CreateDirectory(StaticSettings.appData);
Expand Down
2 changes: 1 addition & 1 deletion MaiChartManager/AppMain.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ namespace MaiChartManager;

public partial class AppMain
{
public const string Version = "26.2";
public const string Version = "26.3";
}
5 changes: 5 additions & 0 deletions MaiChartManager/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ public class Config
public MovieCodec MovieCodec { get; set; } = MovieCodec.ForceVP9;
public bool Yuv420p { get; set; } = true;
public bool NoScale { get; set; } = false;
// 强制视频走软件编码:硬件 H264 产物若游戏不认,改 config.json 设为 true 即可一键退回软件。
public bool ForceSoftwareVideo { get; set; } = false;
public bool IgnoreLevel { get; set; } = false;
public bool DisableBga { get; set; } = false;
public bool UseLegacyMaiLib { get; set; } = false;
public bool ConvertJacketToAssetBundle { get; set; } = true;
public int UiZoom { get; set; } = 0;

// 记住上次文件夹选择对话框选中的目录,下次打开时从这里开始(而不是每次都回到 Documents)。
public string? LastDialogFolder { get; set; } = null;

public void Save()
{
var json = JsonSerializer.Serialize(this);
Expand Down
25 changes: 23 additions & 2 deletions MaiChartManager/Controllers/App/AppLicenseController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Windows.Services.Store;
#if WINDOWS
using Windows.Services.Store;
#endif
using Microsoft.AspNetCore.Mvc;

namespace MaiChartManager.Controllers.App;
Expand All @@ -7,6 +9,7 @@ namespace MaiChartManager.Controllers.App;
[Route("MaiChartManagerServlet/[action]Api")]
public class AppLicenseController : Controller
{
#if WINDOWS
public record RequestPurchaseResult(string? ErrorMessage, StorePurchaseStatus Status);

[HttpPost]
Expand All @@ -32,4 +35,22 @@ public async Task<bool> VerifyOfflineKey([FromBody] string key)
IapManager.SetOfflineLicenseActive();
return true;
}
}
#else
// Linux:始终已授权——不支持商店 / IAP
public record RequestPurchaseResult(string? ErrorMessage, int Status);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Linux purchase response changes status to numeric, breaking existing string-enum API contract.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At MaiChartManager/Controllers/App/AppLicenseController.cs, line 41:

<comment>Linux purchase response changes `status` to numeric, breaking existing string-enum API contract.</comment>

<file context>
@@ -32,4 +35,22 @@ public async Task<bool> VerifyOfflineKey([FromBody] string key)
+#else
+    // Linux:始终已授权——不支持商店 / IAP
+    public record RequestPurchaseResult(string? ErrorMessage, int Status);
+
+    [HttpPost]
+    public Task<RequestPurchaseResult> RequestPurchase()
</file context>

[HttpPost]
public Task<RequestPurchaseResult> RequestPurchase()
{
// StorePurchaseStatus.Succeeded = 0
return Task.FromResult(new RequestPurchaseResult(null, 0));
}

[HttpPost]
public Task<bool> VerifyOfflineKey([FromBody] string key)
{
// Linux 不做离线密钥验证;始终视为已授权
return Task.FromResult(true);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: VerifyOfflineKey unconditionally succeeds on Linux, allowing invalid offline keys to be accepted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At MaiChartManager/Controllers/App/AppLicenseController.cs, line 54:

<comment>`VerifyOfflineKey` unconditionally succeeds on Linux, allowing invalid offline keys to be accepted.</comment>

<file context>
@@ -32,4 +35,22 @@ public async Task<bool> VerifyOfflineKey([FromBody] string key)
+    {
+        // Linux 不做离线密钥验证;始终视为已授权
+        return Task.FromResult(true);
+    }
+#endif
+}
</file context>

#endif
}
22 changes: 20 additions & 2 deletions MaiChartManager/Controllers/App/AppVersionController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using MaiChartManager.Utils;
using MaiChartManager.Utils;
using Microsoft.AspNetCore.Mvc;

namespace MaiChartManager.Controllers.App;
Expand All @@ -7,11 +7,29 @@ namespace MaiChartManager.Controllers.App;
[Route("MaiChartManagerServlet/[action]Api")]
public class AppVersionController(StaticSettings settings, ILogger<AppVersionController> logger) : ControllerBase
{
#if WINDOWS
public record AppVersionResult(string Version, int GameVersion, IapManager.LicenseStatus License, VideoConvert.HardwareAccelerationStatus HardwareAcceleration, string H264Encoder, string Locale);

[HttpGet]
public AppVersionResult GetAppVersion()
{
return new AppVersionResult(Application.ProductVersion, settings.gameVersion, IapManager.License, VideoConvert.HardwareAcceleration, VideoConvert.H264Encoder, StaticSettings.CurrentLocale);
}
}
#else
public enum LicenseStatus { Pending, Active, Inactive }
public record AppVersionResult(string Version, int GameVersion, LicenseStatus License, VideoConvert.HardwareAccelerationStatus HardwareAcceleration, string H264Encoder, string Locale);

[HttpGet]
public AppVersionResult GetAppVersion()
{
// 与 Windows 的 Application.ProductVersion 语义一致:取程序集 InformationalVersion
//(由 PKGBUILD 在 publish 时通过 -p:InformationalVersion 注入 git 派生的版本号),
// 去掉 SourceLink 可能附带的 "+<commit>" 后缀。
var asm = System.Reflection.Assembly.GetExecutingAssembly();
var info = (System.Reflection.AssemblyInformationalVersionAttribute?)System.Attribute
.GetCustomAttribute(asm, typeof(System.Reflection.AssemblyInformationalVersionAttribute));
var version = info?.InformationalVersion?.Split('+')[0] ?? "linux";
return new AppVersionResult(version, settings.gameVersion, LicenseStatus.Active, VideoConvert.HardwareAcceleration, VideoConvert.H264Encoder, StaticSettings.CurrentLocale);
}
#endif
}
29 changes: 27 additions & 2 deletions MaiChartManager/Controllers/App/LocaleController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace MaiChartManager.Controllers.App;

[ApiController]
[Route("MaiChartManagerServlet/[action]Api")]
public class LocaleController(StaticSettings settings, ILogger<LocaleController> logger) : ControllerBase
public class LocaleController(StaticSettings settings, ILogger<LocaleController> logger, MaiChartManager.Platform.IAppShell appShell) : ControllerBase
{
[HttpGet]
public string GetCurrentLocale()
Expand All @@ -17,6 +17,31 @@ public string GetCurrentLocale()
[HttpPost]
public void SetLocale([FromBody] string locale)
{
AppMain.SetLocale(locale);
if (locale != "zh" && locale != "zh-TW" && locale != "en")
{
throw new ArgumentException("Invalid locale. Must be 'zh', 'zh-TW', or 'en'");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Invalid locale input is handled by throwing an exception, producing error-path responses instead of a client validation response. Return BadRequest/ValidationProblem for unsupported locale values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At MaiChartManager/Controllers/App/LocaleController.cs, line 22:

<comment>Invalid locale input is handled by throwing an exception, producing error-path responses instead of a client validation response. Return `BadRequest`/`ValidationProblem` for unsupported locale values.</comment>

<file context>
@@ -17,6 +17,31 @@ public string GetCurrentLocale()
-        AppMain.SetLocale(locale);
+        if (locale != "zh" && locale != "zh-TW" && locale != "en")
+        {
+            throw new ArgumentException("Invalid locale. Must be 'zh', 'zh-TW', or 'en'");
+        }
+
</file context>

}

StaticSettings.CurrentLocale = locale;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Locale application logic is duplicated across controller/startup paths, increasing drift risk when locale rules change. Centralize this block in one shared method/service and call it here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At MaiChartManager/Controllers/App/LocaleController.cs, line 25:

<comment>Locale application logic is duplicated across controller/startup paths, increasing drift risk when locale rules change. Centralize this block in one shared method/service and call it here.</comment>

<file context>
@@ -17,6 +17,31 @@ public string GetCurrentLocale()
+            throw new ArgumentException("Invalid locale. Must be 'zh', 'zh-TW', or 'en'");
+        }
+
+        StaticSettings.CurrentLocale = locale;
+        StaticSettings.Config.Locale = locale;
+
</file context>

StaticSettings.Config.Locale = locale;

// 设置 Locale 资源管理器的 Culture(这会影响所有线程)
var culture = locale switch
{
"zh" => new CultureInfo("zh-CN"),
"zh-TW" => new CultureInfo("zh-TW"),
_ => new CultureInfo("en-US"),
};
Locale.Culture = culture;
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;

// 给外部依赖库设置Locale
MuConvert.utils.Utils.SetLocale(new CultureInfo(locale));

StaticSettings.Config.Save();

// 刷新原生 UI(Windows: 窗口/托盘;Linux: no-op)
appShell.ReloadLocale(locale);
}
}
Loading