From 41dd7593798155e554a914699bacb97d17a92d6d Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 19:54:02 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(godot):=20=E6=B7=BB=E5=8A=A0Godot?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=8C=96=E8=AE=BE=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增LocalizationMap类实现用户语言到Godot locale和框架语言码的映射 - 创建GodotLocalizationSettings类同步应用本地化设置到Godot引擎和GFramework框架 - 添加测试项目配置文件GFramework.Game.Tests.csproj - 实现本地化设置的单元测试验证语言同步功能 --- .../GFramework.Game.Tests.csproj | 1 + .../Setting/GodotLocalizationSettingsTests.cs | 71 +++++++++++++++ .../Setting/Data/LocalizationMap.cs | 44 ++++++++- .../Setting/GodotLocalizationSettings.cs | 90 ++++++++++++++++--- 4 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 GFramework.Game.Tests/Setting/GodotLocalizationSettingsTests.cs diff --git a/GFramework.Game.Tests/GFramework.Game.Tests.csproj b/GFramework.Game.Tests/GFramework.Game.Tests.csproj index 6ad45dbe..9b6d0bdf 100644 --- a/GFramework.Game.Tests/GFramework.Game.Tests.csproj +++ b/GFramework.Game.Tests/GFramework.Game.Tests.csproj @@ -19,6 +19,7 @@ + diff --git a/GFramework.Game.Tests/Setting/GodotLocalizationSettingsTests.cs b/GFramework.Game.Tests/Setting/GodotLocalizationSettingsTests.cs new file mode 100644 index 00000000..ee13a7c1 --- /dev/null +++ b/GFramework.Game.Tests/Setting/GodotLocalizationSettingsTests.cs @@ -0,0 +1,71 @@ +using GFramework.Godot.Setting; +using GFramework.Godot.Setting.Data; + +namespace GFramework.Game.Tests.Setting; + +/// +/// 覆盖 Godot 本地化设置应用器的语言同步行为,防止持久化语言仅影响 Godot 而未同步框架管理器。 +/// +[TestFixture] +public sealed class GodotLocalizationSettingsTests +{ + [Test] + public async Task Apply_ShouldSyncEnglishToGodotLocaleAndFrameworkLanguage() + { + var manager = new Mock(MockBehavior.Strict); + manager.Setup(it => it.SetLanguage("eng")); + string? appliedLocale = null; + + var applicator = CreateApplicator("English", manager.Object, locale => appliedLocale = locale); + + await applicator.Apply(); + + Assert.That(appliedLocale, Is.EqualTo("en")); + manager.Verify(it => it.SetLanguage("eng"), Times.Once); + } + + [Test] + public async Task Apply_ShouldSyncChineseToGodotLocaleAndFrameworkLanguage() + { + var manager = new Mock(MockBehavior.Strict); + manager.Setup(it => it.SetLanguage("zhs")); + string? appliedLocale = null; + + var applicator = CreateApplicator("简体中文", manager.Object, locale => appliedLocale = locale); + + await applicator.Apply(); + + Assert.That(appliedLocale, Is.EqualTo("zh_CN")); + manager.Verify(it => it.SetLanguage("zhs"), Times.Once); + } + + [Test] + public async Task Apply_ShouldFallbackUnknownLanguageToEnglish() + { + var manager = new Mock(MockBehavior.Strict); + manager.Setup(it => it.SetLanguage("eng")); + string? appliedLocale = null; + + var applicator = CreateApplicator("Esperanto", manager.Object, locale => appliedLocale = locale); + + await applicator.Apply(); + + Assert.That(appliedLocale, Is.EqualTo("en")); + manager.Verify(it => it.SetLanguage("eng"), Times.Once); + } + + private static GodotLocalizationSettings CreateApplicator( + string language, + ILocalizationManager manager, + Action applyGodotLocale) + { + var settingsModel = new Mock(MockBehavior.Strict); + settingsModel.Setup(it => it.GetData()).Returns(new LocalizationSettings + { + Language = language + }); + + return new GodotLocalizationSettings(settingsModel.Object, new LocalizationMap(), () => manager, + applyGodotLocale); + } +} \ No newline at end of file diff --git a/GFramework.Godot/Setting/Data/LocalizationMap.cs b/GFramework.Godot/Setting/Data/LocalizationMap.cs index a5cface8..1864f4ae 100644 --- a/GFramework.Godot/Setting/Data/LocalizationMap.cs +++ b/GFramework.Godot/Setting/Data/LocalizationMap.cs @@ -18,12 +18,54 @@ namespace GFramework.Godot.Setting.Data; /// public class LocalizationMap { + private const string DefaultFrameworkLanguage = "eng"; + private const string DefaultGodotLocale = "en"; + /// - /// 用户语言 -> Godot locale 映射表 + /// 用户语言 -> Godot locale 映射表。 /// public Dictionary LanguageMap { get; set; } = new() { { "简体中文", "zh_CN" }, { "English", "en" } }; + + /// + /// 用户语言 -> GFramework 本地化语言码映射表。 + /// + public Dictionary FrameworkLanguageMap { get; set; } = new() + { + { "简体中文", "zhs" }, + { "English", "eng" } + }; + + /// + /// 解析用户保存的语言值对应的 Godot locale。 + /// + /// 设置系统中保存的语言值。 + /// 对应的 Godot locale;未知值时回退为英文。 + public string ResolveGodotLocale(string? storedLanguage) + { + if (string.IsNullOrWhiteSpace(storedLanguage)) + { + return DefaultGodotLocale; + } + + return LanguageMap.GetValueOrDefault(storedLanguage, DefaultGodotLocale); + } + + /// + /// 解析用户保存的语言值对应的框架语言码。 + /// + /// 设置系统中保存的语言值。 + /// 对应的框架语言码;未知值时回退为英文。 + public string ResolveFrameworkLanguage(string? storedLanguage) + { + if (string.IsNullOrWhiteSpace(storedLanguage)) + { + return DefaultFrameworkLanguage; + } + + return FrameworkLanguageMap.GetValueOrDefault(storedLanguage, DefaultFrameworkLanguage); + } } \ No newline at end of file diff --git a/GFramework.Godot/Setting/GodotLocalizationSettings.cs b/GFramework.Godot/Setting/GodotLocalizationSettings.cs index abbc9bfd..a4e31b7f 100644 --- a/GFramework.Godot/Setting/GodotLocalizationSettings.cs +++ b/GFramework.Godot/Setting/GodotLocalizationSettings.cs @@ -11,32 +11,82 @@ // See the License for the specific language governing permissions and // limitations under the License. +using GFramework.Core.Abstractions.Localization; using GFramework.Game.Abstractions.Setting; using GFramework.Game.Abstractions.Setting.Data; using GFramework.Godot.Setting.Data; -using Godot; namespace GFramework.Godot.Setting; /// -/// Godot本地化设置类,负责应用本地化配置到Godot引擎 +/// Godot 本地化设置类,负责将持久化语言配置同时应用到 Godot 引擎与 GFramework 本地化管理器。 /// -/// 设置模型 -/// 本地化映射表 -public class GodotLocalizationSettings(ISettingsModel model, LocalizationMap localizationMap) - : IResetApplyAbleSettings +public class GodotLocalizationSettings : IResetApplyAbleSettings { + private readonly Action _applyGodotLocale; + private readonly Func _localizationManagerResolver; + private readonly LocalizationMap _localizationMap; + private readonly ISettingsModel _model; + /// - /// 应用本地化设置到Godot引擎 + /// 初始化 Godot 本地化设置应用器,并默认从当前架构上下文解析框架本地化管理器。 + /// + /// 设置模型。 + /// 本地化映射表。 + public GodotLocalizationSettings(ISettingsModel model, LocalizationMap localizationMap) + : this(model, localizationMap, TryResolveLocalizationManager, TranslationServer.SetLocale) + { + } + + /// + /// 初始化 Godot 本地化设置应用器。 + /// + /// 设置模型。 + /// 本地化映射表。 + /// 框架本地化管理器解析器。 + public GodotLocalizationSettings( + ISettingsModel model, + LocalizationMap localizationMap, + Func localizationManagerResolver) + : this(model, localizationMap, localizationManagerResolver, TranslationServer.SetLocale) + { + } + + /// + /// 初始化 Godot 本地化设置应用器,并显式指定 Godot locale 应用动作。 + /// 该重载主要用于测试或自定义引擎桥接。 + /// + /// 设置模型。 + /// 本地化映射表。 + /// 框架本地化管理器解析器。 + /// Godot locale 应用动作。 + public GodotLocalizationSettings( + ISettingsModel model, + LocalizationMap localizationMap, + Func localizationManagerResolver, + Action applyGodotLocale) + { + _model = model ?? throw new ArgumentNullException(nameof(model)); + _localizationMap = localizationMap ?? throw new ArgumentNullException(nameof(localizationMap)); + _localizationManagerResolver = + localizationManagerResolver ?? throw new ArgumentNullException(nameof(localizationManagerResolver)); + _applyGodotLocale = applyGodotLocale ?? throw new ArgumentNullException(nameof(applyGodotLocale)); + } + + /// + /// 应用本地化设置到 Godot 引擎与 GFramework 本地化管理器。 /// /// 完成的任务 public Task Apply() { - var settings = model.GetData(); - // 尝试从映射表获取 Godot locale - var locale = localizationMap.LanguageMap.GetValueOrDefault(settings.Language, "en"); - // 默认值 - TranslationServer.SetLocale(locale); + var settings = _model.GetData(); + var locale = _localizationMap.ResolveGodotLocale(settings.Language); + var frameworkLanguage = _localizationMap.ResolveFrameworkLanguage(settings.Language); + + _applyGodotLocale(locale); + + // 设置系统持久化的是用户可见语言值;这里需要同步框架语言码,避免 Godot 与框架状态分裂。 + _localizationManagerResolver()?.SetLanguage(frameworkLanguage); return Task.CompletedTask; } @@ -45,18 +95,30 @@ public class GodotLocalizationSettings(ISettingsModel model, LocalizationMap loc /// public void Reset() { - model.GetData().Reset(); + _model.GetData().Reset(); } /// /// 获取本地化设置的数据对象。 /// 该属性提供对本地化设置数据的只读访问。 /// - public ISettingsData Data { get; } = model.GetData(); + public ISettingsData Data => _model.GetData(); /// /// 获取本地化设置数据的类型。 /// 该属性返回本地化设置数据的具体类型信息。 /// public Type DataType { get; } = typeof(LocalizationSettings); + + private static ILocalizationManager? TryResolveLocalizationManager() + { + try + { + return GameContext.GetFirstArchitectureContext().GetSystem(); + } + catch (InvalidOperationException) + { + return null; + } + } } \ No newline at end of file From d588372cffcddd145057b49ba0d14a7b9bca25fc Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 19:54:09 +0800 Subject: [PATCH 2/3] =?UTF-8?q?docs(godot):=20=E6=B7=BB=E5=8A=A0=20Godot?= =?UTF-8?q?=20=E8=AE=BE=E7=BD=AE=E6=A8=A1=E5=9D=97=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 Godot 设置模块概述和核心类说明 - 添加音频设置系统详细文档包括 AudioBusMap 和 GodotAudioSettings - 添加图形设置系统文档包括 GodotGraphicsSettings 实现 - 添加本地化设置系统文档包括 LocalizationMap 和 GodotLocalizationSettings - 提供完整的架构设计图和使用示例代码 - 添加 API 详细说明和技术实现细节 - 包含最佳实践、性能考虑和故障排除指南 --- docs/zh-CN/godot/setting.md | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/docs/zh-CN/godot/setting.md b/docs/zh-CN/godot/setting.md index 385512ae..5a323626 100644 --- a/docs/zh-CN/godot/setting.md +++ b/docs/zh-CN/godot/setting.md @@ -3,7 +3,7 @@ ## 概述 Godot 设置模块是 GFramework.Godot 的核心组件之一,专门为 Godot 引擎提供游戏设置系统的实现。该模块将通用的设置框架与 Godot -引擎的特定功能相结合,提供了音频设置和图形设置的完整解决方案。 +引擎的特定功能相结合,提供了音频设置、图形设置和本地化设置的完整解决方案。 ## 核心类 @@ -61,23 +61,55 @@ Godot 图形设置实现类,继承自 GraphicsSettings 并实现 IApplyAbleSet - 窗口位置自动居中 - 多显示器支持 +### 本地化设置系统 + +#### LocalizationMap + +本地化映射配置类,用于把设置系统中保存的用户可见语言值解析为: + +- Godot `TranslationServer` 使用的 locale +- GFramework `ILocalizationManager` 使用的语言码 + +默认映射如下: + +- `"简体中文"` -> Godot `zh_CN`,框架语言码 `zhs` +- `"English"` -> Godot `en`,框架语言码 `eng` + +未知语言值会稳定回退到英文,避免重启后出现设置值与运行时语言状态不一致。 + +#### GodotLocalizationSettings + +Godot 本地化设置实现类,负责把 `LocalizationSettings` 同时应用到 Godot 引擎与 GFramework 本地化管理器。 + +**功能:** + +- 将语言设置应用到 `TranslationServer.SetLocale(...)` +- 同步 `ILocalizationManager.SetLanguage(...)` +- 通过统一映射避免 Godot locale 与框架语言码分裂 + ## 架构设计 ```mermaid graph TD A[AudioSettings] --> B[GodotAudioSettings] C[GraphicsSettings] --> D[GodotGraphicsSettings] - E[IApplyAbleSettings] --> B - E --> D + E[LocalizationSettings] --> F[GodotLocalizationSettings] + G[IApplyAbleSettings] --> B + G --> D + G --> F - G[AudioBusMap] --> B + H[AudioBusMap] --> B + I[LocalizationMap] --> F - B --> I[AudioServer API] - D --> J[DisplayServer API] + B --> J[AudioServer API] + D --> K[DisplayServer API] + F --> L[TranslationServer API] + F --> M[ILocalizationManager] - K[SettingsSystem] --> L[Apply Method] - L --> B - L --> D + N[SettingsSystem] --> O[Apply Method] + O --> B + O --> D + O --> F ``` ## 使用示例 @@ -568,4 +600,4 @@ var windowMode = DisplayServer.WindowGetMode(); GD.Print($"Screen: {screenSize}"); GD.Print($"Window: {windowSize} at {windowPos}"); GD.Print($"Mode: {windowMode}"); -``` \ No newline at end of file +``` From e84af1512d73502de35835edaaea2caa0e47b4c5 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 5 Apr 2026 20:08:28 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(setting):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=8C=96=E8=AE=BE=E7=BD=AE=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 GodotLocalizationSettingsTests 中添加核心本地化接口引用 - 为 GodotLocalizationSettings 添加架构依赖项 - 配置 Godot 全局命名空间以便更简单的 API 访问 - 更新测试文件中的设置数据抽象层依赖项 - 简化 Godot 项目中的全局 using 指令结构 --- .../Setting/GodotLocalizationSettingsTests.cs | 3 +++ GFramework.Godot/GlobalUsings.cs | 3 ++- GFramework.Godot/Setting/GodotLocalizationSettings.cs | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/GFramework.Game.Tests/Setting/GodotLocalizationSettingsTests.cs b/GFramework.Game.Tests/Setting/GodotLocalizationSettingsTests.cs index ee13a7c1..2d651627 100644 --- a/GFramework.Game.Tests/Setting/GodotLocalizationSettingsTests.cs +++ b/GFramework.Game.Tests/Setting/GodotLocalizationSettingsTests.cs @@ -1,3 +1,6 @@ +using GFramework.Core.Abstractions.Localization; +using GFramework.Game.Abstractions.Setting; +using GFramework.Game.Abstractions.Setting.Data; using GFramework.Godot.Setting; using GFramework.Godot.Setting.Data; diff --git a/GFramework.Godot/GlobalUsings.cs b/GFramework.Godot/GlobalUsings.cs index 4d271811..41d45db7 100644 --- a/GFramework.Godot/GlobalUsings.cs +++ b/GFramework.Godot/GlobalUsings.cs @@ -15,4 +15,5 @@ global using System; global using System.Collections.Generic; global using System.Linq; global using System.Threading; -global using System.Threading.Tasks; \ No newline at end of file +global using System.Threading.Tasks; +global using Godot; \ No newline at end of file diff --git a/GFramework.Godot/Setting/GodotLocalizationSettings.cs b/GFramework.Godot/Setting/GodotLocalizationSettings.cs index a4e31b7f..ddcfd6fc 100644 --- a/GFramework.Godot/Setting/GodotLocalizationSettings.cs +++ b/GFramework.Godot/Setting/GodotLocalizationSettings.cs @@ -12,6 +12,7 @@ // limitations under the License. using GFramework.Core.Abstractions.Localization; +using GFramework.Core.Architectures; using GFramework.Game.Abstractions.Setting; using GFramework.Game.Abstractions.Setting.Data; using GFramework.Godot.Setting.Data;