Skip to content

Commit 51824c9

Browse files
authored
Merge pull request #11 from dotnet-campus/t/lvyi/type-node
[不兼容警告] 源生成器现在除了接口外也生成类,便于 WPF/WinUI/Uno 绑定
2 parents 4963242 + 6b0f18e commit 51824c9

File tree

16 files changed

+343
-156
lines changed

16 files changed

+343
-156
lines changed
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3+
<Import Project="..\..\src\dotnetCampus.Localizations\Package\build\Package.props" />
4+
35
<PropertyGroup>
4-
<OutputType>WinExe</OutputType>
6+
<OutputType>Exe</OutputType>
57
<TargetFramework>net8.0</TargetFramework>
68
</PropertyGroup>
79

810
<ItemGroup>
11+
<ProjectReference Include="..\..\src\dotnetCampus.Localizations.Analyzer\dotnetCampus.Localizations.Analyzer.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
912
<ProjectReference Include="..\..\src\dotnetCampus.Localizations\dotnetCampus.Localizations.csproj" />
1013
</ItemGroup>
1114

1215
<ItemGroup>
13-
<LocalizationFile Include="Localizations\**\*.yaml" />
16+
<!-- 以下 yaml 和 toml 只能选其一,否则生成的语言类型名称会相同。 -->
17+
<!-- <LocalizationFile Include="Localizations\**\*.yaml" /> -->
1418
<LocalizationFile Include="Localizations\**\*.toml" />
1519
</ItemGroup>
1620

21+
<Import Project="..\..\src\dotnetCampus.Localizations\Package\build\Package.targets" />
22+
1723
</Project>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
A.A1 = "Words"
22
A.A2 = "Error code: {errorCode:int}"
33
A.A3 = "Error: {error}"
4+
A.A4.A41 = "Warning: {error}"
5+
A.A4.A42 = "Tip: {0}={1}"
6+
B.C.A1 = "Hello"
7+
B.C.A2.A3 = "Hello"
8+
B.C.A2.A4.Add.A1123.Sddd.Ffffff.Cxzzxc = "Hello"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
A.A1 = "文本"
22
A.A2 = "错误码:{errorCode:int}"
33
A.A3 = "错误:{error}"
4+
A.A4.A41 = "警告:{error}"
5+
A.A4.A42 = "提示:{0}={1}"
6+
B.C.A1 = "Hello"
7+
B.C.A2.A3 = "Hello"
8+
B.C.A2.A4.Add.A1123.Sddd.Ffffff.Cxzzxc = "Hello"
Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,15 @@
1-
using System.Collections.Frozen;
2-
using System.ComponentModel;
3-
using dotnetCampus.Localizations;
1+
using dotnetCampus.Localizations;
42

53
namespace LocalizationSample;
64

75
internal class Program
86
{
97
public static void Main(string[] args)
108
{
9+
var a = Lang.Current.A.A2.ToString(1);
10+
Console.WriteLine(a);
1111
}
1212
}
1313

1414
[LocalizedConfiguration(Default = "zh-hans", Current = "en")]
1515
internal partial class Lang;
16-
17-
[EditorBrowsable(EditorBrowsableState.Never)]
18-
public interface ILocalizedValues : ILocalizedStringProvider
19-
{
20-
ILocalizedValues_A A => (ILocalizedValues_A)this;
21-
}
22-
23-
[EditorBrowsable(EditorBrowsableState.Never)]
24-
public interface ILocalizedValues_A : ILocalizedStringProvider
25-
{
26-
LocalizedString A1 => this.Get0("A.A1");
27-
28-
LocalizedString<int> A2 => this.Get1<int>("A.A2");
29-
30-
LocalizedString<object> A3 => this.Get1<object>("A.A3");
31-
}
32-
33-
public class Lang_ZhHans(ILocalizedValues? fallback) : ILocalizedValues,
34-
ILocalizedValues_A
35-
{
36-
private readonly FrozenDictionary<string, string> _strings = new Dictionary<string, string>
37-
{
38-
{ "A.A1", "文字" },
39-
{ "A.A2", "错误码:{0}" },
40-
{ "A.A3", "错误:{0}" },
41-
}.ToFrozenDictionary();
42-
43-
public string this[string key] => _strings[key] ?? fallback![key];
44-
45-
public string IetfLanguageTag => "zh-hans";
46-
}
47-
48-
public class Lang_En(ILocalizedValues? fallback) : ILocalizedValues,
49-
ILocalizedValues_A
50-
{
51-
private readonly FrozenDictionary<string, string> _strings = new Dictionary<string, string>
52-
{
53-
{ "A.A1", "Words" },
54-
{ "A.A2", "Error code: {0}" },
55-
{ "A.A3", "Error: {0}" },
56-
}.ToFrozenDictionary();
57-
58-
public string this[string key] => _strings[key] ?? fallback![key];
59-
60-
public string IetfLanguageTag => "en";
61-
}

