Skip to content

Commit 6222734

Browse files
committed
看来不能用隐式接口实现,因为只有 Avalonia 的 axaml 和 Uno/WinUI 的 x:Bind 能用,WPF 和 Uno 的 Binding 都会因为类型没有对应的属性而绑定失败
1 parent f88dd30 commit 6222734

File tree

10 files changed

+194
-82
lines changed

10 files changed

+194
-82
lines changed

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)

src/dotnetCampus.Localizations.Analyzer/Generators/LocalizationTypeGenerator.cs

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,49 +36,36 @@ private void Execute(SourceProductionContext context, (LocalizationGeneratingMod
3636
var defaultLanguageIdentifier = IetfLanguageTagToIdentifier(defaultLanguage);
3737
var currentLanguageIdentifier = IetfLanguageTagToIdentifier(currentLanguage);
3838

39-
// 生成 Localization.current.g.cs
40-
var currentCode = GenerateSetCurrentMethod(typeNamespace, typeName, models);
41-
context.AddSource($"{typeName}.current.g.cs", SourceText.From(currentCode, Encoding.UTF8));
42-
43-
// 生成 Localization.default.g.cs
39+
// 生成 Localization.g.cs
4440
var localizationFile = GeneratorInfo.GetEmbeddedTemplateFile<Localization>();
4541
var originalText = ReplaceNamespaceAndTypeName(localizationFile.Content, typeNamespace, typeName);
4642
var defaultCode = originalText
47-
.Replace("""ILocalizedValues Default { get; } = new LspPlaceholder("default", null)""",
48-
$"global::{GeneratorInfo.RootNamespace}.ILocalizedValues Default {{ get; }} = new global::{GeneratorInfo.RootNamespace}.{nameof(LocalizationValues)}_{defaultLanguageIdentifier}(null)")
49-
.Replace("""ILocalizedValues Current { get; private set; } = new LspPlaceholder("current", null)""", defaultLanguage == currentLanguage
50-
? $"global::{GeneratorInfo.RootNamespace}.ILocalizedValues Current {{ get; private set; }} = Default"
51-
: $"global::{GeneratorInfo.RootNamespace}.ILocalizedValues Current {{ get; private set; }} = new global::{GeneratorInfo.RootNamespace}.{nameof(LocalizationValues)}_{currentLanguageIdentifier}(Default)");
52-
context.AddSource($"{typeName}.default.g.cs", SourceText.From(defaultCode, Encoding.UTF8));
43+
.Replace("LocalizedValues _default = new LocalizedValues(null!);",
44+
$"global::{GeneratorInfo.RootNamespace}.LocalizedValues _default = CreateLocalizedValues(\"{defaultLanguage}\");")
45+
.Replace("LocalizedValues _current = new LocalizedValues(null!);", defaultLanguage == currentLanguage
46+
? $"global::{GeneratorInfo.RootNamespace}.LocalizedValues _current = _default"
47+
: $"global::{GeneratorInfo.RootNamespace}.LocalizedValues _current = CreateLocalizedValues(\"{currentLanguage}\");");
48+
defaultCode = TemplateRegexes.FlagRegex.Replace(defaultCode, GenerateCreateLocalizedValues(defaultLanguage, models));
49+
defaultCode = defaultCode
50+
.Replace("ILocalizedValues", $"global::{GeneratorInfo.RootNamespace}.ILocalizedValues")
51+
.Replace(" LocalizedValues", $" global::{GeneratorInfo.RootNamespace}.LocalizedValues");
52+
context.AddSource($"{typeName}.g.cs", SourceText.From(defaultCode, Encoding.UTF8));
5353
}
5454

55-
private string GenerateSetCurrentMethod(string typeNamespace, string typeName, ImmutableArray<LocalizationFileModel> models) => $$"""
56-
#nullable enable
57-
58-
namespace {{typeNamespace}};
59-
60-
partial class {{typeName}}
61-
{
62-
/// <summary>
63-
/// 设置当前的本地化字符串集。
64-
/// </summary>
65-
/// <param name="ietfLanguageTag">要设置的 IETF 语言标签。</param>
66-
public static void SetCurrent(string ietfLanguageTag)
67-
{
68-
Current = ietfLanguageTag switch
69-
{
70-
{{string.Join("\n", models.Select(x => ConvertModelToPatternMatch(typeNamespace, x)))}}
71-
_ => throw new global::System.ArgumentException($"The language tag {ietfLanguageTag} is not supported.", nameof(ietfLanguageTag)),
72-
};
73-
}
74-
}
55+
private string GenerateCreateLocalizedValues(string defaultIetfTag, ImmutableArray<LocalizationFileModel> models) => $$"""
7556
57+
{{string.Join("\n", models.Select(x => ConvertModelToPatternMatch(defaultIetfTag, x)))}}
7658
""";
7759

78-
private string ConvertModelToPatternMatch(string typeNamespace, LocalizationFileModel model)
60+
private string ConvertModelToPatternMatch(string defaultIetfTag, LocalizationFileModel model)
7961
{
80-
// "zh-hans" => new Lang_ZhHans(Default),
81-
return $" \"{model.IetfLanguageTag}\" => new global::{GeneratorInfo.RootNamespace}.{nameof(LocalizationValues)}_{IetfLanguageTagToIdentifier(model.IetfLanguageTag)}(Default),";
62+
var tagIdentifier = IetfLanguageTagToIdentifier(model.IetfLanguageTag);
63+
var defaultProvider = model.IetfLanguageTag == defaultIetfTag
64+
? "null"
65+
: "_default.LocalizedStringProvider";
66+
return $"""
67+
"{model.IetfLanguageTag}" => new global::{GeneratorInfo.RootNamespace}.LocalizedValues(new global::{GeneratorInfo.RootNamespace}.{nameof(LocalizedStringProvider)}_{tagIdentifier}({defaultProvider})),
68+
""";
8269
}
8370

8471
private static string ReplaceNamespaceAndTypeName(string sourceText, string rootNamespace, string? typeName)

src/dotnetCampus.Localizations.Analyzer/Generators/StringsGenerator.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@ private void Execute(SourceProductionContext context, (ImmutableArray<Localizati
3535
_ => throw new NotSupportedException($"Unsupported localization file format: {file.FileFormat}"),
3636
});
3737

38-
var code = transformer.ToImplementationCodeText(options.Namespace, file.IetfLanguageTag);
39-
context.AddSource($"{nameof(LocalizationValues)}.{file.IetfLanguageTag}.g.cs", SourceText.From(code, Encoding.UTF8));
38+
var code = transformer.ToProviderCodeText(options.Namespace, file.IetfLanguageTag);
39+
context.AddSource($"{nameof(LocalizedStringProvider)}.{file.IetfLanguageTag}.g.cs", SourceText.From(code, Encoding.UTF8));
4040

4141
if (file.IetfLanguageTag == options.DefaultLanguage)
4242
{
43-
var keyCode = transformer.ToInterfaceCodeText(options.Namespace);
44-
context.AddSource($"{nameof(ILocalizedValues)}.g.cs", SourceText.From(keyCode, Encoding.UTF8));
43+
var interfaceCode = transformer.ToInterfaceCodeText();
44+
context.AddSource($"{nameof(ILocalizedValues)}.g.cs", SourceText.From(interfaceCode, Encoding.UTF8));
45+
46+
var implementationCode = transformer.ToImplementationCodeText(options.TypeName);
47+
context.AddSource($"{nameof(LocalizedValues)}.g.cs", SourceText.From(implementationCode, Encoding.UTF8));
4548
}
4649
}
4750
}

0 commit comments

Comments
 (0)