feat(core): 添加本地化系统支持多语言功能#115
Merged
Merged
Conversation
- 实现 ILocalizationManager 接口及 LocalizationManager 管理器 - 添加 ILocalizationTable 和 ILocalizationString 接口及其实现 - 创建 LocalizationConfig 配置类用于管理本地化行为 - 实现 ConditionalFormatter 和 PluralFormatter 内置格式化器 - 添加本地化文档包括 API 参考和使用指南 - 集成本地化系统到核心框架架构中
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| C# | Mar 19, 2026 5:15a.m. | Review ↗ | |
| Secrets | Mar 19, 2026 5:15a.m. | Review ↗ |
审阅者指南在核心框架中实现了完整的本地化子系统,包括抽象层、具体的管理器/表/字符串实现及格式化器支持,同时增加了集成测试,并更新了完整的中文文档和 API 参考。 格式化带变量的本地化字符串的时序图sequenceDiagram
actor GameCode
participant LocalizationManager
participant LocalizationString
participant LocalizationTable
participant Formatter as ILocalizationFormatter
GameCode->>LocalizationManager: GetString(table, key)
LocalizationManager-->>GameCode: LocalizationString instance
GameCode->>LocalizationString: WithVariable(name, value)
LocalizationString-->>GameCode: LocalizationString
GameCode->>LocalizationString: Format()
LocalizationString->>LocalizationString: GetRaw()
LocalizationString->>LocalizationManager: TryGetText(table, key, out text)
alt key exists
LocalizationManager->>LocalizationTable: GetRawText(key)
LocalizationTable-->>LocalizationManager: rawText
LocalizationManager-->>LocalizationString: true, rawText
LocalizationString->>LocalizationString: FormatString(rawText, variables, manager)
loop each placeholder
LocalizationString->>LocalizationManager: GetFormatter(formatterName)
LocalizationManager-->>LocalizationString: Formatter or null
alt formatter found and TryFormat succeeds
LocalizationString->>Formatter: TryFormat(format, value, CurrentCulture, out result)
Formatter-->>LocalizationString: true, result
else no formatter or failure
LocalizationString-->>LocalizationString: fallback ToString with CurrentCulture
end
end
LocalizationString-->>GameCode: formattedText
else key missing
LocalizationManager-->>LocalizationString: false, empty
LocalizationString-->>GameCode: "[table.key]"
end
切换语言并通知监听者的时序图sequenceDiagram
actor GameCode
participant LocalizationManager
participant FileSystem as FileSystem
participant Callback as LanguageChangeCallback
GameCode->>LocalizationManager: SetLanguage(languageCode)
alt languageCode is null or empty
LocalizationManager-->>GameCode: ArgumentNullException
else same as current language
LocalizationManager-->>GameCode: return
else new language
LocalizationManager->>LocalizationManager: LoadLanguage(languageCode)
alt language already loaded
LocalizationManager-->>LocalizationManager: return
else not loaded
LocalizationManager->>LocalizationManager: LoadLanguage(FallbackLanguage)
LocalizationManager->>FileSystem: Read localizationPath/languageCode/*.json
FileSystem-->>LocalizationManager: JSON tables
LocalizationManager->>LocalizationManager: Create LocalizationTable per file
LocalizationManager-->>LocalizationManager: cache tables
end
LocalizationManager->>LocalizationManager: GetCultureInfo(languageCode)
LocalizationManager-->>LocalizationManager: update CurrentLanguage, CurrentCulture
LocalizationManager->>LocalizationManager: TriggerLanguageChange()
loop each subscribed callback
LocalizationManager->>Callback: Invoke(CurrentLanguage)
Callback-->>LocalizationManager: return
end
LocalizationManager-->>GameCode: return
end
本地化核心抽象的类图classDiagram
direction LR
class ISystem {
}
class ILocalizationManager {
<<interface>>
+string CurrentLanguage
+CultureInfo CurrentCulture
+IReadOnlyList~string~ AvailableLanguages
+void SetLanguage(string languageCode)
+ILocalizationTable GetTable(string tableName)
+string GetText(string table, string key)
+ILocalizationString GetString(string table, string key)
+bool TryGetText(string table, string key, out string text)
+void RegisterFormatter(string name, ILocalizationFormatter formatter)
+ILocalizationFormatter GetFormatter(string name)
+void SubscribeToLanguageChange(Action~string~ callback)
+void UnsubscribeFromLanguageChange(Action~string~ callback)
}
class ILocalizationTable {
<<interface>>
+string Name
+string Language
+ILocalizationTable Fallback
+string GetRawText(string key)
+bool ContainsKey(string key)
+IEnumerable~string~ GetKeys()
+void Merge(IReadOnlyDictionary~string, string~ overrides)
}
class ILocalizationString {
<<interface>>
+string Table
+string Key
+ILocalizationString WithVariable(string name, object value)
+ILocalizationString WithVariables(params (string name, object value)[] variables)
+string Format()
+string GetRaw()
+bool Exists()
}
class ILocalizationFormatter {
<<interface>>
+string Name
+bool TryFormat(string format, object value, IFormatProvider provider, out string result)
}
class LocalizationConfig {
+string DefaultLanguage
+string FallbackLanguage
+string LocalizationPath
+string OverridePath
+bool EnableHotReload
+bool ValidateOnLoad
}
class LocalizationException {
+LocalizationException()
+LocalizationException(string message)
+LocalizationException(string message, Exception innerException)
}
class LocalizationKeyNotFoundException {
+string TableName
+string Key
+LocalizationKeyNotFoundException(string tableName, string key)
}
class LocalizationTableNotFoundException {
+string TableName
+LocalizationTableNotFoundException(string tableName)
}
ILocalizationManager ..|> ISystem
LocalizationKeyNotFoundException --|> LocalizationException
LocalizationTableNotFoundException --|> LocalizationException
ILocalizationManager --> ILocalizationTable
ILocalizationManager --> ILocalizationString
ILocalizationManager --> ILocalizationFormatter
ILocalizationTable --> ILocalizationTable : Fallback
本地化具体实现的类图classDiagram
direction LR
class AbstractSystem {
<<abstract>>
#void OnInit()
#void OnDestroy()
}
class LocalizationManager {
-LocalizationConfig _config
-Dictionary~string, ILocalizationFormatter~ _formatters
-List~Action~string~~ _languageChangeCallbacks
-Dictionary~string, Dictionary~string, ILocalizationTable~~ _tables
-List~string~ _availableLanguages
-CultureInfo _currentCulture
-string _currentLanguage
+LocalizationManager(LocalizationConfig config)
+string CurrentLanguage
+CultureInfo CurrentCulture
+IReadOnlyList~string~ AvailableLanguages
+void SetLanguage(string languageCode)
+ILocalizationTable GetTable(string tableName)
+string GetText(string table, string key)
+ILocalizationString GetString(string table, string key)
+bool TryGetText(string table, string key, out string text)
+void RegisterFormatter(string name, ILocalizationFormatter formatter)
+ILocalizationFormatter GetFormatter(string name)
+void SubscribeToLanguageChange(Action~string~ callback)
+void UnsubscribeFromLanguageChange(Action~string~ callback)
#void OnInit()
#void OnDestroy()
-void ScanAvailableLanguages()
-void LoadLanguage(string languageCode)
-Dictionary~string, string~ LoadJsonFile(string filePath)
-CultureInfo GetCultureInfo(string languageCode)
-void TriggerLanguageChange()
}
class LocalizationTable {
-Dictionary~string, string~ _data
-Dictionary~string, string~ _overrides
+LocalizationTable(string name, string language, IReadOnlyDictionary~string, string~ data, ILocalizationTable fallback)
+string Name
+string Language
+ILocalizationTable Fallback
+string GetRawText(string key)
+bool ContainsKey(string key)
+IEnumerable~string~ GetKeys()
+void Merge(IReadOnlyDictionary~string, string~ overrides)
}
class LocalizationString {
-ILocalizationManager _manager
-Dictionary~string, object~ _variables
+LocalizationString(ILocalizationManager manager, string table, string key)
+string Table
+string Key
+ILocalizationString WithVariable(string name, object value)
+ILocalizationString WithVariables(params (string name, object value)[] variables)
+string Format()
+string GetRaw()
+bool Exists()
-string FormatString(string template, Dictionary~string, object~ variables, ILocalizationManager manager)
-ILocalizationFormatter GetFormatter(ILocalizationManager manager, string name)
}
class ConditionalFormatter {
+string Name
+bool TryFormat(string format, object value, IFormatProvider provider, out string result)
}
class PluralFormatter {
+string Name
+bool TryFormat(string format, object value, IFormatProvider provider, out string result)
}
AbstractSystem <|-- LocalizationManager
ILocalizationManager <|.. LocalizationManager
ILocalizationTable <|.. LocalizationTable
ILocalizationString <|.. LocalizationString
ILocalizationFormatter <|.. ConditionalFormatter
ILocalizationFormatter <|.. PluralFormatter
LocalizationManager --> LocalizationConfig
LocalizationManager --> LocalizationTable
LocalizationManager --> LocalizationString
LocalizationManager --> ILocalizationFormatter
LocalizationString --> ILocalizationManager
LocalizationTable --> LocalizationKeyNotFoundException
LocalizationManager --> LocalizationTableNotFoundException
文件级变更
技巧与命令与 Sourcery 交互
自定义你的体验访问你的控制面板 可以:
获取帮助Original review guide in EnglishReviewer's GuideImplements a full localization subsystem in the core framework, including abstractions, concrete manager/table/string implementations with formatter support, integration tests, and comprehensive Chinese documentation and API reference updates. Sequence diagram for formatting a localized string with variablessequenceDiagram
actor GameCode
participant LocalizationManager
participant LocalizationString
participant LocalizationTable
participant Formatter as ILocalizationFormatter
GameCode->>LocalizationManager: GetString(table, key)
LocalizationManager-->>GameCode: LocalizationString instance
GameCode->>LocalizationString: WithVariable(name, value)
LocalizationString-->>GameCode: LocalizationString
GameCode->>LocalizationString: Format()
LocalizationString->>LocalizationString: GetRaw()
LocalizationString->>LocalizationManager: TryGetText(table, key, out text)
alt key exists
LocalizationManager->>LocalizationTable: GetRawText(key)
LocalizationTable-->>LocalizationManager: rawText
LocalizationManager-->>LocalizationString: true, rawText
LocalizationString->>LocalizationString: FormatString(rawText, variables, manager)
loop each placeholder
LocalizationString->>LocalizationManager: GetFormatter(formatterName)
LocalizationManager-->>LocalizationString: Formatter or null
alt formatter found and TryFormat succeeds
LocalizationString->>Formatter: TryFormat(format, value, CurrentCulture, out result)
Formatter-->>LocalizationString: true, result
else no formatter or failure
LocalizationString-->>LocalizationString: fallback ToString with CurrentCulture
end
end
LocalizationString-->>GameCode: formattedText
else key missing
LocalizationManager-->>LocalizationString: false, empty
LocalizationString-->>GameCode: "[table.key]"
end
Sequence diagram for switching language and notifying listenerssequenceDiagram
actor GameCode
participant LocalizationManager
participant FileSystem as FileSystem
participant Callback as LanguageChangeCallback
GameCode->>LocalizationManager: SetLanguage(languageCode)
alt languageCode is null or empty
LocalizationManager-->>GameCode: ArgumentNullException
else same as current language
LocalizationManager-->>GameCode: return
else new language
LocalizationManager->>LocalizationManager: LoadLanguage(languageCode)
alt language already loaded
LocalizationManager-->>LocalizationManager: return
else not loaded
LocalizationManager->>LocalizationManager: LoadLanguage(FallbackLanguage)
LocalizationManager->>FileSystem: Read localizationPath/languageCode/*.json
FileSystem-->>LocalizationManager: JSON tables
LocalizationManager->>LocalizationManager: Create LocalizationTable per file
LocalizationManager-->>LocalizationManager: cache tables
end
LocalizationManager->>LocalizationManager: GetCultureInfo(languageCode)
LocalizationManager-->>LocalizationManager: update CurrentLanguage, CurrentCulture
LocalizationManager->>LocalizationManager: TriggerLanguageChange()
loop each subscribed callback
LocalizationManager->>Callback: Invoke(CurrentLanguage)
Callback-->>LocalizationManager: return
end
LocalizationManager-->>GameCode: return
end
Class diagram for localization core abstractionsclassDiagram
direction LR
class ISystem {
}
class ILocalizationManager {
<<interface>>
+string CurrentLanguage
+CultureInfo CurrentCulture
+IReadOnlyList~string~ AvailableLanguages
+void SetLanguage(string languageCode)
+ILocalizationTable GetTable(string tableName)
+string GetText(string table, string key)
+ILocalizationString GetString(string table, string key)
+bool TryGetText(string table, string key, out string text)
+void RegisterFormatter(string name, ILocalizationFormatter formatter)
+ILocalizationFormatter GetFormatter(string name)
+void SubscribeToLanguageChange(Action~string~ callback)
+void UnsubscribeFromLanguageChange(Action~string~ callback)
}
class ILocalizationTable {
<<interface>>
+string Name
+string Language
+ILocalizationTable Fallback
+string GetRawText(string key)
+bool ContainsKey(string key)
+IEnumerable~string~ GetKeys()
+void Merge(IReadOnlyDictionary~string, string~ overrides)
}
class ILocalizationString {
<<interface>>
+string Table
+string Key
+ILocalizationString WithVariable(string name, object value)
+ILocalizationString WithVariables(params (string name, object value)[] variables)
+string Format()
+string GetRaw()
+bool Exists()
}
class ILocalizationFormatter {
<<interface>>
+string Name
+bool TryFormat(string format, object value, IFormatProvider provider, out string result)
}
class LocalizationConfig {
+string DefaultLanguage
+string FallbackLanguage
+string LocalizationPath
+string OverridePath
+bool EnableHotReload
+bool ValidateOnLoad
}
class LocalizationException {
+LocalizationException()
+LocalizationException(string message)
+LocalizationException(string message, Exception innerException)
}
class LocalizationKeyNotFoundException {
+string TableName
+string Key
+LocalizationKeyNotFoundException(string tableName, string key)
}
class LocalizationTableNotFoundException {
+string TableName
+LocalizationTableNotFoundException(string tableName)
}
ILocalizationManager ..|> ISystem
LocalizationKeyNotFoundException --|> LocalizationException
LocalizationTableNotFoundException --|> LocalizationException
ILocalizationManager --> ILocalizationTable
ILocalizationManager --> ILocalizationString
ILocalizationManager --> ILocalizationFormatter
ILocalizationTable --> ILocalizationTable : Fallback
Class diagram for localization concrete implementationsclassDiagram
direction LR
class AbstractSystem {
<<abstract>>
#void OnInit()
#void OnDestroy()
}
class LocalizationManager {
-LocalizationConfig _config
-Dictionary~string, ILocalizationFormatter~ _formatters
-List~Action~string~~ _languageChangeCallbacks
-Dictionary~string, Dictionary~string, ILocalizationTable~~ _tables
-List~string~ _availableLanguages
-CultureInfo _currentCulture
-string _currentLanguage
+LocalizationManager(LocalizationConfig config)
+string CurrentLanguage
+CultureInfo CurrentCulture
+IReadOnlyList~string~ AvailableLanguages
+void SetLanguage(string languageCode)
+ILocalizationTable GetTable(string tableName)
+string GetText(string table, string key)
+ILocalizationString GetString(string table, string key)
+bool TryGetText(string table, string key, out string text)
+void RegisterFormatter(string name, ILocalizationFormatter formatter)
+ILocalizationFormatter GetFormatter(string name)
+void SubscribeToLanguageChange(Action~string~ callback)
+void UnsubscribeFromLanguageChange(Action~string~ callback)
#void OnInit()
#void OnDestroy()
-void ScanAvailableLanguages()
-void LoadLanguage(string languageCode)
-Dictionary~string, string~ LoadJsonFile(string filePath)
-CultureInfo GetCultureInfo(string languageCode)
-void TriggerLanguageChange()
}
class LocalizationTable {
-Dictionary~string, string~ _data
-Dictionary~string, string~ _overrides
+LocalizationTable(string name, string language, IReadOnlyDictionary~string, string~ data, ILocalizationTable fallback)
+string Name
+string Language
+ILocalizationTable Fallback
+string GetRawText(string key)
+bool ContainsKey(string key)
+IEnumerable~string~ GetKeys()
+void Merge(IReadOnlyDictionary~string, string~ overrides)
}
class LocalizationString {
-ILocalizationManager _manager
-Dictionary~string, object~ _variables
+LocalizationString(ILocalizationManager manager, string table, string key)
+string Table
+string Key
+ILocalizationString WithVariable(string name, object value)
+ILocalizationString WithVariables(params (string name, object value)[] variables)
+string Format()
+string GetRaw()
+bool Exists()
-string FormatString(string template, Dictionary~string, object~ variables, ILocalizationManager manager)
-ILocalizationFormatter GetFormatter(ILocalizationManager manager, string name)
}
class ConditionalFormatter {
+string Name
+bool TryFormat(string format, object value, IFormatProvider provider, out string result)
}
class PluralFormatter {
+string Name
+bool TryFormat(string format, object value, IFormatProvider provider, out string result)
}
AbstractSystem <|-- LocalizationManager
ILocalizationManager <|.. LocalizationManager
ILocalizationTable <|.. LocalizationTable
ILocalizationString <|.. LocalizationString
ILocalizationFormatter <|.. ConditionalFormatter
ILocalizationFormatter <|.. PluralFormatter
LocalizationManager --> LocalizationConfig
LocalizationManager --> LocalizationTable
LocalizationManager --> LocalizationString
LocalizationManager --> ILocalizationFormatter
LocalizationString --> ILocalizationManager
LocalizationTable --> LocalizationKeyNotFoundException
LocalizationManager --> LocalizationTableNotFoundException
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - 我发现了两个问题,并提供了一些整体性的反馈:
- LocalizationManager 目前会忽略 LocalizationConfig 中的 OverridePath、EnableHotReload 和 ValidateOnLoad;建议要么在 LoadLanguage/ScanAvailableLanguages 中接入这些配置(例如合并 override JSON、可选的加载时校验以及可选的文件监视),要么在文档中说明这些配置暂不支持,以避免造成困惑。
- TryGetText 会捕获所有异常并返回空字符串,这可能会掩盖编程/配置错误;建议将捕获范围收窄到 LocalizationException(或更具体的异常类型),从而让意料之外的运行时错误仍然能够暴露出来。
- LocalizationString.FormatString 每次调用都会分配一个新的 Regex;如果格式化字符串的性能比较重要,建议将该正则移动到一个 static readonly 字段中,或者使用 RegexOptions.Compiled 以降低开销。
给 AI 代理的提示词
请根据这次代码评审中的评论进行修改:
## 总体评论
- LocalizationManager 目前会忽略 LocalizationConfig 中的 OverridePath、EnableHotReload 和 ValidateOnLoad;建议要么在 LoadLanguage/ScanAvailableLanguages 中接入这些配置(例如合并 override JSON、可选的加载时校验以及可选的文件监视),要么在文档中说明这些配置暂不支持,以避免造成困惑。
- TryGetText 会捕获所有异常并返回空字符串,这可能会掩盖编程/配置错误;建议将捕获范围收窄到 LocalizationException(或更具体的异常类型),从而让意料之外的运行时错误仍然能够暴露出来。
- LocalizationString.FormatString 每次调用都会分配一个新的 Regex;如果格式化字符串的性能比较重要,建议将该正则移动到一个 static readonly 字段中,或者使用 RegexOptions.Compiled 以降低开销。
## 逐条评论
### 评论 1
<location path="GFramework.Core/Localization/LocalizationString.cs" line_range="100-101" />
<code_context>
+ }
+
+ // 匹配 {variableName} 或 {variableName:formatter:args}
+ var pattern = @"\{([a-zA-Z_][a-zA-Z0-9_]*)(?::([a-zA-Z_][a-zA-Z0-9_]*)(?::([^}]+))?)?\}";
+ var regex = new Regex(pattern);
+
+ return regex.Replace(template, match =>
</code_context>
<issue_to_address>
**suggestion (performance):** 避免在每次调用 FormatString 时都分配一个新的 Regex 实例。
由于该方法可能被频繁调用,反复构造 Regex 会带来不必要的开销。建议将其移动到一个 static readonly 字段中,只创建一次并在整个生命周期内复用;如果可以接受一定的启动成本,也可以考虑启用 RegexOptions.Compiled。
建议实现如下:
```csharp
public class LocalizationString
{
// Matches {variableName} or {variableName:formatter:args}
private static readonly string FormatVariablePattern =
@"\{([a-zA-Z_][a-zA-Z0-9_]*)(?::([a-zA-Z_][a-zA-Z0-9_]*)(?::([^}]+))?)?\}";
private static readonly Regex FormatVariableRegex =
new Regex(FormatVariablePattern, RegexOptions.Compiled | RegexOptions.CultureInvariant);
```
```csharp
// 使用预编译的静态正则表达式匹配 {variableName} 或 {variableName:formatter:args}
return FormatVariableRegex.Replace(template, match =>
```
1. 如果包含此成员的类型并不叫 `LocalizationString`,请在第一个查找/替换代码块中替换为真实的类/结构体名称。
2. 确保文件顶部已包含 `using System.Text.RegularExpressions;`,如果没有请补充。
3. 如果你认为预编译正则的启动成本过高,可以去掉 `RegexOptions.Compiled`,只保留静态 `Regex` 来避免重复分配。
</issue_to_address>
### 评论 2
<location path="docs/zh-CN/core/localization.md" line_range="257-258" />
<code_context>
+ // 更新 UI、重新加载资源等
+});
+
+// 取消订阅
+locManager.UnsubscribeFromLanguageChange(callback);
+```
+
</code_context>
<issue_to_address>
**issue:** `callback` 在示例中未定义,取消订阅用法略显不清晰。
示例中只演示了用 lambda 直接订阅:`SubscribeToLanguageChange(language => { ... })`,但取消订阅时却使用了未定义的 `callback`。建议:要么补充完整示例(先将回调赋给变量,再用该变量取消订阅),要么让取消订阅示例与订阅方式一致,并说明如何保存回调以便后续取消订阅。
</issue_to_address>帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- LocalizationManager currently ignores OverridePath, EnableHotReload, and ValidateOnLoad in LocalizationConfig; consider either wiring these options into LoadLanguage/ScanAvailableLanguages (e.g., merging override JSON, optional validation, and optional file watching) or documenting that they are not yet supported to avoid confusion.
- TryGetText catches all exceptions and returns an empty string, which can hide programming/configuration errors; consider narrowing this to LocalizationException (or more specific types) so unexpected runtime errors still surface.
- LocalizationString.FormatString allocates a new Regex on every call; you can reduce overhead by moving the regex to a static readonly field or using RegexOptions.Compiled if performance of formatted strings is important.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- LocalizationManager currently ignores OverridePath, EnableHotReload, and ValidateOnLoad in LocalizationConfig; consider either wiring these options into LoadLanguage/ScanAvailableLanguages (e.g., merging override JSON, optional validation, and optional file watching) or documenting that they are not yet supported to avoid confusion.
- TryGetText catches all exceptions and returns an empty string, which can hide programming/configuration errors; consider narrowing this to LocalizationException (or more specific types) so unexpected runtime errors still surface.
- LocalizationString.FormatString allocates a new Regex on every call; you can reduce overhead by moving the regex to a static readonly field or using RegexOptions.Compiled if performance of formatted strings is important.
## Individual Comments
### Comment 1
<location path="GFramework.Core/Localization/LocalizationString.cs" line_range="100-101" />
<code_context>
+ }
+
+ // 匹配 {variableName} 或 {variableName:formatter:args}
+ var pattern = @"\{([a-zA-Z_][a-zA-Z0-9_]*)(?::([a-zA-Z_][a-zA-Z0-9_]*)(?::([^}]+))?)?\}";
+ var regex = new Regex(pattern);
+
+ return regex.Replace(template, match =>
</code_context>
<issue_to_address>
**suggestion (performance):** Avoid allocating a new Regex instance on every FormatString call.
Since this method may be called often, repeatedly constructing the Regex is unnecessary overhead. Move it to a static readonly field so it’s created once and reused, and consider enabling RegexOptions.Compiled if startup cost is acceptable.
Suggested implementation:
```csharp
public class LocalizationString
{
// Matches {variableName} or {variableName:formatter:args}
private static readonly string FormatVariablePattern =
@"\{([a-zA-Z_][a-zA-Z0-9_]*)(?::([a-zA-Z_][a-zA-Z0-9_]*)(?::([^}]+))?)?\}";
private static readonly Regex FormatVariableRegex =
new Regex(FormatVariablePattern, RegexOptions.Compiled | RegexOptions.CultureInvariant);
```
```csharp
// 使用预编译的静态正则表达式匹配 {variableName} 或 {variableName:formatter:args}
return FormatVariableRegex.Replace(template, match =>
```
1. If the containing type is not actually named `LocalizationString`, adjust the first SEARCH/REPLACE block to match the real class/struct name.
2. Ensure `using System.Text.RegularExpressions;` is present at the top of the file if it isn’t already.
3. If you decide compiled regex startup cost is too high, you can remove `RegexOptions.Compiled` and keep the static `Regex` for allocation avoidance only.
</issue_to_address>
### Comment 2
<location path="docs/zh-CN/core/localization.md" line_range="257-258" />
<code_context>
+ // 更新 UI、重新加载资源等
+});
+
+// 取消订阅
+locManager.UnsubscribeFromLanguageChange(callback);
+```
+
</code_context>
<issue_to_address>
**issue:** `callback` 在示例中未定义,取消订阅用法略显不清晰。
示例中只演示了用 lambda 直接订阅:`SubscribeToLanguageChange(language => { ... })`,但取消订阅时却使用了未定义的 `callback`。建议:要么补充完整示例(先将回调赋给变量,再用该变量取消订阅),要么让取消订阅示例与订阅方式一致,并说明如何保存回调以便后续取消订阅。
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- 添加了配置项暂不支持的说明信息 - 扩展了语言变化监听的使用方式,增加命名方法订阅示例 - 完善了覆盖机制的文档说明 - 优化了异常处理逻辑,精确捕获本地化相关异常 - 实现了正则表达式的预编译以提升性能 - 添加了必要的命名空间引用
- 添加对 GFramework.Core.Abstractions.Localization 的引用 - 确保测试文件能够访问本地化相关的抽象接口 - 为后续的本地化功能扩展做好准备 - 保持代码结构的一致性与可维护性
- 将 FormatVariablePattern 从 readonly 字段改为 const 常量 - 提取复杂的正则替换逻辑到独立的 FormatMatch 方法中 - 新增 GetOptionalGroupValue 辅助方法处理可选组值提取 - 分离格式化值和尝试格式化的逻辑到独立方法 - 简化条件判断并提高代码可读性 - 优化错误处理流程,保持原有功能不变
- 为构造函数添加更详细的参数描述和异常说明 - 为WithVariable方法添加完整的XML文档注释 - 为WithVariables方法添加完整的XML文档注释 - 为Format方法添加详细的返回值和备注信息 - 为GetRaw方法添加完整的XML文档注释 - 为Exists方法添加完整的XML文档注释 - 为私有FormatString方法添加参数和返回值说明 - 为私有FormatMatch方法添加详细的处理逻辑描述 - 为私有TryFormatValue方法添加格式化器相关参数说明 - 为私有FormatValue方法添加默认格式化逻辑说明 - 为私有GetOptionalGroupValue方法添加功能说明 - 为私有GetFormatter方法添加获取格式化器的详细描述
- 添加同步锁对象以保护处理器集合的并发访问 - 在注册和注销操作中加入线程安全锁机制 - 实现快照创建方法以避免迭代期间的并发修改 - 重构触发器逻辑以使用线程安全的快照创建 - 在计数器方法中添加同步保护 - 确保所有集合操作都在安全锁内执行
- 添加 System.IO 命名空间引用以支持文件操作 - 实现 CreateTestLocalizationFiles 方法创建测试用的多语言文件 - 使用 GUID 生成唯一的临时目录路径避免冲突 - 添加 TearDown 方法清理测试过程中创建的临时文件 - 在 Setup 方法中调用文件创建方法初始化测试环境 - 将目标框架配置改为可配置的条件变量方式
- 将 Fallback.ContainsKey 检查替换为更安全的模式匹配语法 - 避免了潜在的空引用异常风险 - 提高了代码的可读性和健壮性 - 保持了原有的功能逻辑不变
This was referenced Mar 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary by Sourcery
引入一个核心本地化系统,提供多语言支持,包括配置管理、文化区域感知的字符串获取,以及与现有系统架构的集成。
New Features:
ILocalizationManager、ILocalizationTable、ILocalizationString和ILocalizationFormatter抽象及其核心实现,用于管理本地化文本和本地化表。LocalizationConfig,用于配置默认/回退语言、资源路径,以及诸如热重载与校验等运行时行为。ConditionalFormatter和PluralFormatter,以支持条件和复数感知的本地化字符串。Enhancements:
Documentation:
Tests:
Original summary in English
Summary by Sourcery
Introduce a core localization system providing multi-language support, including configuration, culture-aware string retrieval, and integration with the existing system architecture.
New Features:
Enhancements:
Documentation:
Tests: