Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ namespace SampleApp;

// The default language is used to generate localization interfaces, so it must be the most complete one.
// The current language is optional. If not specified, the current OS UI language will be used.
// The notification is optional. If true, when the current language changes, the UI will be notified to update the localization text.
[LocalizedConfiguration(Default = "en", Current = "zh-hans", SupportsNotification = false)]
// The notification is optional. Use NotificationMode.CurrentCulturePropertyChanged to notify the UI to update the localization text when the current language changes.
[LocalizedConfiguration(Default = "en", Current = "zh-hans", NotificationMode = NotificationMode.InitOnly)]
public partial class LocalizedText;
```

Expand Down Expand Up @@ -143,15 +143,15 @@ public sealed partial class MainPage : Page
If you want to add real-time language switching support, you can modify the `LocalizedText` class as follows:

```csharp
[LocalizedConfiguration(Default = "en-US", SupportsNotification = true)]
[LocalizedConfiguration(Default = "en-US", NotificationMode = NotificationMode.CurrentCulturePropertyChanged)]
public static partial class LocalizedText
{
public static AppBuilder UseCompiledLang(this AppBuilder appBuilder)
{
if (OperatingSystem.IsWindows())
{
var language = GetUserProfileLanguage() ?? CultureInfo.CurrentUICulture.Name;
_ = SetCurrent(language);
SetCurrent(language);
SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
}
else
Expand All @@ -166,12 +166,12 @@ public static partial class LocalizedText
{
if (e.Category is UserPreferenceCategory.Locale)
{
Dispatcher.UIThread.InvokeAsync(async () =>
Dispatcher.UIThread.InvokeAsync(() =>
{
var language = GetUserProfileLanguage();
if (language is not null)
{
await SetCurrent(language);
SetCurrent(language);
}
}, DispatcherPriority.Background);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,6 @@ public string Generate(LocalizationGeneratingModel model, string ietfLanguageTag
t.AddAttribute($"""[global::System.Diagnostics.DebuggerDisplay("[{ietfLanguageTag}]")]""");
t.AddBaseTypes(allInterfaces.ToArray());
t.AddRawMembers($"public static LocalizedValues_{tagIdentifier} Instance {{ get; }} = new();");
if (!isNestedSource)
{
t.AddRawMembers(
$"""public string IetfLanguageTag => "{ietfLanguageTag}";""",
"""public string this[string key] => throw new global::System.NotSupportedException("Compiled 模式不支持基于字符串 key 的动态索引,请使用类型化属性。");""");
}
t.AddRawMembers(GenerateCompiledExplicitMembers(referenceTransformer.Tree, transformer));
});
};
Expand Down Expand Up @@ -70,8 +64,7 @@ public string GenerateNotifiable(LocalizationGeneratingModel model)
if (!isNestedSource)
{
builder
.Using("DotNetCampus.Localizations")
.UsingTypeAlias("ILocalizedStringProvider", "DotNetCampus.Localizations.ILocalizedStringProvider");
.Using("DotNetCampus.Localizations");
}

var allInterfaces = new List<string> { "ILocalizedValues" };
Expand All @@ -94,12 +87,6 @@ public string GenerateNotifiable(LocalizationGeneratingModel model)
t.AddBaseTypes(allInterfaces.ToArray());
t.AddRawMembers(GenerateNotifiableCompiledFields(nonLeafNodes));
t.AddRawMembers(GenerateNotifiableCompiledConstructor(nonLeafNodes));
if (!isNestedSource)
{
t.AddRawMembers(
"public string IetfLanguageTag => (_inner as ILocalizedStringProvider)?.IetfLanguageTag ?? \"\";",
"public string this[string key] => (_inner as ILocalizedStringProvider)?[key] ?? \"\";");
}
t.AddRawMembers(GenerateCompiledExplicitMembersForNotifiable(transformer.Tree));
t.AddRawMembers(
GenerateCompiledSetInnerMethod(nonLeafNodes),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private void AddImmutableValuesDeclarations(IAllowTypeDeclaration target, string
target.AddTypeDeclaration("internal sealed class ImmutableLocalizedValues(ILocalizedStringProvider provider)", t => t
.AddGeneratedToolAndEditorBrowsingAttributes()
.AddAttribute("[global::System.Diagnostics.DebuggerDisplay(\"[{LocalizedStringProvider.IetfLanguageTag}] " + typeName + ".???\")]")
.AddBaseTypes("ILocalizedValues")
.AddBaseTypes("IDictionaryLocalizedValues")
.AddRawMembers(
"public ILocalizedStringProvider LocalizedStringProvider => provider;",
"public string IetfLanguageTag => provider.IetfLanguageTag;",
Expand Down Expand Up @@ -106,7 +106,7 @@ private void AddNotifiableValuesDeclarations(IAllowTypeDeclaration target, strin
.WithSummaryComment("提供可通知属性变更的本地化字符串集,当语言文化切换时会发出属性变更通知。")
.AddGeneratedToolAndEditorBrowsingAttributes()
.AddAttribute("[global::System.Diagnostics.DebuggerDisplay(\"[{LocalizedStringProvider.IetfLanguageTag}] " + typeName + ".???\")]")
.AddBaseTypes("ILocalizedValues", "INotifyPropertyChanged")
.AddBaseTypes("IDictionaryLocalizedValues", "INotifyPropertyChanged")
.AddRawMembers(
"public ILocalizedStringProvider LocalizedStringProvider { get; private set; }",
GenerateNotifiableConstructor("NotifiableLocalizedValues", root),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ public string Generate(LocalizationGeneratingModel model)
using var builder = new SourceTextBuilder(GeneratorInfo.RootNamespace);
builder
.Using("DotNetCampus.Localizations")
.UsingTypeAlias("ILocalizedStringProvider", "DotNetCampus.Localizations.ILocalizedStringProvider")
.UsingTypeAlias("LocalizedString", "DotNetCampus.Localizations.LocalizedString");
AddInterfaceDeclarations(builder, model.TypeAccessibility, includeBaseInterface: true);
AddInterfaceDeclarations(builder, model, model.TypeAccessibility);
return builder.ToString();
}

Expand All @@ -23,20 +22,43 @@ public string GenerateNested(LocalizationGeneratingModel model)
using var builder = new SourceTextBuilder(model.Namespace);
builder.AddTypeDeclaration($"partial class {model.TypeName}", wrapper =>
{
AddInterfaceDeclarations(wrapper, "public", includeBaseInterface: false);
AddInterfaceDeclarations(wrapper, model, "public");
});
return builder.ToString();
}

private void AddInterfaceDeclarations(IAllowTypeDeclaration builder, string accessibility, bool includeBaseInterface)
private void AddInterfaceDeclarations(IAllowTypeDeclaration builder, LocalizationGeneratingModel model, string accessibility)
{
// ILocalizedValues 仅承载 Lang.A.B.C 形式的强类型导航语法糖,不携带任何随生成模式变化的运行时能力。
builder.AddTypeDeclaration($"{accessibility} partial interface ILocalizedValues", t => t
.WithSummaryComment("提供本地化字符串的访问接口。通过属性导航访问各分组和叶子节点的本地化值。")
.AddGeneratedToolAndEditorBrowsingAttributes()
.If(includeBaseInterface, t => t.AddBaseTypes("ILocalizedStringProvider"))
.AddRawMembers(GenerateInterfacePropertyMembers(transformer.Tree))
);

// 仅 Dictionary 模式提供基于运行时字符串 key 的动态索引能力;Compiled 模式此能力应在编译期即不可用。
if (model.GenerationMode == GenerationMode.Dictionary)
{
builder.AddTypeDeclaration($"{accessibility} partial interface IDictionaryLocalizedValues : ILocalizedValues", t => t
.WithSummaryComment("在强类型导航访问之外,额外支持通过运行时字符串 key 动态访问本地化字符串。仅 Dictionary 生成模式提供。")
.AddGeneratedToolAndEditorBrowsingAttributes()
.AddRawMembers(
"""
/// <summary>
/// 获取指定键的本地化字符串。如果字符串包含占位符,则返回值会包含形如 "{0}" "{1}" 的占位符用于格式化。
/// </summary>
/// <param name="key">要获取的本地化字符串的键。</param>
string this[string key] { get; }
""",
"""
/// <summary>
/// 获取符合 IETF 规范的当前语言标签。
/// </summary>
string IetfLanguageTag { get; }
""")
);
}

foreach (var node in transformer.EnumerateAllNonLeafDescendants(transformer.Tree))
{
var nodeTypeName = node.GetFullIdentifierKey("_");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private string GenerateDictionaryMainClass(
/// <summary>
/// 获取默认语言的本地化字符串集。
/// </summary>
public static {typePrefix}ILocalizedValues Default => _default;
public static {typePrefix}IDictionaryLocalizedValues Default => _default;
""",
$"""
/// <summary>
Expand All @@ -166,7 +166,7 @@ public static void SetCurrent(string languageTag)
/// </summary>
/// <param name="languageTag">语言标签。</param>
/// <returns>对应语言的本地化字符串集。</returns>
public static {typePrefix}ILocalizedValues Create(string languageTag) => new {typePrefix}ImmutableLocalizedValues(CreateLocalizedStringProvider(languageTag));
public static {typePrefix}IDictionaryLocalizedValues Create(string languageTag) => new {typePrefix}ImmutableLocalizedValues(CreateLocalizedStringProvider(languageTag));
""",
$$"""
private static {{typePrefix}}ILocalizedStringProvider CreateLocalizedStringProvider(string languageTag)
Expand Down Expand Up @@ -223,13 +223,13 @@ public static void SetCurrent(string languageTag)
/// <summary>
/// 获取默认语言的本地化字符串集。
/// </summary>
public static {typePrefix}ILocalizedValues Default => _default;
public static {typePrefix}IDictionaryLocalizedValues Default => _default;
""",
$"""
/// <summary>
/// 获取当前语言的本地化字符串集。调用 <see cref="SetCurrent(string)"/> 后需重新访问此属性获取新值。
/// </summary>
public static {typePrefix}ILocalizedValues Current => _current;
public static {typePrefix}IDictionaryLocalizedValues Current => _current;
""",
$$"""
/// <summary>
Expand All @@ -247,7 +247,7 @@ public static void SetCurrent(string languageTag)
/// </summary>
/// <param name="languageTag">语言标签。</param>
/// <returns>对应语言的本地化字符串集。</returns>
public static {typePrefix}ILocalizedValues Create(string languageTag) => GetOrCreateLocalizedValues(languageTag);
public static {typePrefix}IDictionaryLocalizedValues Create(string languageTag) => GetOrCreateLocalizedValues(languageTag);
""",
$$"""
private static {{typePrefix}}ImmutableLocalizedValues GetOrCreateLocalizedValues(string languageTag)
Expand Down
49 changes: 0 additions & 49 deletions src/DotNetCampus.Localizations/LocalizedConfigurationAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,6 @@ public class LocalizedConfigurationAttribute : Attribute
/// </remarks>
public string? Current { get; init; }

/// <summary>
/// 指定开发时预览语言项文字所用的语言。指定后可在语言项的文档注释中查看此语言项的文本,方便开发时人工检查语言项的正确性。
/// </summary>
/// <remarks>
/// 一般无需指定。如果没有指定,则会使用编译时当前计算机的语言文化。
/// </remarks>
public string? Preview { get; init; }

/// <summary>
/// 指定是否确保所有语言文件中的键都是一致的。默认值为 false,不执行检查。如果是 true,则会在生成代码时检查所有语言文件中的键是否一致,如果不相同则会报错。
/// </summary>
Expand All @@ -62,26 +54,6 @@ public class LocalizedConfigurationAttribute : Attribute
/// </summary>
public DependencyMode DependencyMode { get; init; }

/// <summary>
/// 指定本地化文件的格式。
/// </summary>
public LocalizationFileFormat FileFormat { get; init; }

/// <summary>
/// 默认情况下,DotNetCampus.SourceLocalizations 会自动在项目中寻找看起来像本地化文件的文件(.toml/.yaml/.yml,且文件名或文件夹名中包含语言文化名称)。<br/>
/// 但如果多语言文件在项目之外,或你不希望项目中某些看起来像本地化文件的文件被视作最终的本地化文件,可以通过此属性来指定本地化文件的搜索正则表达式。<br/>
/// 为此,你需要做这些事情:
/// <list type="number">
/// <item>修改项目文件(csproj)或其他 MSBuild 属性文件(.props/.targets),确保在时机 DotNetCampusGenerateLocalizations 之前,修改 LocalizationFile 集合,使其包含所有你希望被视作本地化文件的文件。</item>
/// <item>如果保持此属性为 <see langword="null"/>,那么上一步中的所有文件都将成为目标本地化文件;而指定此属性会过滤这些文件,仅包含符合此属性指定模式的文件。(指定此属性不会导致引入比上一步中更多的文件。)</item>
/// </list>
/// 此正则表达式将匹配路径,包含其所在的文件夹路径和文件名(包含扩展名)。
/// </summary>
/// <remarks>
/// 如果项目中只存在一套多语言机制,通常不需要指定此属性,直接修改 .csproj/.props/.targets 中的 LocalizationFile 集合即可。
/// </remarks>
public string? LocalizationFileRegex { get; init; }

/// <summary>
/// 是否支持在修改当前语言时,发出属性变更通知,可用于数据绑定。
/// </summary>
Expand Down Expand Up @@ -157,24 +129,3 @@ public enum DependencyMode
/// </summary>
NestedSource,
}

/// <summary>
/// 指定本地化文件的格式。
/// </summary>
public enum LocalizationFileFormat
{
/// <summary>
/// 根据文件的扩展名决定文件格式。
/// </summary>
AutoDetect,

/// <summary>
/// 视目标文件为 TOML 格式。
/// </summary>
Toml,

/// <summary>
/// 视目标文件为 YAML 格式。
/// </summary>
Yaml,
}
Loading