Added Ellie.Bot.Generators.Strings
This commit is contained in:
parent
9a379044d9
commit
6e293408ce
4 changed files with 202 additions and 0 deletions
|
@ -5,6 +5,14 @@ VisualStudioVersion = 17.6.33815.320
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0B2F1537-4BF0-422B-A0DD-8F9CCEFB340F}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
CHANGELOG.md = CHANGELOG.md
|
||||||
|
LICENSE.md = LICENSE.md
|
||||||
|
README.md = README.md
|
||||||
|
Dockerfile = Dockerfile
|
||||||
|
NuGet.Config = NuGet.Config
|
||||||
|
EndProjectSection
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ellie", "src\Ellie\Ellie.csproj", "{2BAF005E-781D-45FF-B218-E6361F5E8CD4}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ellie", "src\Ellie\Ellie.csproj", "{2BAF005E-781D-45FF-B218-E6361F5E8CD4}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ayu", "ayu", "{5284415D-A43F-4539-9483-410124199743}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ayu", "ayu", "{5284415D-A43F-4539-9483-410124199743}"
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
|
<IsRoslynComponent>true</IsRoslynComponent>
|
||||||
|
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<RootNamespace>Ellie.Generators</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" PrivateAssets="all" GeneratePathProperty="true" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="GetDependencyTargetPaths">
|
||||||
|
<ItemGroup>
|
||||||
|
<TargetPathWithTargetPlatformMoniker Include="$(PKGNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" IncludeRuntimeDependency="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
140
src/Ellie.Bot.Generators.Strings/LocalizedStringsGenerator.cs
Normal file
140
src/Ellie.Bot.Generators.Strings/LocalizedStringsGenerator.cs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#nullable enable
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Ellie.Generators
|
||||||
|
{
|
||||||
|
internal readonly struct TranslationPair
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
public string Value { get; }
|
||||||
|
|
||||||
|
public TranslationPair(string name, string value)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Generator]
|
||||||
|
public class LocalizedStringsGenerator : ISourceGenerator
|
||||||
|
{
|
||||||
|
// private const string LOC_STR_SOURCE = @"namespace Ellie
|
||||||
|
// {
|
||||||
|
// public readonly struct LocStr
|
||||||
|
// {
|
||||||
|
// public readonly string Key;
|
||||||
|
// public readonly object[] Params;
|
||||||
|
//
|
||||||
|
// public LocStr(string key, params object[] data)
|
||||||
|
// {
|
||||||
|
// Key = key;
|
||||||
|
// Params = data;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }";
|
||||||
|
|
||||||
|
public void Initialize(GeneratorInitializationContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(GeneratorExecutionContext context)
|
||||||
|
{
|
||||||
|
var file = context.AdditionalFiles.First(x => x.Path.EndsWith("responses.en-US.json"));
|
||||||
|
|
||||||
|
var fields = GetFields(file.GetText()?.ToString());
|
||||||
|
|
||||||
|
using (var stringWriter = new StringWriter())
|
||||||
|
using (var sw = new IndentedTextWriter(stringWriter))
|
||||||
|
{
|
||||||
|
sw.WriteLine("#pragma warning disable CS8981");
|
||||||
|
sw.WriteLine("namespace Ellie;");
|
||||||
|
sw.WriteLine();
|
||||||
|
|
||||||
|
sw.WriteLine("public static class strs");
|
||||||
|
sw.WriteLine("{");
|
||||||
|
sw.Indent++;
|
||||||
|
|
||||||
|
var typedParamStrings = new List<string>(10);
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
var matches = Regex.Matches(field.Value, @"{(?<num>\d)[}:]");
|
||||||
|
var max = 0;
|
||||||
|
foreach (Match match in matches)
|
||||||
|
{
|
||||||
|
max = Math.Max(max, int.Parse(match.Groups["num"].Value) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedParamStrings.Clear();
|
||||||
|
var typeParams = new string[max];
|
||||||
|
var passedParamString = string.Empty;
|
||||||
|
for (var i = 0; i < max; i++)
|
||||||
|
{
|
||||||
|
typedParamStrings.Add($"in T{i} p{i}");
|
||||||
|
passedParamString += $", p{i}";
|
||||||
|
typeParams[i] = $"T{i}";
|
||||||
|
}
|
||||||
|
|
||||||
|
var sig = string.Empty;
|
||||||
|
var typeParamStr = string.Empty;
|
||||||
|
if (max > 0)
|
||||||
|
{
|
||||||
|
sig = $"({string.Join(", ", typedParamStrings)})";
|
||||||
|
typeParamStr = $"<{string.Join(", ", typeParams)}>";
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.WriteLine("public static LocStr {0}{1}{2} => new LocStr(\"{3}\"{4});",
|
||||||
|
field.Name,
|
||||||
|
typeParamStr,
|
||||||
|
sig,
|
||||||
|
field.Name,
|
||||||
|
passedParamString);
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Indent--;
|
||||||
|
sw.WriteLine("}");
|
||||||
|
|
||||||
|
|
||||||
|
sw.Flush();
|
||||||
|
context.AddSource("strs.g.cs", stringWriter.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// context.AddSource("LocStr.g.cs", LOC_STR_SOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TranslationPair> GetFields(string? dataText)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(dataText))
|
||||||
|
return new();
|
||||||
|
|
||||||
|
Dictionary<string, string> data;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var output = JsonConvert.DeserializeObject<Dictionary<string, string>>(dataText!);
|
||||||
|
if (output is null)
|
||||||
|
return new();
|
||||||
|
|
||||||
|
data = output;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Failed parsing responses file.");
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = new List<TranslationPair>();
|
||||||
|
foreach (var entry in data)
|
||||||
|
{
|
||||||
|
list.Add(new(
|
||||||
|
entry.Key,
|
||||||
|
entry.Value
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
src/Ellie.Bot.Generators.Strings/README.md
Normal file
24
src/Ellie.Bot.Generators.Strings/README.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
## Generators
|
||||||
|
|
||||||
|
Project which contains source generators required for Ellie project
|
||||||
|
|
||||||
|
---
|
||||||
|
### 1) Localized Strings Generator
|
||||||
|
|
||||||
|
-- Why --
|
||||||
|
Type safe response strings access, and enforces correct usage of response strings.
|
||||||
|
|
||||||
|
-- How it works --
|
||||||
|
Creates a file "strs.cs" containing a class called "strs" in "Ellie" namespace.
|
||||||
|
|
||||||
|
Loads "data/strings/responses.en-US.json" and creates a property or a function for each key in the responses json file based on whether the value has string format placeholders or not.
|
||||||
|
|
||||||
|
- If a value has no placeholders, it creates a property in the strs class which returns an instance of a LocStr struct containing only the key and no replacement parameters
|
||||||
|
|
||||||
|
- If a value has placeholders, it creates a function with the same number of arguments as the number of placeholders, and passes those arguments to the LocStr instance
|
||||||
|
|
||||||
|
-- How to use --
|
||||||
|
1. Add a new key to responses.en-US.json "greet_me": "Hello, {0}"
|
||||||
|
2. You now have access to a function strs.greet_me(obj p1)
|
||||||
|
3. Using "GetText(strs.greet_me("Me"))" will return "Hello, Me"
|
||||||
|
|
Loading…
Reference in a new issue