mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-14 06:34:30 +08:00
Compare commits
No commits in common. "46ea6f1ffdd3cfc55ffb7e4dfa3acfdd53b84fb2" and "a22e522cf9af7bbad24821a388b815b10cec240c" have entirely different histories.
46ea6f1ffd
...
a22e522cf9
6
.github/workflows/license-compliance.yml
vendored
6
.github/workflows/license-compliance.yml
vendored
@ -5,10 +5,6 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
@ -118,4 +114,4 @@ jobs:
|
|||||||
sbom-cyclonedx-validation.txt
|
sbom-cyclonedx-validation.txt
|
||||||
license-compliance.zip
|
license-compliance.zip
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
189
.github/workflows/publish.yml
vendored
189
.github/workflows/publish.yml
vendored
@ -1,37 +1,30 @@
|
|||||||
# 发布工作流(NuGet + GitHub Packages + GitHub Release)
|
# 发布工作流(NuGet + GitHub Release)
|
||||||
#
|
#
|
||||||
# 功能:当推送标签时自动构建、打包,并将相同产物并发发布到 NuGet.org 与 GitHub Packages,
|
# 功能:当推送标签时自动构建、打包并发布到 NuGet.org 和 GitHub Release
|
||||||
# 最后创建 GitHub Release。
|
|
||||||
# 触发条件:推送任何标签(如 v1.0.0 或 1.0.0)
|
# 触发条件:推送任何标签(如 v1.0.0 或 1.0.0)
|
||||||
# 权限:允许写入内容、包和使用 OIDC 身份验证
|
# 权限:允许写入内容、包和使用 OIDC 身份验证
|
||||||
name: Publish (NuGet + GitHub Packages + GitHub Release)
|
name: Publish (NuGet + GitHub Release)
|
||||||
|
|
||||||
|
# 触发:推送 tag 时触发(例如 v1.0.0 或 1.0.0)
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
concurrency:
|
# 顶级权限:允许创建 release、写 packages,并允许 id-token(OIDC)
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-pack:
|
build-and-publish:
|
||||||
name: Build And Pack
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
|
||||||
packages: read
|
|
||||||
id-token: write
|
id-token: write
|
||||||
|
contents: write
|
||||||
outputs:
|
packages: write
|
||||||
package_version: ${{ steps.tag_version.outputs.version }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository (at tag)
|
- name: Checkout repository (at tag)
|
||||||
@ -45,17 +38,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
dotnet-version: 10.0.x
|
dotnet-version: 10.0.x
|
||||||
|
|
||||||
|
- name: Install unzip (for reading .nuspec from .nupkg)
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y unzip
|
||||||
- name: Cache NuGet packages
|
- name: Cache NuGet packages
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ~/.nuget/packages
|
path: ~/.nuget/packages
|
||||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||||
|
|
||||||
- name: Restore dependencies
|
- name: Restore dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
|
|
||||||
# 从 GitHub 引用中提取标签版本。
|
# 从 GitHub 引用中提取标签版本
|
||||||
# 提取逻辑:去除 refs/tags/ 前缀,然后去除 v/V 前缀。
|
# 提取逻辑:去除 refs/tags/ 前缀,然后去除 v/V 前缀
|
||||||
|
# 输出:version - 处理后的版本号
|
||||||
- name: Determine tag version
|
- name: Determine tag version
|
||||||
id: tag_version
|
id: tag_version
|
||||||
run: |
|
run: |
|
||||||
@ -65,47 +60,26 @@ jobs:
|
|||||||
VERSION=${TAG#v}
|
VERSION=${TAG#v}
|
||||||
VERSION=${VERSION#V}
|
VERSION=${VERSION#V}
|
||||||
echo "tag='$TAG' -> version='$VERSION'"
|
echo "tag='$TAG' -> version='$VERSION'"
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Pack (use tag version)
|
- name: Pack (use tag version)
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
echo "Packing with version=${{ steps.tag_version.outputs.version }}"
|
echo "Packing with version=${{ steps.tag_version.outputs.version }}"
|
||||||
dotnet pack -c Release -o ./packages -p:PackageVersion=${{ steps.tag_version.outputs.version }} -p:IncludeSymbols=false
|
dotnet pack -c Release -o ./packages -p:PackageVersion=${{ steps.tag_version.outputs.version }} -p:IncludeSymbols=false
|
||||||
|
# 上传许可证合规相关的工件文件,包括通知文件、第三方许可证、SBOM文件及验证结果
|
||||||
- name: Show packages
|
- name: Upload compliance artifacts
|
||||||
run: ls -la ./packages || true
|
|
||||||
|
|
||||||
# 上传 nupkg 工件,供多个发布 job 复用,避免重复打包。
|
|
||||||
- name: Upload package artifacts
|
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: packages
|
name: license-compliance
|
||||||
path: ./packages/*.nupkg
|
path: |
|
||||||
|
NOTICE
|
||||||
publish-nuget:
|
THIRD_PARTY_LICENSES.md
|
||||||
name: Publish To NuGet.org
|
sbom.spdx.json
|
||||||
runs-on: ubuntu-latest
|
sbom.cyclonedx.json
|
||||||
needs: build-pack
|
sbom-spdx-validation.txt
|
||||||
|
sbom-cyclonedx-validation.txt
|
||||||
permissions:
|
- name: Show packages
|
||||||
contents: read
|
|
||||||
packages: read
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v5
|
|
||||||
with:
|
|
||||||
dotnet-version: 10.0.x
|
|
||||||
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@v5
|
|
||||||
with:
|
|
||||||
name: packages
|
|
||||||
path: ./packages
|
|
||||||
|
|
||||||
- name: Show downloaded packages
|
|
||||||
run: ls -la ./packages || true
|
run: ls -la ./packages || true
|
||||||
|
|
||||||
- name: NuGet login (OIDC → temporary API key)
|
- name: NuGet login (OIDC → temporary API key)
|
||||||
@ -114,8 +88,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
user: ${{ secrets.NUGET_USER }}
|
user: ${{ secrets.NUGET_USER }}
|
||||||
|
|
||||||
# 将所有生成的包推送到 nuget.org。
|
# 将所有生成的包推送到 nuget.org
|
||||||
# 使用临时 API 密钥进行身份验证,并跳过重复包上传。
|
# 使用临时 API 密钥进行身份验证
|
||||||
|
# 跳过重复包的上传
|
||||||
- name: Push all packages to nuget.org
|
- name: Push all packages to nuget.org
|
||||||
env:
|
env:
|
||||||
NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }}
|
NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }}
|
||||||
@ -136,78 +111,35 @@ jobs:
|
|||||||
echo "No packages found to push."
|
echo "No packages found to push."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
publish-github-packages:
|
# 从 .nupkg 文件中提取版本信息
|
||||||
name: Publish To GitHub Packages
|
# 通过解压 .nupkg(zip 格式)并读取 .nuspec 文件来获取版本
|
||||||
runs-on: ubuntu-latest
|
# 输出:
|
||||||
needs: build-pack
|
# package_file - 第一个找到的包文件路径
|
||||||
|
# package_basename - 包文件的基本名称
|
||||||
permissions:
|
# version - 从 nuspec 中解析出的版本号
|
||||||
contents: read
|
- name: Get Version and First Package Path
|
||||||
packages: write
|
id: get_version
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v5
|
|
||||||
with:
|
|
||||||
dotnet-version: 10.0.x
|
|
||||||
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@v5
|
|
||||||
with:
|
|
||||||
name: packages
|
|
||||||
path: ./packages
|
|
||||||
|
|
||||||
- name: Show downloaded packages
|
|
||||||
run: ls -la ./packages || true
|
|
||||||
|
|
||||||
# 使用仓库内建的 GITHUB_TOKEN 配置 GitHub Packages NuGet 源。
|
|
||||||
- name: Configure GitHub Packages source
|
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
dotnet nuget add source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \
|
PACKAGE_FILE=$(find ./packages -name "*.nupkg" | head -n 1 || true)
|
||||||
--name github \
|
if [ -z "$PACKAGE_FILE" ]; then
|
||||||
--username "${{ github.repository_owner }}" \
|
echo "No .nupkg file found in ./packages"
|
||||||
--password "${{ github.token }}" \
|
exit 1
|
||||||
--store-password-in-clear-text
|
|
||||||
|
|
||||||
- name: Push all packages to GitHub Packages
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
pushed_any=false
|
|
||||||
for PKG in ./packages/*.nupkg; do
|
|
||||||
[ -f "$PKG" ] || continue
|
|
||||||
pushed_any=true
|
|
||||||
echo "Pushing $PKG to GitHub Packages..."
|
|
||||||
dotnet nuget push "$PKG" \
|
|
||||||
--source github \
|
|
||||||
--skip-duplicate
|
|
||||||
done
|
|
||||||
if [ "$pushed_any" = false ]; then
|
|
||||||
echo "No packages found to push."
|
|
||||||
fi
|
fi
|
||||||
|
# 从 .nupkg(zip)里读取 .nuspec 并提取 <version>
|
||||||
|
VERSION=$(unzip -p "$PACKAGE_FILE" '*.nuspec' 2>/dev/null | sed -n 's:.*<version>\(.*\)</version>.*:\1:p' | head -n1)
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
echo "Failed to parse version from $PACKAGE_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BASENAME=$(basename "$PACKAGE_FILE")
|
||||||
|
echo "package_file=$PACKAGE_FILE" >> $GITHUB_OUTPUT
|
||||||
|
echo "package_basename=$BASENAME" >> $GITHUB_OUTPUT
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
create-release:
|
# 创建 GitHub Release
|
||||||
name: Create GitHub Release
|
# 使用从包中提取的版本信息和当前标签创建发布
|
||||||
runs-on: ubuntu-latest
|
# 发布包含描述信息和版本详情
|
||||||
needs:
|
|
||||||
- build-pack
|
|
||||||
- publish-nuget
|
|
||||||
- publish-github-packages
|
|
||||||
if: ${{ always() && needs.build-pack.result == 'success' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
packages: read
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Download package artifacts
|
|
||||||
uses: actions/download-artifact@v5
|
|
||||||
with:
|
|
||||||
name: packages
|
|
||||||
path: ./packages
|
|
||||||
|
|
||||||
# 无论某一侧包源发布是否失败,都继续创建 Release。
|
|
||||||
# 合规工件由独立 workflow 生成,当前发布流不再假设这些文件在同一次运行中可用。
|
|
||||||
- name: Create GitHub Release and Upload Assets
|
- name: Create GitHub Release and Upload Assets
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
@ -215,10 +147,21 @@ jobs:
|
|||||||
name: "Release ${{ github.ref_name }}"
|
name: "Release ${{ github.ref_name }}"
|
||||||
body: |
|
body: |
|
||||||
Release created by CI for tag ${{ github.ref_name }}
|
Release created by CI for tag ${{ github.ref_name }}
|
||||||
Package version: ${{ needs.build-pack.outputs.package_version }}
|
Package version: ${{ steps.get_version.outputs.version }}
|
||||||
|
|
||||||
|
## Compliance
|
||||||
|
- NOTICE
|
||||||
|
- THIRD_PARTY_LICENSES
|
||||||
|
- SPDX & CycloneDX SBOM
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: |
|
files: |
|
||||||
./packages/*.nupkg
|
./packages/*.nupkg
|
||||||
|
NOTICE
|
||||||
|
THIRD_PARTY_LICENSES.md
|
||||||
|
sbom.spdx.json
|
||||||
|
sbom.cyclonedx.json
|
||||||
|
sbom-spdx-validation.txt
|
||||||
|
sbom-cyclonedx-validation.txt
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -19,7 +19,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\GFramework.Game\GFramework.Game.csproj"/>
|
<ProjectReference Include="..\GFramework.Game\GFramework.Game.csproj"/>
|
||||||
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
|
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
|
||||||
<ProjectReference Include="..\GFramework.Godot\GFramework.Godot.csproj"/>
|
|
||||||
<ProjectReference Include="..\GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj"
|
<ProjectReference Include="..\GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj"
|
||||||
OutputItemType="Analyzer"
|
OutputItemType="Analyzer"
|
||||||
ReferenceOutputAssembly="false"/>
|
ReferenceOutputAssembly="false"/>
|
||||||
|
|||||||
@ -1,74 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
namespace GFramework.Game.Tests.Setting;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 覆盖 Godot 本地化设置应用器的语言同步行为,防止持久化语言仅影响 Godot 而未同步框架管理器。
|
|
||||||
/// </summary>
|
|
||||||
[TestFixture]
|
|
||||||
public sealed class GodotLocalizationSettingsTests
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public async Task Apply_ShouldSyncEnglishToGodotLocaleAndFrameworkLanguage()
|
|
||||||
{
|
|
||||||
var manager = new Mock<ILocalizationManager>(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<ILocalizationManager>(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<ILocalizationManager>(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<string> applyGodotLocale)
|
|
||||||
{
|
|
||||||
var settingsModel = new Mock<ISettingsModel>(MockBehavior.Strict);
|
|
||||||
settingsModel.Setup(it => it.GetData<LocalizationSettings>()).Returns(new LocalizationSettings
|
|
||||||
{
|
|
||||||
Language = language
|
|
||||||
});
|
|
||||||
|
|
||||||
return new GodotLocalizationSettings(settingsModel.Object, new LocalizationMap(), () => manager,
|
|
||||||
applyGodotLocale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -16,4 +16,3 @@ global using System.Collections.Generic;
|
|||||||
global using System.Linq;
|
global using System.Linq;
|
||||||
global using System.Threading;
|
global using System.Threading;
|
||||||
global using System.Threading.Tasks;
|
global using System.Threading.Tasks;
|
||||||
global using Godot;
|
|
||||||
@ -18,54 +18,12 @@ namespace GFramework.Godot.Setting.Data;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class LocalizationMap
|
public class LocalizationMap
|
||||||
{
|
{
|
||||||
private const string DefaultFrameworkLanguage = "eng";
|
|
||||||
private const string DefaultGodotLocale = "en";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户语言 -> Godot locale 映射表。
|
/// 用户语言 -> Godot locale 映射表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, string> LanguageMap { get; set; } = new()
|
public Dictionary<string, string> LanguageMap { get; set; } = new()
|
||||||
{
|
{
|
||||||
{ "简体中文", "zh_CN" },
|
{ "简体中文", "zh_CN" },
|
||||||
{ "English", "en" }
|
{ "English", "en" }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户语言 -> GFramework 本地化语言码映射表。
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, string> FrameworkLanguageMap { get; set; } = new()
|
|
||||||
{
|
|
||||||
{ "简体中文", "zhs" },
|
|
||||||
{ "English", "eng" }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析用户保存的语言值对应的 Godot locale。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="storedLanguage">设置系统中保存的语言值。</param>
|
|
||||||
/// <returns>对应的 Godot locale;未知值时回退为英文。</returns>
|
|
||||||
public string ResolveGodotLocale(string? storedLanguage)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(storedLanguage))
|
|
||||||
{
|
|
||||||
return DefaultGodotLocale;
|
|
||||||
}
|
|
||||||
|
|
||||||
return LanguageMap.GetValueOrDefault(storedLanguage, DefaultGodotLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析用户保存的语言值对应的框架语言码。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="storedLanguage">设置系统中保存的语言值。</param>
|
|
||||||
/// <returns>对应的框架语言码;未知值时回退为英文。</returns>
|
|
||||||
public string ResolveFrameworkLanguage(string? storedLanguage)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(storedLanguage))
|
|
||||||
{
|
|
||||||
return DefaultFrameworkLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FrameworkLanguageMap.GetValueOrDefault(storedLanguage, DefaultFrameworkLanguage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -11,83 +11,32 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
using GFramework.Core.Abstractions.Localization;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.Game.Abstractions.Setting;
|
using GFramework.Game.Abstractions.Setting;
|
||||||
using GFramework.Game.Abstractions.Setting.Data;
|
using GFramework.Game.Abstractions.Setting.Data;
|
||||||
using GFramework.Godot.Setting.Data;
|
using GFramework.Godot.Setting.Data;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
namespace GFramework.Godot.Setting;
|
namespace GFramework.Godot.Setting;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Godot 本地化设置类,负责将持久化语言配置同时应用到 Godot 引擎与 GFramework 本地化管理器。
|
/// Godot本地化设置类,负责应用本地化配置到Godot引擎
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GodotLocalizationSettings : IResetApplyAbleSettings
|
/// <param name="model">设置模型</param>
|
||||||
|
/// <param name="localizationMap">本地化映射表</param>
|
||||||
|
public class GodotLocalizationSettings(ISettingsModel model, LocalizationMap localizationMap)
|
||||||
|
: IResetApplyAbleSettings
|
||||||
{
|
{
|
||||||
private readonly Action<string> _applyGodotLocale;
|
|
||||||
private readonly Func<ILocalizationManager?> _localizationManagerResolver;
|
|
||||||
private readonly LocalizationMap _localizationMap;
|
|
||||||
private readonly ISettingsModel _model;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化 Godot 本地化设置应用器,并默认从当前架构上下文解析框架本地化管理器。
|
/// 应用本地化设置到Godot引擎
|
||||||
/// </summary>
|
|
||||||
/// <param name="model">设置模型。</param>
|
|
||||||
/// <param name="localizationMap">本地化映射表。</param>
|
|
||||||
public GodotLocalizationSettings(ISettingsModel model, LocalizationMap localizationMap)
|
|
||||||
: this(model, localizationMap, TryResolveLocalizationManager, TranslationServer.SetLocale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化 Godot 本地化设置应用器。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="model">设置模型。</param>
|
|
||||||
/// <param name="localizationMap">本地化映射表。</param>
|
|
||||||
/// <param name="localizationManagerResolver">框架本地化管理器解析器。</param>
|
|
||||||
public GodotLocalizationSettings(
|
|
||||||
ISettingsModel model,
|
|
||||||
LocalizationMap localizationMap,
|
|
||||||
Func<ILocalizationManager?> localizationManagerResolver)
|
|
||||||
: this(model, localizationMap, localizationManagerResolver, TranslationServer.SetLocale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化 Godot 本地化设置应用器,并显式指定 Godot locale 应用动作。
|
|
||||||
/// 该重载主要用于测试或自定义引擎桥接。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="model">设置模型。</param>
|
|
||||||
/// <param name="localizationMap">本地化映射表。</param>
|
|
||||||
/// <param name="localizationManagerResolver">框架本地化管理器解析器。</param>
|
|
||||||
/// <param name="applyGodotLocale">Godot locale 应用动作。</param>
|
|
||||||
public GodotLocalizationSettings(
|
|
||||||
ISettingsModel model,
|
|
||||||
LocalizationMap localizationMap,
|
|
||||||
Func<ILocalizationManager?> localizationManagerResolver,
|
|
||||||
Action<string> 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 应用本地化设置到 Godot 引擎与 GFramework 本地化管理器。
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>完成的任务</returns>
|
/// <returns>完成的任务</returns>
|
||||||
public Task Apply()
|
public Task Apply()
|
||||||
{
|
{
|
||||||
var settings = _model.GetData<LocalizationSettings>();
|
var settings = model.GetData<LocalizationSettings>();
|
||||||
var locale = _localizationMap.ResolveGodotLocale(settings.Language);
|
// 尝试从映射表获取 Godot locale
|
||||||
var frameworkLanguage = _localizationMap.ResolveFrameworkLanguage(settings.Language);
|
var locale = localizationMap.LanguageMap.GetValueOrDefault(settings.Language, "en");
|
||||||
|
// 默认值
|
||||||
_applyGodotLocale(locale);
|
TranslationServer.SetLocale(locale);
|
||||||
|
|
||||||
// 设置系统持久化的是用户可见语言值;这里需要同步框架语言码,避免 Godot 与框架状态分裂。
|
|
||||||
_localizationManagerResolver()?.SetLanguage(frameworkLanguage);
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,30 +45,18 @@ public class GodotLocalizationSettings : IResetApplyAbleSettings
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
_model.GetData<LocalizationSettings>().Reset();
|
model.GetData<LocalizationSettings>().Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取本地化设置的数据对象。
|
/// 获取本地化设置的数据对象。
|
||||||
/// 该属性提供对本地化设置数据的只读访问。
|
/// 该属性提供对本地化设置数据的只读访问。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ISettingsData Data => _model.GetData<LocalizationSettings>();
|
public ISettingsData Data { get; } = model.GetData<LocalizationSettings>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取本地化设置数据的类型。
|
/// 获取本地化设置数据的类型。
|
||||||
/// 该属性返回本地化设置数据的具体类型信息。
|
/// 该属性返回本地化设置数据的具体类型信息。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Type DataType { get; } = typeof(LocalizationSettings);
|
public Type DataType { get; } = typeof(LocalizationSettings);
|
||||||
|
|
||||||
private static ILocalizationManager? TryResolveLocalizationManager()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return GameContext.GetFirstArchitectureContext().GetSystem<ILocalizationManager>();
|
|
||||||
}
|
|
||||||
catch (InvalidOperationException)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
Godot 设置模块是 GFramework.Godot 的核心组件之一,专门为 Godot 引擎提供游戏设置系统的实现。该模块将通用的设置框架与 Godot
|
Godot 设置模块是 GFramework.Godot 的核心组件之一,专门为 Godot 引擎提供游戏设置系统的实现。该模块将通用的设置框架与 Godot
|
||||||
引擎的特定功能相结合,提供了音频设置、图形设置和本地化设置的完整解决方案。
|
引擎的特定功能相结合,提供了音频设置和图形设置的完整解决方案。
|
||||||
|
|
||||||
## 核心类
|
## 核心类
|
||||||
|
|
||||||
@ -61,55 +61,23 @@ 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
|
```mermaid
|
||||||
graph TD
|
graph TD
|
||||||
A[AudioSettings] --> B[GodotAudioSettings]
|
A[AudioSettings] --> B[GodotAudioSettings]
|
||||||
C[GraphicsSettings] --> D[GodotGraphicsSettings]
|
C[GraphicsSettings] --> D[GodotGraphicsSettings]
|
||||||
E[LocalizationSettings] --> F[GodotLocalizationSettings]
|
E[IApplyAbleSettings] --> B
|
||||||
G[IApplyAbleSettings] --> B
|
E --> D
|
||||||
G --> D
|
|
||||||
G --> F
|
|
||||||
|
|
||||||
H[AudioBusMap] --> B
|
G[AudioBusMap] --> B
|
||||||
I[LocalizationMap] --> F
|
|
||||||
|
|
||||||
B --> J[AudioServer API]
|
B --> I[AudioServer API]
|
||||||
D --> K[DisplayServer API]
|
D --> J[DisplayServer API]
|
||||||
F --> L[TranslationServer API]
|
|
||||||
F --> M[ILocalizationManager]
|
|
||||||
|
|
||||||
N[SettingsSystem] --> O[Apply Method]
|
K[SettingsSystem] --> L[Apply Method]
|
||||||
O --> B
|
L --> B
|
||||||
O --> D
|
L --> D
|
||||||
O --> F
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用示例
|
## 使用示例
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user