From 141191aca8d0baf39e93f4e3d09a968719e6adf9 Mon Sep 17 00:00:00 2001 From: Zhou Jing Date: Wed, 11 Nov 2026 08:21:30 +0800 Subject: [PATCH 1/2] ci: validate Chinese and French locale environments --- .../actions/report-dotnet-runtime/action.yml | 17 ++++++++- .github/workflows/LocalesTest.yml | 37 +++++++++++-------- README.md | 2 + 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/.github/actions/report-dotnet-runtime/action.yml b/.github/actions/report-dotnet-runtime/action.yml index 89a1484b..25c38831 100644 --- a/.github/actions/report-dotnet-runtime/action.yml +++ b/.github/actions/report-dotnet-runtime/action.yml @@ -1,10 +1,17 @@ name: Report selected .NET runtimes description: Execute a small probe for every target framework declared by Common.props +inputs: + expected-culture: + description: Optional culture to require for both CurrentCulture and CurrentUICulture + required: false + default: '' runs: using: composite steps: - name: Execute runtime probes shell: pwsh + env: + EXPECTED_CULTURE: ${{ inputs.expected-culture }} run: | $ErrorActionPreference = 'Stop' $probeDirectory = Join-Path $env:RUNNER_TEMP ("dotnet-runtime-probe-" + [Guid]::NewGuid().ToString('N')) @@ -16,6 +23,14 @@ runs: Set-Content (Join-Path $probeDirectory 'RuntimeProbe.csproj') $project -Encoding utf8 $source = @' System.Console.WriteLine($"Actual runtime: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}; version={System.Environment.Version}; architecture={System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture}"); + string culture = System.Globalization.CultureInfo.CurrentCulture.Name; + string uiCulture = System.Globalization.CultureInfo.CurrentUICulture.Name; + System.Console.WriteLine($"Actual culture: {culture}; UI culture: {uiCulture}"); + if (args.Length > 0 && !string.IsNullOrEmpty(args[0]) + && (culture != args[0] || uiCulture != args[0])) + { + throw new System.InvalidOperationException($"Expected culture {args[0]}, got {culture}/{uiCulture}."); + } '@ Set-Content (Join-Path $probeDirectory 'Program.cs') $source -Encoding utf8 Push-Location $probeDirectory @@ -26,7 +41,7 @@ runs: $frameworks = ([xml](Get-Content $commonPath -Raw)).Project.PropertyGroup.TargetFrameworks.Split(';') foreach ($framework in $frameworks) { Write-Host "Target framework: $framework" - dotnet (Join-Path $probeDirectory "bin/Debug/$framework/RuntimeProbe.dll") + dotnet (Join-Path $probeDirectory "bin/Debug/$framework/RuntimeProbe.dll") $env:EXPECTED_CULTURE if ($LASTEXITCODE -ne 0) { throw "Runtime probe failed for ${framework}: $LASTEXITCODE" } } } finally { diff --git a/.github/workflows/LocalesTest.yml b/.github/workflows/LocalesTest.yml index f1f36686..3319db68 100644 --- a/.github/workflows/LocalesTest.yml +++ b/.github/workflows/LocalesTest.yml @@ -1,4 +1,4 @@ -name: Test Different Locales +name: Chinese and Cross-Locale Validation on: push: @@ -8,12 +8,18 @@ on: jobs: set-build-and-test-locale-languages: - name: Build and Test ${{ matrix.locale-lang }} Locale On Ubuntu + name: ${{ matrix.label }} on Ubuntu runs-on: ubuntu-latest strategy: fail-fast: false matrix: - locale-lang: ["pt_BR.UTF-8", "fr_FR.UTF-8"] + include: + - locale: zh_CN.UTF-8 + culture: zh-CN + label: 简体中文主验证 + - locale: fr_FR.UTF-8 + culture: fr-FR + label: 法语跨区域检查 steps: - uses: actions/checkout@v4 - name: Verify repository text encoding @@ -26,6 +32,15 @@ jobs: dotnet-version: | 6.0.x 7.0.x + - name: Configure test locale + shell: bash + env: + TEST_LOCALE: ${{ matrix.locale }} + run: | + sudo locale-gen "$TEST_LOCALE" + sudo localectl set-locale LANG="$TEST_LOCALE" + sudo update-locale LANG="$TEST_LOCALE" + printf 'LANG=%s\nLC_ALL=%s\n' "$TEST_LOCALE" "$TEST_LOCALE" >> "$GITHUB_ENV" - name: Get Build Information run: | dotnet --info @@ -34,6 +49,8 @@ jobs: dotnet --list-runtimes - name: Verify actual target runtimes uses: ./.github/actions/report-dotnet-runtime + with: + expected-culture: ${{ matrix.culture }} - name: Set up dependency caching for faster builds uses: actions/cache@v4 id: nuget-cache @@ -45,22 +62,10 @@ jobs: restore-keys: | ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} ${{ runner.os }}-nuget- - - name: SetLocale To ${{ matrix.locale-lang }}, and Build and Test + - name: Build and test all frameworks in the selected locale run: | - sudo locale-gen ${{ matrix.locale-lang }} - sudo localectl set-locale LANG=${{ matrix.locale-lang }} - LANG=${{ matrix.locale-lang }} - export LANG=${{ matrix.locale-lang }} - sudo update-locale LANG=${{ matrix.locale-lang }} echo Current Locale Settings: - echo Run locale to get the information about the current locale and language settings: locale - echo Run locale -a to list all enabled locales: - locale -a - echo Run localectl status to find system locale status. - localectl status - echo Visual Test - echo "Printing date" date echo Restore dependencies dotnet restore diff --git a/README.md b/README.md index 73ac6396..42d0491f 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ 测试断言按中文版译稿及本仓库的配套示例维护,覆盖现有的 net6.0、net7.0 和 net8.0 目标框架。 +CI 区域设置测试包括“简体中文主验证”(zh-CN)和“法语跨区域检查”(fr-FR),两组均运行全部现有测试及三个目标框架。简体中文组覆盖中文读者的典型环境,法语组检查不同数字、日期等格式下的行为;它不是法语版教材测试。CI 会实际核对每个目标框架的运行时、CurrentCulture 和 CurrentUICulture,区域设置不符时检查失败。 + 部分清单刻意演示编译错误或运行时异常;对应测试会核对编译诊断或预期异常,不应为了通过测试而消除这些教学示例中的错误。 许多代码清单都有相应的单元测试。 From f6f75e3a9cec8145674699689af0052c21f62e08 Mon Sep 17 00:00:00 2001 From: Zhou Jing Date: Wed, 11 Nov 2026 09:31:05 +0800 Subject: [PATCH 2/2] docs: clarify Chinese edition guidance and add CI badge --- README.md | 110 +++++++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 42d0491f..0ac94798 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,95 @@ -# EssentialCSharp - C#本质论 +# EssentialCSharp — C# 本质论 -本项目包含《C# 12.0本质论》的源代码,2024年清华大学出版社出版。 +[![构建与测试](https://github.com/transbot/EssentialCSharp/actions/workflows/build-and-test.yml/badge.svg?branch=v12.0Chinese&event=push)](https://github.com/transbot/EssentialCSharp/actions/workflows/build-and-test.yml?query=branch%3Av12.0Chinese) -## 中文版主页请访问[周靖的博客](https://bookzhou.com) -## 英文版在线手稿请访问[EssentialCSharp.com](https://essentialcsharp.com) -## 在英文版(print edition)中发现并纠正了不少疏漏,已提供英文版勘误。 -![C# 12 0本质论(第8版)-平展封](https://github.com/user-attachments/assets/bb84e67a-e659-4ab3-ba76-438ca110458a) +本仓库提供《C# 12.0 本质论》(第 8 版,清华大学出版社,2024 年)中文版的配套源代码。 + +代码及测试以中文版译稿和本仓库已修正的示例为准。中文版翻译过程中核对并修订了英文纸质版的部分疏漏,相关勘误和配套资源见[中文版主页](https://bookzhou.com)。 + +[EssentialCSharp.com](https://essentialcsharp.com) 提供英文在线资料,参阅时请留意版次和代码清单编号的差异。 + +![《C# 12.0 本质论》第 8 版封面](https://github.com/user-attachments/assets/bb84e67a-e659-4ab3-ba76-438ca110458a) ## 中文版购买链接 -- [京东官方](https://item.jd.com/14279697.html) -- [当当网](https://product.dangdang.com/29781408.html) +- [京东](https://item.jd.com/14279697.html) +- [当当](https://product.dangdang.com/29781408.html) -## 中英文版配套资源(含试读PDF)备用下载地址 +## 配套资源与试读 -- [百度网盘](https://pan.baidu.com/s/1yd43W) -- 也可以访问[中文版主页](https://bookzhou.com)从本地下载(已完成) +中英文版配套资源和试读 PDF 可从[中文版主页](https://bookzhou.com)获取,也可使用[百度网盘备用地址](https://pan.baidu.com/s/1yd43W)。 -## 要求 +## 开发环境 -- [.NET](https://dotnet.microsoft.com/zh-cn/download) - 该项目当前经过.NET 8.0、7.0和6.0的测试。 -- [Visual Studio](https://visualstudio.microsoft.com/downloads/) - 已集成.NET Core,这是我们主推的IDE,用它可以方便地入门。 -- [Git](https://git-scm.com/book/zh/v2) - 虽然不是必需的,但它是一个可以快速下载此代码仓库的好工具。如果不喜欢使用命令行,建议使用Visual Studio自带的Git功能,或者使用第三方工具,例如[GitKraken](https://www.gitkraken.com/invite/bX2Nqsqr) 。 +- [.NET SDK](https://dotnet.microsoft.com/zh-cn/download):使用 .NET 8 SDK 构建项目,SDK 选择规则见 `global.json`。项目保留 `net8.0`、`net7.0` 和 `net6.0` 三个目标框架;运行或测试某个目标框架时,需要安装对应的运行时。 +- [Visual Studio](https://visualstudio.microsoft.com/downloads/):可使用支持 .NET 8 的 Visual Studio 2022 进行开发,也可使用下文的 .NET CLI 命令。 +- [Git](https://git-scm.com/book/zh/v2):可用于克隆仓库和获取更新;也可使用 Visual Studio 的 Git 功能。 ## 运行代码 ### Visual Studio -1. **EssentialCSharp.sln** 是项目的主解决方案,请使用Visual Studio打开。 -2. 打开解决方案文件后,选择生成->生成解决方案,使用Visual Studio编译代码。 - - 注意:在执行源代码之前,需要将和当前章对应的项目设为启动项目。例如,要执行第1章的示例,那么应该右键单击Chapter01项目,然后选择“设为启动项目”。如果选择一个目标章作为启动项目,但输入其他章的代码清单编号,那么会导致异常,并显示消息:“不存在和代码清单...对应的章”,“不存在代码清单...”或者“异常”。 -3. 要运行代码,请按F5或选择调试->开始调试。 -4. 运行后,程序将提示输入要执行的代码清单编号(例如,18.33)。 - - 注意:如前所述,只能输入本书合法的代码清单编号。 - -![image](https://github.com/transbot/EssentialCSharp/assets/12107254/9b8891de-3df3-48d9-838e-1a36750e7102) +1. 打开主解决方案 `EssentialCSharp.sln`。 +2. 选择“生成 → 生成解决方案”。 +3. 将要阅读的章节设为启动项目。例如,运行第 1 章时,右键单击 `Chapter01`,选择“设为启动项目”。 +4. 选择 .NET 8 目标框架,按 F5 或选择“调试 → 开始调试”。 +5. 按程序提示输入当前章节的代码清单编号,例如 `1.1`。运行其他章节时,请切换到对应的启动项目。 +![运行代码清单示例](https://github.com/transbot/EssentialCSharp/assets/12107254/9b8891de-3df3-48d9-838e-1a36750e7102) +### .NET CLI -### Dotnet CLI +在仓库根目录(包含 `EssentialCSharp.sln` 的目录)还原并构建项目: -1. 从本地终端导航到EssentialCSharp的根目录(根目录是包含`EssentialCSharp.sln`文件的目录)。 -2. 从该根目录运行以下命令以还原和构建所有项目: +```bash +dotnet restore +dotnet build --no-restore +``` - ```C# - dotnet restore - dotnet build - ``` +例如,从仓库根目录运行第 1 章: -3. 要运行代码,请首先导航到希望执行的那个章节所在的项目目录。例如,要执行第1章的示例,需要先导航到Chapter01目录(例如:`cd src/Chapter01`)。 -4. 然后使用`dotnet run`命令启动程序,并按照提示输入要执行的代码清单编号。 - - 注意:只能输入在当前运行的项目(本例是Chapter01)中存在的代码清单编号。 -5. 还可以使用`dotnet run -p `命令,其中project file是要执行的项目文件的路径(例如,`dotnet run -p .\src\Chapter01\Chapter01.csproj`)。执行时,程序将提示要执行哪个代码清单。 +```bash +dotnet run --project src/Chapter01/Chapter01.csproj --framework net8.0 +``` -.NET CLI工具的文档可以在这里找到:https://learn.microsoft.com/zh-cn/dotnet/core/tools/ +项目包含多个目标框架,运行时需使用 `--framework` 指定其中一个。程序启动后,按提示输入当前章节的代码清单编号。也可以先进入章节目录,再执行 `dotnet run --framework net8.0`。 +更多命令说明见 [.NET CLI 文档](https://learn.microsoft.com/zh-cn/dotnet/core/tools/)。 ## 测试 -测试断言按中文版译稿及本仓库的配套示例维护,覆盖现有的 net6.0、net7.0 和 net8.0 目标框架。 +测试断言按中文版译稿及本仓库的配套示例维护,覆盖 `net6.0`、`net7.0` 和 `net8.0` 三个目标框架。 -CI 区域设置测试包括“简体中文主验证”(zh-CN)和“法语跨区域检查”(fr-FR),两组均运行全部现有测试及三个目标框架。简体中文组覆盖中文读者的典型环境,法语组检查不同数字、日期等格式下的行为;它不是法语版教材测试。CI 会实际核对每个目标框架的运行时、CurrentCulture 和 CurrentUICulture,区域设置不符时检查失败。 +CI 在 Windows、Linux 和 macOS 上执行构建与测试,并在 Linux 上分别执行“简体中文主验证”(zh-CN)和“法语跨区域检查”(fr-FR)。两组区域测试均运行全部现有测试及三个目标框架:简体中文组覆盖中文读者的典型环境,法语组验证数字、日期等区域格式差异。CI 会核对实际区域设置和目标运行时。 -部分清单刻意演示编译错误或运行时异常;对应测试会核对编译诊断或预期异常,不应为了通过测试而消除这些教学示例中的错误。 +部分清单刻意演示编译错误或运行时异常;对应测试会核对编译诊断或预期异常。请保留这些教学示例的设计意图。 -许多代码清单都有相应的单元测试。 +在 Visual Studio 中,可以通过“测试资源管理器”运行全部测试,也可以右键单击具体测试方法,选择“运行测试”或“调试测试”。详见 [Visual Studio 测试文档](https://learn.microsoft.com/visualstudio/test/run-unit-tests-with-test-explorer)。 -在Visual Studio中,要执行测试,请打开测试项目 +从仓库根目录运行全部测试: -并导航到与您要执行的代码清单对应的测试。然后,在测试方法上右键单击,选择运行测试(Ctrl+R,T)或调试测试(Ctrl+R,Ctrl+T)。或者,打开Test Explorer窗口,并从那里运行一些或所有测试([更多信息](https://learn.microsoft.com/visualstudio/test/run-unit-tests-with-test-explorer))。 +```bash +dotnet test +``` -在dotnet test中,要在所有项目中运行所有测试,请在根EssentialCSharp目录中的命令提示符上运行`dotnet test`。要运行单个项目的测试,请在要执行的测试的测试项目的测试目录中使用dotnet test命令。 +运行单章测试时,可指定测试项目,例如: -## 阅读源代码 - -在源代码中,有一些可能与在书中查看源代码时看起来不同的地方。这是因为即使书中的示例直接来自这个存储库,但并非所有代码都在书中原样显示。 +```bash +dotnet test src/Chapter01.Tests/Chapter01.Tests.csproj +``` -一些注意事项: - -当代码中有匹配的`#region INCLUDE`和`#endregion INCLUDE`标签时,在这两个标签之间的是在本书中能够看到的源代码。 +## 阅读源代码 -当代码中有匹配的`#region EXCLUDE`和`#endregion EXCLUDE`标签时,即使这些标签在两个`INCLUDE`标签之间,此部分也会在书中省略,并用`// ...`替换,目的是让您将重点放在当前讲解的主题上。 +配套工程包含运行入口、测试和辅助代码,因此部分文件与书中的展示形式有所不同。阅读代码时,可通过以下标记识别书中展示的部分: -此外,任何以`// EXCLUDE`结尾的行都在书中省略了。 +- `#region INCLUDE` 与 `#endregion INCLUDE` 之间是书中展示的代码。 +- `#region EXCLUDE` 与 `#endregion EXCLUDE` 之间的内容在书中省略,通常以 `// ...` 表示。 +- 行尾带有 `// EXCLUDE` 的代码在书中省略。 -## 问题/错误 +## 问题反馈与修正 -如果有任何问题或发现任何错误,请随时打开[本书中文版Git](https://github.com/transbot/EssentialCSharp/discussions)、[Pull Request](https://github.com/transbot/EssentialCSharp/pulls)或[Issue](https://github.com/transbot/EssentialCSharp/discussions)。 +使用 [Issues](https://github.com/transbot/EssentialCSharp/issues) 报告问题或错误,通过 [Discussions](https://github.com/transbot/EssentialCSharp/discussions) 讨论用法,通过 [Pull requests](https://github.com/transbot/EssentialCSharp/pulls) 提交修正。反馈时请注明中文版版次、章节或代码清单编号,以及复现步骤。 -另外,请访问[本书中文版主页](https://bookzhou.com),获取最新资讯、下载和勘误。 +资讯、下载与勘误请访问[中文版主页](https://bookzhou.com)。 -## 祝您编码愉快 +祝编程愉快!