src/dotnetCampus.Localizations.Analyzer/Assets/Analyzers/LspPlaceholder.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/dotnetCampus.Localizations.Analyzer/Assets/Templates/ILocalizedValues.g.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
namespace dotnetCampus.Localizations.Assets.Templates;
77

88
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
9-
public partial interface ILocalizedValues : ILocalizedStringProvider
9+
public partial interface ILocalizedValues
1010
{
1111
// <FLAG>
12-
// ILocalizedValues A => (ILocalizedValues_A)this;
13-
// LocalizedString A1 => this.Get0("A.A1");
12+
// LocalizedString A1 { get; }
13+
// LocalizedString<int> A2 { get; }
14+
// ILocalizedValues_A_A3 A3 { get; }
1415
// </FLAG>
1516
}

src/dotnetCampus.Localizations.Analyzer/Assets/Templates/Localization.g.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,34 @@ namespace dotnetCampus.Localizations.Assets.Templates;
44

55
partial class Localization
66
{
7+
private static LocalizedValues _default = new LocalizedValues(null!);
8+
private static LocalizedValues _current = new LocalizedValues(null!);
9+
710
/// <summary>
811
/// 获取默认的本地化字符串集。
912
/// </summary>
10-
public static ILocalizedValues Default { get; } = new LspPlaceholder("default", null);
13+
public static ILocalizedValues Default => _default;
1114

1215
/// <summary>
1316
/// 获取当前的本地化字符串集。
1417
/// </summary>
15-
public static ILocalizedValues Current { get; private set; } = new LspPlaceholder("current", null);
18+
public static ILocalizedValues Current => _current;
19+
20+
/// <summary>
21+
/// 设置当前的本地化字符串集。
22+
/// </summary>
23+
/// <param name="ietfLanguageTag">要设置的 IETF 语言标签。</param>
24+
public static void SetCurrent(string ietfLanguageTag) => _current = CreateLocalizedValues(ietfLanguageTag);
25+
26+
/// <summary>
27+
/// 创建指定 IETF 语言标签的本地化字符串集。
28+
/// </summary>
29+
/// <param name="ietfLanguageTag">IETF 语言标签。</param>
30+
private static LocalizedValues CreateLocalizedValues(string ietfLanguageTag) => ietfLanguageTag switch
31+
{
32+
// <FLAG>
33+
"en" => new LocalizedValues(null!),
34+
// </FLAG>
35+
_ => throw new global::System.ArgumentException($"The language tag {ietfLanguageTag} is not supported.", nameof(ietfLanguageTag)),
36+
};
1637
}

src/dotnetCampus.Localizations.Analyzer/Assets/Templates/LocalizationValues.g.cs renamed to src/dotnetCampus.Localizations.Analyzer/Assets/Templates/LocalizedStringProvider.g.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ namespace dotnetCampus.Localizations.Assets.Templates;
77

88
/// <inheritdoc />
99
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
10-
public class LocalizationValues(ILocalizedValues? fallback) : ILocalizedValues
10+
public class LocalizedStringProvider(ILocalizedStringProvider? fallback) : ILocalizedStringProvider
1111
{
1212
/// <inheritdoc />
1313
public string IetfLanguageTag => "default";
1414

1515
/// <inheritdoc />
16-
public string this[string key]
16+
public string this[string key]
1717
{
1818
get
1919
{
@@ -27,7 +27,7 @@ public string this[string key]
2727
}
2828
return "";
2929
}
30-
}
30+
}
3131

3232
private readonly FrozenDictionary<string, string> _strings = new Dictionary<string, string>
3333
{
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#nullable enable
2+
3+
using ILocalizedStringProvider = global::dotnetCampus.Localizations.ILocalizedStringProvider;
4+
using LocalizedString = global::dotnetCampus.Localizations.LocalizedString;
5+
6+
namespace dotnetCampus.Localizations.Assets.Templates;
7+
8+
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
9+
public sealed class LocalizedValues(ILocalizedStringProvider provider) : ILocalizedValues
10+
{
11+
/// <summary>
12+
/// 获取本地化字符串提供器。
13+
/// </summary>
14+
public ILocalizedStringProvider LocalizedStringProvider => provider;
15+
16+
// <FLAG>
17+
// public LocalizedString A1 => provider.Get0("A.A1");
18+
// public LocalizedString<int> A2 => provider.Get1<int>("A.A2");
19+
// public ILocalizedValues_A_A3 A3 { get; } = new LocalizedValues_A_A3(provider);
20+
// </FLAG>
21+
}

src/dotnetCampus.Localizations.Analyzer/Generators/CodeTransforming/LocalizationCodeTransformer.cs

Lines changed: 101 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public LocalizationCodeTransformer(string content, ILocalizationFileReader reade
3737

3838
#region Language Key Interfaces
3939

40-
public string ToInterfaceCodeText(string rootNamespace) => $"""
40+
public string ToInterfaceCodeText() => $"""
4141
#nullable enable
4242
4343
using global::dotnetCampus.Localizations;
@@ -66,35 +66,37 @@ private string RecursiveConvertLocalizationTreeNodeToKeyInterfaceCode(Localizati
6666
{
6767
if (x.Item.ValueArgumentTypes.Length is 0)
6868
{
69-
return $"""
69+
return $$"""
7070
/// <summary>
71-
/// {ConvertValueToComment(x.Item.SampleValue)}
71+
/// {{ConvertValueToComment(x.Item.SampleValue)}}
7272
/// </summary>
73-
LocalizedString {x.IdentifierKey} => this.Get0("{x.Item.Key}");
73+
LocalizedString {{x.IdentifierKey}} { get; }
7474
""";
7575
}
7676
else
7777
{
7878
var genericTypes = string.Join(", ", x.Item.ValueArgumentTypes);
79-
return $"""
79+
return $$"""
8080
/// <summary>
81-
/// {ConvertValueToComment(x.Item.SampleValue)}
81+
/// {{ConvertValueToComment(x.Item.SampleValue)}}
8282
/// </summary>
83-
LocalizedString<{genericTypes}> {x.IdentifierKey} => this.Get{x.Item.ValueArgumentTypes.Length}<{genericTypes}>("{x.Item.Key}");
83+
LocalizedString<{{genericTypes}}> {{x.IdentifierKey}} { get; }
8484
""";
8585
}
8686
}
8787
else
8888
{
89-
return $" ILocalizedValues_{identifierKey} {x.IdentifierKey} => (ILocalizedValues_{identifierKey})this;";
89+
return $$"""
90+
ILocalizedValues_{{identifierKey}} {{x.IdentifierKey}} { get; }
91+
""";
9092
}
9193
});
9294
return $$"""
9395
9496
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
95-
public{{(depth is 0 ? " partial" : "")}} interface ILocalizedValues{{nodeTypeName}} : ILocalizedStringProvider
97+
public{{(depth is 0 ? " partial" : "")}} interface ILocalizedValues{{nodeTypeName}}
9698
{
97-
{{string.Join("\n", propertyLines)}}
99+
{{string.Join("\n\n", propertyLines)}}
98100
}
99101
{{string.Concat(node.Children.Select(x => RecursiveConvertLocalizationTreeNodeToKeyInterfaceCode(x, depth + 1)))}}
100102
""";
@@ -115,19 +117,95 @@ private string ConvertValueToComment(string? value)
115117

116118
#region Language Value Implementations
117119

118-
public string ToImplementationCodeText(string rootNamespace, string ietfLanguageTag)
120+
public string ToImplementationCodeText(string typeName) => $"""
121+
#nullable enable
122+
123+
using global::dotnetCampus.Localizations;
124+
125+
using ILocalizedStringProvider = global::dotnetCampus.Localizations.ILocalizedStringProvider;
126+
using LocalizedString = global::dotnetCampus.Localizations.LocalizedString;
127+
128+
namespace {GeneratorInfo.RootNamespace};
129+
{RecursiveConvertLocalizationTreeNodeToKeyImplementationCode(Tree, 0, typeName)}
130+
""";
131+
132+
private string RecursiveConvertLocalizationTreeNodeToKeyImplementationCode(LocalizationTreeNode node, int depth, string typeName)
133+
{
134+
if (node.Children.Count is 0)
135+
{
136+
return "";
137+
}
138+
139+
var nodeKeyName = depth is 0
140+
? ""
141+
: "." + string.Join("_", node.IdentifierKey);
142+
var nodeTypeName = depth is 0
143+
? ""
144+
: "_" + string.Join("_", node.FullIdentifierKey);
145+
var propertyLines = node.Children.Select(x =>
146+
{
147+
var identifierKey = string.Join("_", x.FullIdentifierKey);
148+
if (x.Children.Count is 0)
149+
{
150+
if (x.Item.ValueArgumentTypes.Length is 0)
151+
{
152+
return $"""
153+
/// <inheritdoc />
154+
public LocalizedString {x.IdentifierKey} => provider.Get0("{x.Item.Key}");
155+
""";
156+
}
157+
else
158+
{
159+
var genericTypes = string.Join(", ", x.Item.ValueArgumentTypes);
160+
return $"""
161+
/// <inheritdoc />
162+
public LocalizedString<{genericTypes}> {x.IdentifierKey} => provider.Get{x.Item.ValueArgumentTypes.Length}<{genericTypes}>("{x.Item.Key}");
163+
""";
164+
}
165+
}
166+
else
167+
{
168+
return $$"""
169+
public ILocalizedValues_{{identifierKey}} {{x.IdentifierKey}} { get; } = new LocalizedValues_{{identifierKey}}(provider);
170+
""";
171+
}
172+
});
173+
return $$"""
174+
175+
[global::System.Diagnostics.DebuggerDisplay("[{LocalizedStringProvider.IetfLanguageTag}] {{typeName}}{{nodeKeyName}}.???")]
176+
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
177+
public sealed class LocalizedValues{{nodeTypeName}}(ILocalizedStringProvider provider) : ILocalizedValues{{nodeTypeName}}
178+
{
179+
/// <summary>
180+
/// 获取本地化字符串提供器。
181+
/// </summary>
182+
public ILocalizedStringProvider LocalizedStringProvider => provider;
183+
184+
{{string.Join("\n\n", propertyLines)}}
185+
186+
/// <summary>
187+
/// 获取非完整本地化字符串键的字符串表示。
188+
/// </summary>
189+
public override string ToString() => "{{typeName}}{{nodeKeyName}}.";
190+
}
191+
{{string.Concat(node.Children.Select(x => RecursiveConvertLocalizationTreeNodeToKeyImplementationCode(x, depth + 1, typeName)))}}
192+
""";
193+
}
194+
195+
#endregion
196+
197+
#region Language Value Provider
198+
199+
public string ToProviderCodeText(string rootNamespace, string ietfLanguageTag)
119200
{
120201
var typeName = IetfLanguageTagToIdentifier(ietfLanguageTag);
121-
var template = GeneratorInfo.GetEmbeddedTemplateFile<LocalizationValues>();
202+
var template = GeneratorInfo.GetEmbeddedTemplateFile<LocalizedStringProvider>();
122203
var code = template.Content
123204
.Replace($"namespace {template.Namespace};", $"namespace {GeneratorInfo.RootNamespace};")
124-
.Replace($"class {nameof(LocalizationValues)}", $"class {nameof(LocalizationValues)}_{typeName}")
125-
.Replace(
126-
$" : ILocalizedValues",
127-
$" : ILocalizedValues{string.Concat(EnumerateConvertTreeNodeToInterfaceNames(Tree.Children).Select(x => $",\n ILocalizedValues_{x}"))}")
205+
.Replace($"class {nameof(LocalizedStringProvider)}", $"class {nameof(LocalizedStringProvider)}_{typeName}")
128206
.Replace("""IetfLanguageTag => "default";""", $"""IetfLanguageTag => "{ietfLanguageTag}";""");
129-
var lines = LocalizationItems.Select(x => ConvertKeyValueToValueCodeLine(x.Key, x.Value));
130-
code = TemplateRegexes.FlagRegex.Replace(code, string.Concat(lines));
207+
var dictLines = LocalizationItems.Select(x => ConvertKeyValueToValueCodeLine(x.Key, x.Value));
208+
code = TemplateRegexes.FlagRegex.Replace(code, string.Concat(dictLines));
131209
return code;
132210
}
133211

@@ -137,6 +215,11 @@ private string ConvertKeyValueToValueCodeLine(string key, string value)
137215
return $"\n {{ \"{key}\", {escapedValue} }},";
138216
}
139217

218+
private string ConvertKeyValueToProperty(string key, string value)
219+
{
220+
return "";
221+
}
222+
140223
private IEnumerable<string> EnumerateConvertTreeNodeToInterfaceNames(IEnumerable<LocalizationTreeNode> nodes)
141224
{
142225
foreach (var node in nodes)

0 commit comments

Comments
 (0)