From 989a492740c632480df20034d85da2688d628351 Mon Sep 17 00:00:00 2001 From: JaJa Date: Fri, 5 Dec 2025 19:22:25 +0300 Subject: [PATCH 1/3] add yaml support --- .../Features/Feature2/Feature2.cs | 4 + Examples/Slang.Console/Program.cs | 12 ++ Examples/Slang.Console/Slang.Console.csproj | 1 + .../i18n/feature2_en-US.i18n.yaml | 82 +++++++++++++ .../i18n/feature2_ru-RU.i18n.yaml | 82 +++++++++++++ Slang.Generator/TranslateGenerator.cs | 7 +- Slang.Net/Constants.cs | 3 +- Slang.Net/Data/FilesRepository.cs | 14 ++- Slang.Net/Data/TranslationsDecoder.cs | 8 +- Slang.Net/Data/TranslationsRepository.cs | 2 +- Slang.Net/DiagnosticDescriptors.cs | 2 +- Slang.Net/GeneratorContext.cs | 6 +- Slang.Net/Slang.Net.csproj | 4 + Slang.Net/SystemTextJsonRawProvider.cs | 6 + Slang.Net/TranslationsGenerator.Roslyn4.cs | 19 +-- Slang.Net/TranslationsGenerator.cs | 12 +- Slang.Runner/Generator.cs | 1 + Slang.Runner/Program.cs | 12 +- Slang.Runner/Slang.Runner.csproj | 6 +- Slang.Shared/Constants.cs | 3 +- Slang.Tests/Helpers/TextNodeBuilder.cs | 2 +- .../Integration/Main/CompilationTests.cs | 2 +- Slang.Tests/Integration/Main/JsonTests.cs | 4 +- Slang.Tests/Integration/Main/YamlTests.cs | 53 ++++++++ .../Integration/Resources/yaml_de.yaml | 112 +++++++++++++++++ .../Integration/Resources/yaml_en.yaml | 114 ++++++++++++++++++ Slang.Tests/Slang.Tests.csproj | 6 +- .../Slang.Gpt/Domain/SlangGptTranslator.cs | 2 +- .../Translate/AdditionalFilesRepository.cs | 4 +- .../Translate/FilesRepository.cs | 2 +- 30 files changed, 543 insertions(+), 44 deletions(-) create mode 100644 Examples/Slang.Console/Features/Feature2/Feature2.cs create mode 100644 Examples/Slang.Console/i18n/feature2_en-US.i18n.yaml create mode 100644 Examples/Slang.Console/i18n/feature2_ru-RU.i18n.yaml create mode 100644 Slang.Tests/Integration/Main/YamlTests.cs create mode 100644 Slang.Tests/Integration/Resources/yaml_de.yaml create mode 100644 Slang.Tests/Integration/Resources/yaml_en.yaml diff --git a/Examples/Slang.Console/Features/Feature2/Feature2.cs b/Examples/Slang.Console/Features/Feature2/Feature2.cs new file mode 100644 index 0000000..fddf0d8 --- /dev/null +++ b/Examples/Slang.Console/Features/Feature2/Feature2.cs @@ -0,0 +1,4 @@ +namespace Slang.Console.Features.Feature2; + +[Translations(InputFileName = "feature2")] +internal partial class Feature2; \ No newline at end of file diff --git a/Examples/Slang.Console/Program.cs b/Examples/Slang.Console/Program.cs index c0366a4..75b1bdd 100644 --- a/Examples/Slang.Console/Program.cs +++ b/Examples/Slang.Console/Program.cs @@ -1,5 +1,6 @@ using Slang.Console; using Slang.Console.Features.Feature1; +using Slang.Console.Features.Feature2; foreach (var culture in Feature1.SupportedCultures) { @@ -48,4 +49,15 @@ void ShowLocales() Console.WriteLine(formattingBloc.TimeSpanExample(TimeSpan.FromMinutes(13))); Console.WriteLine(formattingBloc.ObjectExample2("Hello World!")); Console.WriteLine(formattingBloc.FloatExample(123.31414f)); + + var formattingBloc2 = Feature2.Instance.Root.Formatting; + + string price2 = formattingBloc2.DecimalExample(12.23123M); + + Console.WriteLine(price2); + Console.WriteLine(formattingBloc2.LongExample(124214)); + Console.WriteLine(formattingBloc2.IntExample(123)); + Console.WriteLine(formattingBloc2.TimeSpanExample(TimeSpan.FromMinutes(13))); + Console.WriteLine(formattingBloc2.ObjectExample2("Hello World!")); + Console.WriteLine(formattingBloc2.FloatExample(123.31414f)); } \ No newline at end of file diff --git a/Examples/Slang.Console/Slang.Console.csproj b/Examples/Slang.Console/Slang.Console.csproj index 7a3a64b..783263e 100644 --- a/Examples/Slang.Console/Slang.Console.csproj +++ b/Examples/Slang.Console/Slang.Console.csproj @@ -7,6 +7,7 @@ + diff --git a/Examples/Slang.Console/i18n/feature2_en-US.i18n.yaml b/Examples/Slang.Console/i18n/feature2_en-US.i18n.yaml new file mode 100644 index 0000000..e3b157b --- /dev/null +++ b/Examples/Slang.Console/i18n/feature2_en-US.i18n.yaml @@ -0,0 +1,82 @@ +formatting: + doubleExample: 'Double formatting: {value}' + '@doubleExample': + placeholders: + value: + type: double + format: F2 + doubleExample2: 'Double formatting: {value:double}' + '@doubleExample2': + placeholders: + value: + format: F3 + objectExample2: 'Object formatting: {value}' + '@objectExample2': + placeholders: + value: + format: Qwerty {0} qwerty + intExample: 'Int formatting: {value}' + '@intExample': + placeholders: + value: + type: int + format: X + longExample: 'Long formatting: {value}' + '@longExample': + placeholders: + value: + type: long + format: N + decimalExample: 'Decimal formatting: {value}' + '@decimalExample': + placeholders: + value: + type: decimal + format: C2 + floatExample: 'Float formatting: {value}' + '@floatExample': + placeholders: + value: + type: float + format: F2 + dateExample: Date {date} + '@dateExample': + placeholders: + date: + type: DateTime + format: dd MMMM HH:mm + dateOnlyExample: Date only {date} + '@dateOnlyExample': + placeholders: + date: + type: DateOnly + format: dd MMMM + timeOnlyExample: Time only {time} + '@timeOnlyExample': + placeholders: + time: + type: TimeOnly + format: HH:mm + timeSpanExample: Timespan only {time} + '@timeSpanExample': + placeholders: + time: + type: TimeSpan + format: t +'@formatting': + description: Formatting bloc comment +screen: + data: Date {date} + '@data': + description: Title of the day in the schedule of visits + placeholders: + date: + type: DateOnly + format: dd MMMM + test_param: 'param: {param:double}' +profile_rating: 'My rating: {rating}' +'@profile_rating': + placeholders: + rating: + type: double + format: F2 diff --git a/Examples/Slang.Console/i18n/feature2_ru-RU.i18n.yaml b/Examples/Slang.Console/i18n/feature2_ru-RU.i18n.yaml new file mode 100644 index 0000000..f84af30 --- /dev/null +++ b/Examples/Slang.Console/i18n/feature2_ru-RU.i18n.yaml @@ -0,0 +1,82 @@ +formatting: + doubleExample: 'Double formatting: {value}' + '@doubleExample': + placeholders: + value: + type: double + format: F2 + doubleExample2: 'Double formatting: {value:double}' + '@doubleExample2': + placeholders: + value: + format: F3 + objectExample2: 'Object formatting: {value}' + '@objectExample2': + placeholders: + value: + format: Qwerty {0} qwerty + intExample: 'Int formatting: {value}' + '@intExample': + placeholders: + value: + type: int + format: X + longExample: 'Long formatting: {value}' + '@longExample': + placeholders: + value: + type: long + format: N + decimalExample: 'Decimal formatting: {value}' + '@decimalExample': + placeholders: + value: + type: decimal + format: C2 + floatExample: 'Float formatting: {value}' + '@floatExample': + placeholders: + value: + type: float + format: F2 + dateExample: Date {date} + '@dateExample': + placeholders: + date: + type: DateTime + format: dd MMMM HH:mm + dateOnlyExample: Date only {date} + '@dateOnlyExample': + placeholders: + date: + type: DateOnly + format: dd MMMM + timeOnlyExample: Time only {time} + '@timeOnlyExample': + placeholders: + time: + type: TimeOnly + format: HH:mm + timeSpanExample: Timespan only {time} + '@timeSpanExample': + placeholders: + time: + type: TimeSpan + format: t +'@formatting': + description: Formatting bloc comment +screen: + data: Дата {date} + '@data': + description: Title of the day in the schedule of visits + placeholders: + date: + type: DateOnly + format: dd MMMM + test_param: 'param: {param:double}' +profile_rating: 'Мой рейтинг: {rating}' +'@profile_rating': + placeholders: + rating: + type: double + format: F2 diff --git a/Slang.Generator/TranslateGenerator.cs b/Slang.Generator/TranslateGenerator.cs index 84d0667..f8bab70 100644 --- a/Slang.Generator/TranslateGenerator.cs +++ b/Slang.Generator/TranslateGenerator.cs @@ -56,7 +56,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) generationInfoWithErrors.Select(static (item, _) => item.Value)!; var jsonFiles = context.AdditionalTextsProvider - .Where(file => file.Path.EndsWith(Constants.AdditionalFilePattern)) + .Where(file => + file.Path.EndsWith(Constants.AdditionalFileJsonPattern) || + file.Path.EndsWith(Constants.AdditionalFileYamlPattern)) .Select((file, cancellationToken) => new { FileName = Path.GetFileName(file.Path), @@ -141,5 +143,6 @@ private static TranslationsParam ValidateTargetTypeAndGetInfo(AttributeData attr internal record GlobalConfigDto { - [JsonPropertyName("base_culture")] public string? BaseCulture { get; set; } + [JsonPropertyName("base_culture")] + public string? BaseCulture { get; set; } } \ No newline at end of file diff --git a/Slang.Net/Constants.cs b/Slang.Net/Constants.cs index 6473c23..d22039e 100644 --- a/Slang.Net/Constants.cs +++ b/Slang.Net/Constants.cs @@ -2,5 +2,6 @@ namespace Slang.Shared; public static class Constants { - public const string AdditionalFilePattern = ".i18n.json"; + public const string AdditionalFileJsonPattern = ".i18n.json"; + public const string AdditionalFileYamlPattern = ".i18n.yaml"; } \ No newline at end of file diff --git a/Slang.Net/Data/FilesRepository.cs b/Slang.Net/Data/FilesRepository.cs index 91e2ab4..7e382bf 100644 --- a/Slang.Net/Data/FilesRepository.cs +++ b/Slang.Net/Data/FilesRepository.cs @@ -13,7 +13,8 @@ public record struct SlangFileCollection(List Files); /// The inferred locale of this file (by file name, directory name, or config) public record struct TranslationFile( Func> Read, - CultureInfo Locale + CultureInfo Locale, + string Type ); public static class FilesRepository @@ -54,10 +55,13 @@ public static SlangFileCollection GetFileCollection( return GetTranslationFile(baseCulture, fileName, () => Task.FromResult(content)); } - internal static TranslationFile? GetTranslationFile(CultureInfo baseCulture, string fileName, + internal static TranslationFile? GetTranslationFile( + CultureInfo baseCulture, + string fileName, Func> contentFactory) { string fileNameNoExtension = Path.GetFileNameWithoutExtension(fileName).Split('.').First(); + string fileNameExtension = Path.GetExtension(fileName).Split('.').Last(); var baseFileMatch = Regexes.BaseFileRegex.Match(fileNameNoExtension); @@ -68,7 +72,8 @@ public static SlangFileCollection GetFileCollection( return new TranslationFile( Locale: baseCulture, - Read: contentFactory); + Read: contentFactory, + Type: fileNameExtension); } // secondary files (strings_x) @@ -89,7 +94,8 @@ public static SlangFileCollection GetFileCollection( return new TranslationFile( Locale: locale, - Read: contentFactory); + Read: contentFactory, + Type: fileNameExtension); } return null; diff --git a/Slang.Net/Data/TranslationsDecoder.cs b/Slang.Net/Data/TranslationsDecoder.cs index c937f00..cc92dcd 100644 --- a/Slang.Net/Data/TranslationsDecoder.cs +++ b/Slang.Net/Data/TranslationsDecoder.cs @@ -1,12 +1,16 @@ using System.Text.Json; +using YamlDotNet.Serialization; namespace Slang.Generator.Core.Data; public static class TranslationsDecoder { /// Decodes with the specified file type - public static Dictionary DecodeWithFileType(string json) + public static Dictionary DecodeWithFileType(string content, string type) { - return JsonSerializer.Deserialize>(json)!; + if (type == "yaml") + return new Deserializer().Deserialize>(content)!; + + return JsonSerializer.Deserialize>(content)!; } } \ No newline at end of file diff --git a/Slang.Net/Data/TranslationsRepository.cs b/Slang.Net/Data/TranslationsRepository.cs index d156b6f..0aa11d9 100644 --- a/Slang.Net/Data/TranslationsRepository.cs +++ b/Slang.Net/Data/TranslationsRepository.cs @@ -22,7 +22,7 @@ public static async Task Build(CultureInfo baseCulture, try { - translations = TranslationsDecoder.DecodeWithFileType(content); + translations = TranslationsDecoder.DecodeWithFileType(content, file.Type); } catch (Exception e) { diff --git a/Slang.Net/DiagnosticDescriptors.cs b/Slang.Net/DiagnosticDescriptors.cs index a555978..ced23f5 100644 --- a/Slang.Net/DiagnosticDescriptors.cs +++ b/Slang.Net/DiagnosticDescriptors.cs @@ -4,5 +4,5 @@ internal static class DiagnosticDescriptors { public static readonly DiagnosticDescriptor UnexpectedErrorDescriptor = new("SLANG0001", "Unexpected error during generation", - "Unexpected error occurred during code generation: {0}", "Usage", DiagnosticSeverity.Error, true); + "Unexpected error occurred during code generation: {0} {1}", "Usage", DiagnosticSeverity.Error, true); } \ No newline at end of file diff --git a/Slang.Net/GeneratorContext.cs b/Slang.Net/GeneratorContext.cs index 03992f1..11110a6 100644 --- a/Slang.Net/GeneratorContext.cs +++ b/Slang.Net/GeneratorContext.cs @@ -8,14 +8,14 @@ internal readonly struct GeneratorContext public GeneratorContext( SourceProductionContext sourceProductionContext, Result result, - ImmutableArray jsonFiles, + ImmutableArray langFiles, ImmutableArray projectParams, Compilation compilation) { _sourceProductionContext = sourceProductionContext; Result = result; - JsonFiles = jsonFiles; + LangFiles = langFiles; ProjectParams = projectParams; Compilation = compilation; @@ -51,7 +51,7 @@ public GeneratorContext(GeneratorExecutionContext sourceProductionContext) public Compilation Compilation { get; } public Result Result { get; } - public ImmutableArray JsonFiles { get; } + public ImmutableArray LangFiles { get; } public ImmutableArray ProjectParams { get; } public void ReportDiagnostic(Diagnostic diagnostic) diff --git a/Slang.Net/Slang.Net.csproj b/Slang.Net/Slang.Net.csproj index c379acd..33ec5c4 100644 --- a/Slang.Net/Slang.Net.csproj +++ b/Slang.Net/Slang.Net.csproj @@ -27,6 +27,7 @@ + @@ -80,4 +81,7 @@ + + + diff --git a/Slang.Net/SystemTextJsonRawProvider.cs b/Slang.Net/SystemTextJsonRawProvider.cs index c9ee7e0..50d59c8 100644 --- a/Slang.Net/SystemTextJsonRawProvider.cs +++ b/Slang.Net/SystemTextJsonRawProvider.cs @@ -50,6 +50,12 @@ public bool TryGetArray(object? input, out IEnumerable value) foreach (var property in jsonElement.EnumerateObject()) dict.Add(property.Name, property.Value); + break; + case Dictionary dictionary: + dict = []; + foreach (var property in dictionary) + dict.Add((string)property.Key, property.Value); + break; default: return null; diff --git a/Slang.Net/TranslationsGenerator.Roslyn4.cs b/Slang.Net/TranslationsGenerator.Roslyn4.cs index 1365d43..70d860a 100644 --- a/Slang.Net/TranslationsGenerator.Roslyn4.cs +++ b/Slang.Net/TranslationsGenerator.Roslyn4.cs @@ -17,7 +17,7 @@ public record struct ProjectParam( string? BaseCulture ); - public record struct JsonFile( + public record struct LangFile( string FileName, string? Content ); @@ -67,10 +67,12 @@ public void Initialize(IncrementalGeneratorInitializationContext ctx) return new Result(info, hierarchy); }); - var jsonFilesProvider = + var langFilesProvider = ctx.AdditionalTextsProvider - .Where(file => file.Path.EndsWith(Constants.AdditionalFilePattern)) - .Select((file, cancellationToken) => new JsonFile + .Where(file => + file.Path.EndsWith(Constants.AdditionalFileJsonPattern) || + file.Path.EndsWith(Constants.AdditionalFileYamlPattern)) + .Select((file, cancellationToken) => new LangFile ( FileName: Path.GetFileName(file.Path), Content: file.GetText(cancellationToken)?.ToString() @@ -95,7 +97,7 @@ public void Initialize(IncrementalGeneratorInitializationContext ctx) var input = attributedClasses .Combine(configFileProvider.Collect()) - .Combine(jsonFilesProvider.Collect()) + .Combine(langFilesProvider.Collect()) .Combine(ctx.CompilationProvider); ctx.RegisterSourceOutput( @@ -103,13 +105,13 @@ public void Initialize(IncrementalGeneratorInitializationContext ctx) (productionContext, data) => { var compilation = data.Right; - var jsonFiles = data.Left.Right; + var langFiles = data.Left.Right; var projectParams = data.Left.Left.Right; var syntax = data.Left.Left.Left; Execute(new GeneratorContext(productionContext, syntax, - jsonFiles, + langFiles, projectParams, compilation )); @@ -132,5 +134,6 @@ public void Initialize(IncrementalGeneratorInitializationContext ctx) internal record GlobalConfigDto { - [JsonPropertyName("base_culture")] public string? BaseCulture { get; set; } + [JsonPropertyName("base_culture")] + public string? BaseCulture { get; set; } } \ No newline at end of file diff --git a/Slang.Net/TranslationsGenerator.cs b/Slang.Net/TranslationsGenerator.cs index 6751941..8151742 100644 --- a/Slang.Net/TranslationsGenerator.cs +++ b/Slang.Net/TranslationsGenerator.cs @@ -24,7 +24,7 @@ private void Execute(GeneratorContext context) string className = hierarchy.MetadataName; string namespaceName = hierarchy.Namespace; - if (context.JsonFiles.Length < 1) + if (context.LangFiles.Length < 1) return; if (string.IsNullOrEmpty(info?.InputFileName)) @@ -53,17 +53,17 @@ private void Execute(GeneratorContext context) ); // - var paths = context.JsonFiles + var paths = context.LangFiles .Where(file => file.FileName.StartsWith(config.InputFileName)); var fileCollection = FilesRepository.GetFileCollection( config.BaseLocale, allFiles: paths.Select(file => (file.FileName, file.Content!)) ); - - // foreach (var jsonFile in context.JsonFiles) + // var _i = 0; + // foreach (var jsonFile in context.LangFiles) // { - // context.AddSource($"Translations{_i++}.g.cs", + // context.AddSource($"Translations{_i++}.g.cs", //{error}|{baseLocale}|{jsonFile.FileName} // error + baseLocale + " | " + jsonFile.FileName); // } @@ -72,7 +72,7 @@ private void Execute(GeneratorContext context) catch (Exception) { // context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.UnexpectedErrorDescriptor, Location.None, - // e.ToString().Replace(Environment.NewLine, " "))); + // e.ToString().Replace("\n", " "))); } } diff --git a/Slang.Runner/Generator.cs b/Slang.Runner/Generator.cs index ff49a4d..29b40cc 100644 --- a/Slang.Runner/Generator.cs +++ b/Slang.Runner/Generator.cs @@ -9,6 +9,7 @@ internal class Generator(RawConfig config, string filesDirectory) public async Task Generate() { var paths = Directory.GetFiles(filesDirectory, "*.i18n.json") + .Concat(Directory.GetFiles(filesDirectory, "*.i18n.yaml")) .Where(file => Path.GetFileName(file).StartsWith(config.InputFileName)); var files = paths diff --git a/Slang.Runner/Program.cs b/Slang.Runner/Program.cs index e507dc4..44b3b7c 100644 --- a/Slang.Runner/Program.cs +++ b/Slang.Runner/Program.cs @@ -12,7 +12,7 @@ var config = Test.GetConfig(); -const string sourceFilesDirectory = "/Users/egorozh/RiderProjects/Slang.NET/Examples/Slang.Console/i18n"; +const string sourceFilesDirectory = "../../../../Examples/Slang.Console/i18n"; Generator builder = new(config, sourceFilesDirectory); @@ -28,6 +28,16 @@ await builder2.Generate(); +var config3 = ConfigRepository.Create( + inputFileName: "feature2", + @namespace: "Slang.Console.MyNamespace", + className: "Feature2", + baseLocale: "ru-RU"); + +Generator builder3 = new(config3, sourceFilesDirectory); + +await builder3.Generate(); + Console.WriteLine("End"); #else diff --git a/Slang.Runner/Slang.Runner.csproj b/Slang.Runner/Slang.Runner.csproj index 1d4072c..03d233a 100644 --- a/Slang.Runner/Slang.Runner.csproj +++ b/Slang.Runner/Slang.Runner.csproj @@ -6,11 +6,11 @@ - + - + - + diff --git a/Slang.Shared/Constants.cs b/Slang.Shared/Constants.cs index 6473c23..d22039e 100644 --- a/Slang.Shared/Constants.cs +++ b/Slang.Shared/Constants.cs @@ -2,5 +2,6 @@ namespace Slang.Shared; public static class Constants { - public const string AdditionalFilePattern = ".i18n.json"; + public const string AdditionalFileJsonPattern = ".i18n.json"; + public const string AdditionalFileYamlPattern = ".i18n.yaml"; } \ No newline at end of file diff --git a/Slang.Tests/Helpers/TextNodeBuilder.cs b/Slang.Tests/Helpers/TextNodeBuilder.cs index c032c68..a67e9fc 100644 --- a/Slang.Tests/Helpers/TextNodeBuilder.cs +++ b/Slang.Tests/Helpers/TextNodeBuilder.cs @@ -18,7 +18,7 @@ public static StringTextNode TextNode( KeyCase: CaseStyle.Pascal, KeyMapCase: CaseStyle.Camel, ParamCase: paramCase, - PluralAuto: Slang.Generator.Core.Entities.PluralAuto.Off, + PluralAutoEntity: PluralAutoEntity.Off, PluralParameter: "n"), raw, modifiers: new Dictionary() diff --git a/Slang.Tests/Integration/Main/CompilationTests.cs b/Slang.Tests/Integration/Main/CompilationTests.cs index 1c1d4b1..1f2c5b7 100644 --- a/Slang.Tests/Integration/Main/CompilationTests.cs +++ b/Slang.Tests/Integration/Main/CompilationTests.cs @@ -28,7 +28,7 @@ private static void ExpectSourceCode(SyntaxTree[] syntaxTrees) .Cast() .ToList(); - var assembly = typeof(PluralResolver).Assembly; + var assembly = typeof(PluralResolvers).Assembly; references.Add(MetadataReference.CreateFromFile(assembly.Location)); diff --git a/Slang.Tests/Integration/Main/JsonTests.cs b/Slang.Tests/Integration/Main/JsonTests.cs index 183442e..d2232b9 100644 --- a/Slang.Tests/Integration/Main/JsonTests.cs +++ b/Slang.Tests/Integration/Main/JsonTests.cs @@ -37,8 +37,8 @@ public void Json() ), new TranslationComposition { - {en, TranslationsDecoder.DecodeWithFileType(_enInput)}, - {de, TranslationsDecoder.DecodeWithFileType(_deInput)} + {en, TranslationsDecoder.DecodeWithFileType(_enInput, "json")}, + {de, TranslationsDecoder.DecodeWithFileType(_deInput, "json")} }, new DateTime(2024, 1, 1, 12, 0, 0) ); diff --git a/Slang.Tests/Integration/Main/YamlTests.cs b/Slang.Tests/Integration/Main/YamlTests.cs new file mode 100644 index 0000000..0dfcae6 --- /dev/null +++ b/Slang.Tests/Integration/Main/YamlTests.cs @@ -0,0 +1,53 @@ +using System.Globalization; +using Slang.Generator.Core; +using Slang.Generator.Core.Data; + +namespace Slang.Tests.Integration.Main; + +public class YamlTests +{ + private string _enInput; + private string _deInput; + private string _expectedOutputHeader; + private string _expectedOutputEn; + private string _expectedOutputDe; + + [SetUp] + public void Setup() + { + _enInput = EmbeddedLoader.LoadResource("Slang.Tests.Integration.Resources.yaml_en.yaml"); + _deInput = EmbeddedLoader.LoadResource("Slang.Tests.Integration.Resources.yaml_de.yaml"); + _expectedOutputHeader = EmbeddedLoader.LoadResource("Slang.Tests.Integration.Resources._expected_header.output"); + _expectedOutputEn = EmbeddedLoader.LoadResource("Slang.Tests.Integration.Resources._expected_en.output"); + _expectedOutputDe = EmbeddedLoader.LoadResource("Slang.Tests.Integration.Resources._expected_de.output"); + } + + [Test] + public void Yaml() + { + CultureInfo en = new("en"); + CultureInfo de = new("de"); + + var result = GeneratorFacade.Generate( + rawConfig: ConfigRepository.Create( + inputFileName: "yaml", + @namespace: "Slang.Tests", + className: "TestLocales" + ), + new TranslationComposition + { + { en, TranslationsDecoder.DecodeWithFileType(_enInput, "yaml") }, + { de, TranslationsDecoder.DecodeWithFileType(_deInput, "yaml") } + }, + new DateTime(2024, 1, 1, 12, 0, 0) + ); + + Console.Write(result.Translations[en]); + Assert.Multiple(() => + { + Assert.That(result.Header, Is.EqualTo(_expectedOutputHeader)); + Assert.That(result.Translations[en], Is.EqualTo(_expectedOutputEn)); + Assert.That(result.Translations[de], Is.EqualTo(_expectedOutputDe)); + }); + } +} \ No newline at end of file diff --git a/Slang.Tests/Integration/Resources/yaml_de.yaml b/Slang.Tests/Integration/Resources/yaml_de.yaml new file mode 100644 index 0000000..184b4ca --- /dev/null +++ b/Slang.Tests/Integration/Resources/yaml_de.yaml @@ -0,0 +1,112 @@ +formatting: + doubleExample: 'Double formatting: {value}' + '@doubleExample': + placeholders: + value: + type: double + format: F2 + doubleExample2: 'Double formatting: {value:double}' + '@doubleExample2': + placeholders: + value: + format: F3 + objectExample2: 'Object formatting: {value}' + '@objectExample2': + placeholders: + value: + format: Qwerty {0} qwerty + intExample: 'Int formatting: {value}' + '@intExample': + placeholders: + value: + type: int + format: X + longExample: 'Long formatting: {value}' + '@longExample': + placeholders: + value: + type: long + format: N + decimalExample: 'Decimal formatting: {value}' + '@decimalExample': + placeholders: + value: + type: decimal + format: C2 + floatExample: 'Float formatting: {value}' + '@floatExample': + placeholders: + value: + type: float + format: F2 + dateExample: Date {date} + '@dateExample': + placeholders: + date: + type: DateTime + format: dd MMMM HH:mm + dateOnlyExample: Date only {date} + '@dateOnlyExample': + placeholders: + date: + type: DateOnly + format: dd MMMM + timeOnlyExample: Time only {time} + '@timeOnlyExample': + placeholders: + time: + type: TimeOnly + format: HH:mm + timeSpanExample: Timespan only {time} + '@timeSpanExample': + placeholders: + time: + type: TimeSpan + format: t +'@formatting': + description: Formatting bloc comment +onboarding: + welcome: Willkommen {fullName} + '@welcome': >- + Willkommen + + qwerty + welcomeAlias: '@:onboarding.welcome' + welcomeOnlyParam: '{firstName}' + bye: Tschüss {firstName} + '@bye': Bye text + pages: + - title: Erste Seite + content: Erster Seiteninhalt + - title: Zweite Seite + modifierPages: + - title: Erste Modifier Seite + content: Erster Seiteninhalt + - title: Zweite Modifier Seite + greet: + male: Hallo Herr {lastName} und @:onboarding.welcome + female: Hallo Frau {lastName} und @:onboarding.bye + greet2: + male: Hallo Herr + female: Hallo Frau + welcomeLinkedPlural: Hallo @:group.users + welcomeFullLink: Ultimative @:onboarding.welcomeLinkedPlural +group: + users: + zero: Keine Nutzer und @:onboarding.welcome + one: Ein Nutzer + other: '{n} Nutzer und @:onboarding.bye' +end: + stringPages: + - 1. Seite + - 2. Seite + pages: + - unknown: >- + Unbekannter + + Fehler + - with space: Ein Fehler + with second space: Ein 2. Fehler +advancedPlural(param=count): + one: Eins + other: Andere {count}, @:group.users diff --git a/Slang.Tests/Integration/Resources/yaml_en.yaml b/Slang.Tests/Integration/Resources/yaml_en.yaml new file mode 100644 index 0000000..dd84700 --- /dev/null +++ b/Slang.Tests/Integration/Resources/yaml_en.yaml @@ -0,0 +1,114 @@ +formatting: + doubleExample: 'Double formatting: {value}' + '@doubleExample': + placeholders: + value: + type: double + format: F2 + doubleExample2: 'Double formatting: {value:double}' + '@doubleExample2': + placeholders: + value: + format: F3 + objectExample2: 'Object formatting: {value}' + '@objectExample2': + placeholders: + value: + format: Qwerty {0} qwerty + intExample: 'Int formatting: {value}' + '@intExample': + placeholders: + value: + type: int + format: X + longExample: 'Long formatting: {value}' + '@longExample': + placeholders: + value: + type: long + format: N + decimalExample: 'Decimal formatting: {value}' + '@decimalExample': + placeholders: + value: + type: decimal + format: C2 + floatExample: 'Float formatting: {value}' + '@floatExample': + placeholders: + value: + type: float + format: F2 + dateExample: Date {date} + '@dateExample': + placeholders: + date: + type: DateTime + format: dd MMMM HH:mm + dateOnlyExample: Date only {date} + '@dateOnlyExample': + placeholders: + date: + type: DateOnly + format: dd MMMM + timeOnlyExample: Time only {time} + '@timeOnlyExample': + placeholders: + time: + type: TimeOnly + format: HH:mm + timeSpanExample: Timespan only {time} + '@timeSpanExample': + placeholders: + time: + type: TimeSpan + format: t +'@formatting': + description: Formatting bloc comment +onboarding: + welcome: Welcome {fullName} + '@welcome': >- + Willkommen + + qwerty + welcomeAlias: '@:onboarding.welcome' + welcomeOnlyParam: '{firstName}' + bye: Bye {firstName} + '@bye': + this should be ignored: ignored + description: Bye text + pages: + - title: First Page + content: First Page Content + - title: Second Page + modifierPages: + - title: First Modifier Page + content: First Page Content + - title: Second Modifier Page + greet: + male: Hello Mr {lastName} and @:onboarding.welcome + female: Hello Ms {lastName} and @:onboarding.bye + greet2: + male: Hello Mr + female: Hello Ms + welcomeLinkedPlural: Hello @:group.users + welcomeFullLink: Ultimate @:onboarding.welcomeLinkedPlural +group: + users: + zero: No Users and @:onboarding.welcome + one: One User + other: '{n} Users and @:onboarding.bye' +end: + stringPages: + - 1st Page + - 2nd Page + pages: + - unknown: >- + Unknown + + Error + - with space: An Error + with second space: An 2nd Error +advancedPlural(param=count): + one: One + other: Other {count}, @:group.users \ No newline at end of file diff --git a/Slang.Tests/Slang.Tests.csproj b/Slang.Tests/Slang.Tests.csproj index 99a8483..e68fca2 100644 --- a/Slang.Tests/Slang.Tests.csproj +++ b/Slang.Tests/Slang.Tests.csproj @@ -8,7 +8,7 @@ - + @@ -17,8 +17,8 @@ - - + + diff --git a/Utilities/Gpt/Slang.Gpt/Domain/SlangGptTranslator.cs b/Utilities/Gpt/Slang.Gpt/Domain/SlangGptTranslator.cs index 78eb21a..a48ef42 100644 --- a/Utilities/Gpt/Slang.Gpt/Domain/SlangGptTranslator.cs +++ b/Utilities/Gpt/Slang.Gpt/Domain/SlangGptTranslator.cs @@ -45,7 +45,7 @@ int promptCount string targetPath = Path.Combine( outDir, - $"{file.Namespace}_{targetLocale}{Constants.AdditionalFilePattern}" + $"{file.Namespace}_{targetLocale}{Constants.AdditionalFileJsonPattern}" ); foreach (var destFile in files) diff --git a/Utilities/Slang.Utilities.Core/Translate/AdditionalFilesRepository.cs b/Utilities/Slang.Utilities.Core/Translate/AdditionalFilesRepository.cs index c815f70..8b09fbb 100644 --- a/Utilities/Slang.Utilities.Core/Translate/AdditionalFilesRepository.cs +++ b/Utilities/Slang.Utilities.Core/Translate/AdditionalFilesRepository.cs @@ -45,7 +45,7 @@ private static IEnumerable GetFiles(List additionalFiles, st { var fileInfo = new FileInfo(file); - if (fileInfo.Exists && fileInfo.Name.EndsWith(Constants.AdditionalFilePattern)) + if (fileInfo.Exists && fileInfo.Name.EndsWith(Constants.AdditionalFileJsonPattern)) yield return fileInfo; } } @@ -55,7 +55,7 @@ private static IEnumerable GetFiles(List additionalFiles, st var fileInfo = new FileInfo(filePath); - if (fileInfo.Exists && fileInfo.Name.EndsWith(Constants.AdditionalFilePattern)) + if (fileInfo.Exists && fileInfo.Name.EndsWith(Constants.AdditionalFileJsonPattern)) yield return fileInfo; } } diff --git a/Utilities/Slang.Utilities.Core/Translate/FilesRepository.cs b/Utilities/Slang.Utilities.Core/Translate/FilesRepository.cs index 96ff18b..cfbe6c9 100644 --- a/Utilities/Slang.Utilities.Core/Translate/FilesRepository.cs +++ b/Utilities/Slang.Utilities.Core/Translate/FilesRepository.cs @@ -36,7 +36,7 @@ public static SlangFileCollection GetFileCollection(CultureInfo baseCulture, IEn // could also be a non-base locale when directory name is a locale return new TranslationFile( Locale: baseCulture, - Namespace: fileName.Replace(Constants.AdditionalFilePattern, ""), + Namespace: fileName.Replace(Constants.AdditionalFileJsonPattern, ""), FileName: fileName, FilePath: filePath, Read: contentFactory); From 3e1de3e4aee2e972329c1e866803f06df9cafa58 Mon Sep 17 00:00:00 2001 From: JaJa Date: Fri, 5 Dec 2025 22:39:31 +0300 Subject: [PATCH 2/3] bump version fix ref include, add net10 --- Directory.Build.props | 2 +- Examples/Directory.Build.props | 2 +- Examples/Slang.Console/Program.cs | 4 +- Examples/Slang.Console/Slang.Console.csproj | 6 +- .../Slang.Desktop.Avalonia.csproj | 2 +- Examples/Slang.WebApi/Slang.WebApi.csproj | 2 +- NuGet.config | 7 + Slang.Net/Data/TranslationsDecoder.cs | 6 +- Slang.Net/Slang.Common.props | 2 +- Slang.Net/Slang.Net.csproj | 20 ++- Slang.Net/TranslationsGenerator.cs | 2 +- Slang.sln | 149 ------------------ Slang.slnx | 29 ++-- Utilities/CLI/Slang.CLI/Slang.CLI.csproj | 2 +- .../Slang.Desktop/Slang.Desktop.csproj | 2 +- 15 files changed, 57 insertions(+), 180 deletions(-) create mode 100644 NuGet.config delete mode 100644 Slang.sln diff --git a/Directory.Build.props b/Directory.Build.props index c2cdd84..6ffc8e3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - net8.0;net9.0; + net8.0;net9.0;net10.0; enable enable latest diff --git a/Examples/Directory.Build.props b/Examples/Directory.Build.props index c2cdd84..6ffc8e3 100644 --- a/Examples/Directory.Build.props +++ b/Examples/Directory.Build.props @@ -1,7 +1,7 @@ - net8.0;net9.0; + net8.0;net9.0;net10.0; enable enable latest diff --git a/Examples/Slang.Console/Program.cs b/Examples/Slang.Console/Program.cs index 75b1bdd..bcd5ae1 100644 --- a/Examples/Slang.Console/Program.cs +++ b/Examples/Slang.Console/Program.cs @@ -51,9 +51,9 @@ void ShowLocales() Console.WriteLine(formattingBloc.FloatExample(123.31414f)); var formattingBloc2 = Feature2.Instance.Root.Formatting; - + string price2 = formattingBloc2.DecimalExample(12.23123M); - + Console.WriteLine(price2); Console.WriteLine(formattingBloc2.LongExample(124214)); Console.WriteLine(formattingBloc2.IntExample(123)); diff --git a/Examples/Slang.Console/Slang.Console.csproj b/Examples/Slang.Console/Slang.Console.csproj index 783263e..b4d3415 100644 --- a/Examples/Slang.Console/Slang.Console.csproj +++ b/Examples/Slang.Console/Slang.Console.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/Examples/Slang.Desktop.Avalonia/Slang.Desktop.Avalonia.csproj b/Examples/Slang.Desktop.Avalonia/Slang.Desktop.Avalonia.csproj index d981b11..f17be96 100644 --- a/Examples/Slang.Desktop.Avalonia/Slang.Desktop.Avalonia.csproj +++ b/Examples/Slang.Desktop.Avalonia/Slang.Desktop.Avalonia.csproj @@ -20,7 +20,7 @@ - + diff --git a/Examples/Slang.WebApi/Slang.WebApi.csproj b/Examples/Slang.WebApi/Slang.WebApi.csproj index 571eb69..1f162fd 100644 --- a/Examples/Slang.WebApi/Slang.WebApi.csproj +++ b/Examples/Slang.WebApi/Slang.WebApi.csproj @@ -1,7 +1,7 @@ - + diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 0000000..a872e5c --- /dev/null +++ b/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Slang.Net/Data/TranslationsDecoder.cs b/Slang.Net/Data/TranslationsDecoder.cs index cc92dcd..f8d8f65 100644 --- a/Slang.Net/Data/TranslationsDecoder.cs +++ b/Slang.Net/Data/TranslationsDecoder.cs @@ -1,5 +1,6 @@ using System.Text.Json; using YamlDotNet.Serialization; +using YamlDotNet.System.Text.Json; namespace Slang.Generator.Core.Data; @@ -9,7 +10,10 @@ public static class TranslationsDecoder public static Dictionary DecodeWithFileType(string content, string type) { if (type == "yaml") - return new Deserializer().Deserialize>(content)!; + return new DeserializerBuilder() + .AddSystemTextJson() + .Build() + .Deserialize>(content)!; return JsonSerializer.Deserialize>(content)!; } diff --git a/Slang.Net/Slang.Common.props b/Slang.Net/Slang.Common.props index 756b0cb..fbfdd2d 100644 --- a/Slang.Net/Slang.Common.props +++ b/Slang.Net/Slang.Common.props @@ -4,7 +4,7 @@ netstandard2.0; preview enable - 9.7.0 + 10.0.0 $(ReleaseVersion) Slang diff --git a/Slang.Net/Slang.Net.csproj b/Slang.Net/Slang.Net.csproj index 33ec5c4..52a8f3f 100644 --- a/Slang.Net/Slang.Net.csproj +++ b/Slang.Net/Slang.Net.csproj @@ -26,8 +26,9 @@ - - + + + @@ -46,7 +47,6 @@ - @@ -78,6 +78,20 @@ + + + + $(GetTargetPathDependsOn);GetDependencyTargetPaths + + + + + + + + + diff --git a/Slang.Net/TranslationsGenerator.cs b/Slang.Net/TranslationsGenerator.cs index 8151742..6e3016f 100644 --- a/Slang.Net/TranslationsGenerator.cs +++ b/Slang.Net/TranslationsGenerator.cs @@ -72,7 +72,7 @@ private void Execute(GeneratorContext context) catch (Exception) { // context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.UnexpectedErrorDescriptor, Location.None, - // e.ToString().Replace("\n", " "))); + // e.ToString().Replace("\n", " "),e.StackTrace)); } } diff --git a/Slang.sln b/Slang.sln deleted file mode 100644 index 80a8234..0000000 --- a/Slang.sln +++ /dev/null @@ -1,149 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Generator.Core", "Slang.Generator.Core\Slang.Generator.Core.csproj", "{5809F682-EC51-9811-A512-795ABBBF9E21}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Generator", "Slang.Generator\Slang.Generator.csproj", "{3A4B646D-0604-AED8-1642-23D08637F353}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Runner", "Slang.Runner\Slang.Runner.csproj", "{2F8B8BCC-5F67-0537-0459-735D1781C74A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Shared", "Slang.Shared\Slang.Shared.csproj", "{91641927-147B-FB23-E498-CBFFDCBB1D29}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Tests", "Slang.Tests\Slang.Tests.csproj", "{F85C2F00-A8F0-DCAE-40B3-6CAFA2ACEA4B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang", "Slang\Slang.csproj", "{8FC65231-45BC-3846-861E-77693579E5C3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{B36A84DF-456D-A817-6EDD-3EC3E7F6E11F}" - ProjectSection(SolutionItems) = preProject - Examples\README.md = Examples\README.md - Examples\Directory.Build.props = Examples\Directory.Build.props - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Console", "Examples\Slang.Console\Slang.Console.csproj", "{2DF47CF1-EE56-67A3-165E-8CA18ECA133B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Desktop.Avalonia", "Examples\Slang.Desktop.Avalonia\Slang.Desktop.Avalonia.csproj", "{E6C95533-091C-8D05-B178-0B204476DCBA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.WebApi", "Examples\Slang.WebApi\Slang.WebApi.csproj", "{14BF857E-96BF-0A6C-1FD0-7135FBD3C08D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{089100B1-113F-4E66-888A-E83F3999EAFD}" - ProjectSection(SolutionItems) = preProject - README.md = README.md - Directory.Build.props = Directory.Build.props - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{DF572EAA-2BC4-7083-A1C9-E138B4BE775E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Utilities.Core", "Utilities\Slang.Utilities.Core\Slang.Utilities.Core.csproj", "{E4940BF8-6ADC-EE4D-0832-12D4113C5AF3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CLI", "CLI", "{E0560124-CF62-2213-72AD-A15742EDEEE7}" - ProjectSection(SolutionItems) = preProject - Utilities\CLI\README.md = Utilities\CLI\README.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.CLI", "Utilities\CLI\Slang.CLI\Slang.CLI.csproj", "{32D19FC6-82E9-45DF-EC9F-35C79A13C647}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Desktop", "Desktop", "{8A3D9811-1B74-EE40-DAD4-6426832E1BDA}" - ProjectSection(SolutionItems) = preProject - Utilities\Desktop\Slang-desktop-screenshot.jpg = Utilities\Desktop\Slang-desktop-screenshot.jpg - Utilities\Desktop\README.md = Utilities\Desktop\README.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Desktop", "Utilities\Desktop\Slang.Desktop\Slang.Desktop.csproj", "{917AC75E-0BAB-4A86-8871-B4F29EF7B195}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gpt", "Gpt", "{DF967749-A283-A6D4-2779-4325AB6D3FE6}" - ProjectSection(SolutionItems) = preProject - Utilities\Gpt\README.md = Utilities\Gpt\README.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Gpt.Tests", "Utilities\Gpt\Slang.Gpt.Tests\Slang.Gpt.Tests.csproj", "{D432E690-EEFC-17A9-3E3F-1F27876141FF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Gpt", "Utilities\Gpt\Slang.Gpt\Slang.Gpt.csproj", "{FECD01FB-C684-8794-7B60-4D683E64CA82}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Net", "Slang.Net\Slang.Net.csproj", "{8E2C1D79-4E9D-4989-941D-93A0AACA0565}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slang.Net.Attributes", "Slang.Net.Attributes\Slang.Net.Attributes.csproj", "{EA1E51F7-225A-4DEC-B38F-8CF7F81B3470}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5809F682-EC51-9811-A512-795ABBBF9E21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5809F682-EC51-9811-A512-795ABBBF9E21}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5809F682-EC51-9811-A512-795ABBBF9E21}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5809F682-EC51-9811-A512-795ABBBF9E21}.Release|Any CPU.Build.0 = Release|Any CPU - {3A4B646D-0604-AED8-1642-23D08637F353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3A4B646D-0604-AED8-1642-23D08637F353}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3A4B646D-0604-AED8-1642-23D08637F353}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3A4B646D-0604-AED8-1642-23D08637F353}.Release|Any CPU.Build.0 = Release|Any CPU - {2F8B8BCC-5F67-0537-0459-735D1781C74A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F8B8BCC-5F67-0537-0459-735D1781C74A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F8B8BCC-5F67-0537-0459-735D1781C74A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F8B8BCC-5F67-0537-0459-735D1781C74A}.Release|Any CPU.Build.0 = Release|Any CPU - {91641927-147B-FB23-E498-CBFFDCBB1D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {91641927-147B-FB23-E498-CBFFDCBB1D29}.Debug|Any CPU.Build.0 = Debug|Any CPU - {91641927-147B-FB23-E498-CBFFDCBB1D29}.Release|Any CPU.ActiveCfg = Release|Any CPU - {91641927-147B-FB23-E498-CBFFDCBB1D29}.Release|Any CPU.Build.0 = Release|Any CPU - {F85C2F00-A8F0-DCAE-40B3-6CAFA2ACEA4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F85C2F00-A8F0-DCAE-40B3-6CAFA2ACEA4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F85C2F00-A8F0-DCAE-40B3-6CAFA2ACEA4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F85C2F00-A8F0-DCAE-40B3-6CAFA2ACEA4B}.Release|Any CPU.Build.0 = Release|Any CPU - {8FC65231-45BC-3846-861E-77693579E5C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8FC65231-45BC-3846-861E-77693579E5C3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8FC65231-45BC-3846-861E-77693579E5C3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8FC65231-45BC-3846-861E-77693579E5C3}.Release|Any CPU.Build.0 = Release|Any CPU - {2DF47CF1-EE56-67A3-165E-8CA18ECA133B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2DF47CF1-EE56-67A3-165E-8CA18ECA133B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2DF47CF1-EE56-67A3-165E-8CA18ECA133B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2DF47CF1-EE56-67A3-165E-8CA18ECA133B}.Release|Any CPU.Build.0 = Release|Any CPU - {E6C95533-091C-8D05-B178-0B204476DCBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E6C95533-091C-8D05-B178-0B204476DCBA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E6C95533-091C-8D05-B178-0B204476DCBA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E6C95533-091C-8D05-B178-0B204476DCBA}.Release|Any CPU.Build.0 = Release|Any CPU - {14BF857E-96BF-0A6C-1FD0-7135FBD3C08D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {14BF857E-96BF-0A6C-1FD0-7135FBD3C08D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {14BF857E-96BF-0A6C-1FD0-7135FBD3C08D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {14BF857E-96BF-0A6C-1FD0-7135FBD3C08D}.Release|Any CPU.Build.0 = Release|Any CPU - {E4940BF8-6ADC-EE4D-0832-12D4113C5AF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4940BF8-6ADC-EE4D-0832-12D4113C5AF3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E4940BF8-6ADC-EE4D-0832-12D4113C5AF3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E4940BF8-6ADC-EE4D-0832-12D4113C5AF3}.Release|Any CPU.Build.0 = Release|Any CPU - {32D19FC6-82E9-45DF-EC9F-35C79A13C647}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32D19FC6-82E9-45DF-EC9F-35C79A13C647}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32D19FC6-82E9-45DF-EC9F-35C79A13C647}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32D19FC6-82E9-45DF-EC9F-35C79A13C647}.Release|Any CPU.Build.0 = Release|Any CPU - {917AC75E-0BAB-4A86-8871-B4F29EF7B195}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {917AC75E-0BAB-4A86-8871-B4F29EF7B195}.Debug|Any CPU.Build.0 = Debug|Any CPU - {917AC75E-0BAB-4A86-8871-B4F29EF7B195}.Release|Any CPU.ActiveCfg = Release|Any CPU - {917AC75E-0BAB-4A86-8871-B4F29EF7B195}.Release|Any CPU.Build.0 = Release|Any CPU - {D432E690-EEFC-17A9-3E3F-1F27876141FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D432E690-EEFC-17A9-3E3F-1F27876141FF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D432E690-EEFC-17A9-3E3F-1F27876141FF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D432E690-EEFC-17A9-3E3F-1F27876141FF}.Release|Any CPU.Build.0 = Release|Any CPU - {FECD01FB-C684-8794-7B60-4D683E64CA82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FECD01FB-C684-8794-7B60-4D683E64CA82}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FECD01FB-C684-8794-7B60-4D683E64CA82}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FECD01FB-C684-8794-7B60-4D683E64CA82}.Release|Any CPU.Build.0 = Release|Any CPU - {8E2C1D79-4E9D-4989-941D-93A0AACA0565}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8E2C1D79-4E9D-4989-941D-93A0AACA0565}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8E2C1D79-4E9D-4989-941D-93A0AACA0565}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8E2C1D79-4E9D-4989-941D-93A0AACA0565}.Release|Any CPU.Build.0 = Release|Any CPU - {EA1E51F7-225A-4DEC-B38F-8CF7F81B3470}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA1E51F7-225A-4DEC-B38F-8CF7F81B3470}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA1E51F7-225A-4DEC-B38F-8CF7F81B3470}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA1E51F7-225A-4DEC-B38F-8CF7F81B3470}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {2DF47CF1-EE56-67A3-165E-8CA18ECA133B} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F} - {E6C95533-091C-8D05-B178-0B204476DCBA} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F} - {14BF857E-96BF-0A6C-1FD0-7135FBD3C08D} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F} - {E4940BF8-6ADC-EE4D-0832-12D4113C5AF3} = {DF572EAA-2BC4-7083-A1C9-E138B4BE775E} - {E0560124-CF62-2213-72AD-A15742EDEEE7} = {DF572EAA-2BC4-7083-A1C9-E138B4BE775E} - {8A3D9811-1B74-EE40-DAD4-6426832E1BDA} = {DF572EAA-2BC4-7083-A1C9-E138B4BE775E} - {DF967749-A283-A6D4-2779-4325AB6D3FE6} = {DF572EAA-2BC4-7083-A1C9-E138B4BE775E} - {32D19FC6-82E9-45DF-EC9F-35C79A13C647} = {E0560124-CF62-2213-72AD-A15742EDEEE7} - {917AC75E-0BAB-4A86-8871-B4F29EF7B195} = {8A3D9811-1B74-EE40-DAD4-6426832E1BDA} - {D432E690-EEFC-17A9-3E3F-1F27876141FF} = {DF967749-A283-A6D4-2779-4325AB6D3FE6} - {FECD01FB-C684-8794-7B60-4D683E64CA82} = {DF967749-A283-A6D4-2779-4325AB6D3FE6} - EndGlobalSection -EndGlobal diff --git a/Slang.slnx b/Slang.slnx index 759c50c..ceff80d 100644 --- a/Slang.slnx +++ b/Slang.slnx @@ -1,8 +1,8 @@  - - - + + + @@ -11,26 +11,27 @@ - + - + - + - - + + - - - - - - + + + + + + + \ No newline at end of file diff --git a/Utilities/CLI/Slang.CLI/Slang.CLI.csproj b/Utilities/CLI/Slang.CLI/Slang.CLI.csproj index a025e69..0367b9e 100644 --- a/Utilities/CLI/Slang.CLI/Slang.CLI.csproj +++ b/Utilities/CLI/Slang.CLI/Slang.CLI.csproj @@ -20,7 +20,7 @@ - + diff --git a/Utilities/Desktop/Slang.Desktop/Slang.Desktop.csproj b/Utilities/Desktop/Slang.Desktop/Slang.Desktop.csproj index 2983a18..df28af1 100644 --- a/Utilities/Desktop/Slang.Desktop/Slang.Desktop.csproj +++ b/Utilities/Desktop/Slang.Desktop/Slang.Desktop.csproj @@ -20,7 +20,7 @@ - + From ab05b3a0a38d898c31487319895ae4af409ed9fd Mon Sep 17 00:00:00 2001 From: JaJa Date: Sat, 6 Dec 2025 01:27:36 +0300 Subject: [PATCH 3/3] add ability to change placeholder style --- .../Features/Feature3/Feature3.cs | 4 ++ Examples/Slang.Console/Program.cs | 3 + .../i18n/feature3_ru-RU.i18n.yaml | 2 + Slang.Net/Attributes.cs | 4 ++ .../CodeBuilder/TranslationsCodeBuilder.cs | 1 + Slang.Net/Data/ConfigRepository.cs | 11 +++- Slang.Net/Entities/RawConfig.cs | 4 +- Slang.Net/GeneratorFacade.cs | 12 +++- Slang.Net/Nodes/NodesRepository.Parser.cs | 4 +- Slang.Net/Nodes/NodesRepository.cs | 14 +++- Slang.Net/Nodes/Utils/NodeHelpers.cs | 66 +++++++++++-------- Slang.Net/NodesData/NodesDataRepository.cs | 10 ++- Slang.Net/Slang.Common.props | 2 +- Slang.Net/Slang.Net.csproj | 6 ++ Slang.Net/TranslationsGenerator.Roslyn4.cs | 5 +- Slang.Net/TranslationsGenerator.cs | 18 +++-- Slang.Runner/Program.cs | 60 ++++++++++------- Slang.Runner/Slang.Runner.csproj | 1 + Slang.Tests/Helpers/TextNodeBuilder.cs | 4 +- .../Integration/Main/YamlJsonFixTests.cs | 43 ++++++++++++ .../Resources/_expected_en_json.output | 33 ++++++++++ .../Integration/Resources/yaml_en_json.yaml | 2 + .../Unit/Utils/ParseInterpolationTests.cs | 37 +++++++++++ Slang.slnx | 29 ++++---- 24 files changed, 288 insertions(+), 87 deletions(-) create mode 100644 Examples/Slang.Console/Features/Feature3/Feature3.cs create mode 100644 Examples/Slang.Console/i18n/feature3_ru-RU.i18n.yaml create mode 100644 Slang.Tests/Integration/Main/YamlJsonFixTests.cs create mode 100644 Slang.Tests/Integration/Resources/_expected_en_json.output create mode 100644 Slang.Tests/Integration/Resources/yaml_en_json.yaml create mode 100644 Slang.Tests/Unit/Utils/ParseInterpolationTests.cs diff --git a/Examples/Slang.Console/Features/Feature3/Feature3.cs b/Examples/Slang.Console/Features/Feature3/Feature3.cs new file mode 100644 index 0000000..e241cef --- /dev/null +++ b/Examples/Slang.Console/Features/Feature3/Feature3.cs @@ -0,0 +1,4 @@ +namespace Slang.Console.Features.Feature3; + +[Translations(InputFileName = "feature3", StartCharacter = "{{", EndCharacter = "}}")] +internal partial class Feature3; \ No newline at end of file diff --git a/Examples/Slang.Console/Program.cs b/Examples/Slang.Console/Program.cs index bcd5ae1..b552397 100644 --- a/Examples/Slang.Console/Program.cs +++ b/Examples/Slang.Console/Program.cs @@ -1,6 +1,7 @@ using Slang.Console; using Slang.Console.Features.Feature1; using Slang.Console.Features.Feature2; +using Slang.Console.Features.Feature3; foreach (var culture in Feature1.SupportedCultures) { @@ -60,4 +61,6 @@ void ShowLocales() Console.WriteLine(formattingBloc2.TimeSpanExample(TimeSpan.FromMinutes(13))); Console.WriteLine(formattingBloc2.ObjectExample2("Hello World!")); Console.WriteLine(formattingBloc2.FloatExample(123.31414f)); + + Console.WriteLine(Feature3.Instance.Root.SomeExt(3)); } \ No newline at end of file diff --git a/Examples/Slang.Console/i18n/feature3_ru-RU.i18n.yaml b/Examples/Slang.Console/i18n/feature3_ru-RU.i18n.yaml new file mode 100644 index 0000000..0fe9300 --- /dev/null +++ b/Examples/Slang.Console/i18n/feature3_ru-RU.i18n.yaml @@ -0,0 +1,2 @@ +SomeJson: '[{"aaa": "bbb"}]' +SomeExt: 'asd {{some: int}}' \ No newline at end of file diff --git a/Slang.Net/Attributes.cs b/Slang.Net/Attributes.cs index 6a3e4a1..e1c56f0 100644 --- a/Slang.Net/Attributes.cs +++ b/Slang.Net/Attributes.cs @@ -25,6 +25,10 @@ class TranslationsAttribute: Attribute public PluralAuto PluralAuto { get; set; } = PluralAuto.Cardinal; public string? PluralParameter { get; set; } + + public string StartCharacter { get; set; } = "{"; + + public string EndCharacter { get; set; } = "}"; } #if SLANG_ATTRIBUTES_PACKAGE diff --git a/Slang.Net/CodeBuilder/TranslationsCodeBuilder.cs b/Slang.Net/CodeBuilder/TranslationsCodeBuilder.cs index 3a703f6..14e2f74 100644 --- a/Slang.Net/CodeBuilder/TranslationsCodeBuilder.cs +++ b/Slang.Net/CodeBuilder/TranslationsCodeBuilder.cs @@ -12,6 +12,7 @@ public static async Task Generate( RawConfig config, SlangFileCollection fileCollection) { + //Debugger.Launch(); // STEP 2: scan translations var translationMap = await TranslationsRepository.Build(config.BaseLocale, fileCollection: fileCollection); diff --git a/Slang.Net/Data/ConfigRepository.cs b/Slang.Net/Data/ConfigRepository.cs index e295f8c..1d7563b 100644 --- a/Slang.Net/Data/ConfigRepository.cs +++ b/Slang.Net/Data/ConfigRepository.cs @@ -5,13 +5,16 @@ namespace Slang.Generator.Core.Data; public static class ConfigRepository { - public static RawConfig Create(string inputFileName, + public static RawConfig Create( + string inputFileName, string @namespace, string className, string baseLocale = "en", PluralAutoEntity pluralAutoEntity = PluralAutoEntity.Cardinal, string rootPropertyName = "Root", - string pluralParameter = "n") + string pluralParameter = "n", + string startCharacter = "{", + string endCharacter = "}") { return new RawConfig( Namespace: @namespace, @@ -20,7 +23,9 @@ public static RawConfig Create(string inputFileName, InputFileName: inputFileName, PluralAutoEntity: pluralAutoEntity, PluralParameter: pluralParameter, - RootPropertyName: rootPropertyName + RootPropertyName: rootPropertyName, + StartCharacter: startCharacter, + EndCharacter: endCharacter ); } } \ No newline at end of file diff --git a/Slang.Net/Entities/RawConfig.cs b/Slang.Net/Entities/RawConfig.cs index 5f10427..dab4570 100644 --- a/Slang.Net/Entities/RawConfig.cs +++ b/Slang.Net/Entities/RawConfig.cs @@ -12,5 +12,7 @@ public record RawConfig( string ClassName, PluralAutoEntity PluralAutoEntity, string PluralParameter, - string RootPropertyName + string RootPropertyName, + string StartCharacter, + string EndCharacter ); \ No newline at end of file diff --git a/Slang.Net/GeneratorFacade.cs b/Slang.Net/GeneratorFacade.cs index 2f5548c..7664e61 100644 --- a/Slang.Net/GeneratorFacade.cs +++ b/Slang.Net/GeneratorFacade.cs @@ -18,7 +18,9 @@ public static BuildResult Generate( rawConfig.BaseLocale, translationComposition, rawConfig.PluralAutoEntity, - rawConfig.PluralParameter + rawConfig.PluralParameter, + rawConfig.StartCharacter, + rawConfig.EndCharacter ); // generate config @@ -43,7 +45,9 @@ public static void GetNodes( rawConfig.BaseLocale, translationComposition, rawConfig.PluralAutoEntity, - rawConfig.PluralParameter + rawConfig.PluralParameter, + rawConfig.StartCharacter, + rawConfig.EndCharacter ); } @@ -81,7 +85,9 @@ public class BenchmarkGeneratorData( rawConfig.BaseLocale, translationMap, rawConfig.PluralAutoEntity, - rawConfig.PluralParameter + rawConfig.PluralParameter, + rawConfig.StartCharacter, + rawConfig.EndCharacter ); } } \ No newline at end of file diff --git a/Slang.Net/Nodes/NodesRepository.Parser.cs b/Slang.Net/Nodes/NodesRepository.Parser.cs index 9ae70e6..ca243bf 100644 --- a/Slang.Net/Nodes/NodesRepository.Parser.cs +++ b/Slang.Net/Nodes/NodesRepository.Parser.cs @@ -130,7 +130,9 @@ internal static StringTextNode CreateTextNode( (string? parsedContent, var paramTypeMap) = NodeHelpers.ParseInterpolation( raw: shouldEscape ? EscapeContent(value) : value, defaultType: "object", - paramCase: config.ParamCase + paramCase: config.ParamCase, + startCharacter: config.StartCharacter, + endCharacter: config.EndCharacter ); var @params = new HashSet(paramTypeMap.Keys); diff --git a/Slang.Net/Nodes/NodesRepository.cs b/Slang.Net/Nodes/NodesRepository.cs index 7a8d2ff..33d50bc 100644 --- a/Slang.Net/Nodes/NodesRepository.cs +++ b/Slang.Net/Nodes/NodesRepository.cs @@ -14,17 +14,25 @@ internal record BuildModelConfig( CaseStyle? KeyMapCase, CaseStyle? ParamCase, PluralAutoEntity PluralAutoEntity, - string PluralParameter + string PluralParameter, + string StartCharacter, + string EndCharacter ); internal static partial class NodesRepository { - public static BuildModelConfig ToBuildModelConfig(PluralAutoEntity pluralAutoEntity, string pluralParameter) => new( + public static BuildModelConfig ToBuildModelConfig( + PluralAutoEntity pluralAutoEntity, + string pluralParameter, + string startCharacter, + string endCharacter) => new( KeyCase: CaseStyle.Pascal, KeyMapCase: CaseStyle.Camel, ParamCase: CaseStyle.Camel, PluralAutoEntity: pluralAutoEntity, - PluralParameter: pluralParameter + PluralParameter: pluralParameter, + StartCharacter: startCharacter, + EndCharacter: endCharacter ); diff --git a/Slang.Net/Nodes/Utils/NodeHelpers.cs b/Slang.Net/Nodes/Utils/NodeHelpers.cs index 5932cd9..cfffebf 100644 --- a/Slang.Net/Nodes/Utils/NodeHelpers.cs +++ b/Slang.Net/Nodes/Utils/NodeHelpers.cs @@ -10,17 +10,17 @@ public static string DetermineGenericType(IReadOnlyCollection entries) { const string dynamicType = "dynamic"; - if (entries.All(child => child is StringTextNode {ParamTypeMap.Count: 0})) + if (entries.All(child => child is StringTextNode { ParamTypeMap.Count: 0 })) return "string"; if (entries.All(child => child is ListNode)) { - string childGenericType = ((ListNode) entries.First()).GenericType; + string childGenericType = ((ListNode)entries.First()).GenericType; foreach (var node in entries) { var child = (ListNode)node; - + if (childGenericType != child.GenericType) { childGenericType = dynamicType; // default @@ -30,14 +30,14 @@ public static string DetermineGenericType(IReadOnlyCollection entries) return $"List<{childGenericType}>"; // all lists have the same generic type } - if (entries.All(child => child is ObjectNode {IsMap: true})) + if (entries.All(child => child is ObjectNode { IsMap: true })) { - string childGenericType = ((ObjectNode) entries.First()).GenericType; + string childGenericType = ((ObjectNode)entries.First()).GenericType; foreach (var node in entries) { var child = (ObjectNode)node; - + if (childGenericType != child.GenericType) { childGenericType = dynamicType; // default @@ -93,18 +93,24 @@ internal record struct ParseInterpolationResult(string ParsedContent, Dictionary public static ParseInterpolationResult ParseInterpolation( string raw, string defaultType, - CaseStyle? paramCase + CaseStyle? paramCase, + string startCharacter, + string endCharacter ) { Dictionary @params = []; - string parsedContent = ReplaceBracesInterpolation(raw, replacer: match => - { - string rawParam = match.Substring(1, match.Length - 2); - var parsedParam = ParseParam(rawParam: rawParam, defaultType: defaultType, caseStyle: paramCase); - @params[parsedParam.ParamName] = parsedParam.ParamType; - return $"{{{parsedParam.ParamName}}}"; - }); + string parsedContent = ReplaceBracesInterpolation( + raw, + startCharacter: startCharacter, + endCharacter: endCharacter, + replacer: match => + { + string rawParam = match.Substring(startCharacter.Length, match.Length - (startCharacter.Length + endCharacter.Length)); + var parsedParam = ParseParam(rawParam: rawParam, defaultType: defaultType, caseStyle: paramCase); + @params[parsedParam.ParamName] = parsedParam.ParamType; + return $"{{{parsedParam.ParamName}}}"; + }); return new ParseInterpolationResult(parsedContent, @params); } @@ -133,18 +139,22 @@ private static ParseParamResult ParseParam( /// Replaces every {x} with the result of [replacer]. private static string ReplaceBracesInterpolation( - string s, Func replacer + string s, + Func replacer, + string startCharacter, + string endCharacter ) { return ReplaceBetween( input: s, - startCharacter: "{", - endCharacter: "}", + startCharacter: startCharacter, + endCharacter: endCharacter, replacer: replacer ); } - private static string ReplaceBetween(string input, + private static string ReplaceBetween( + string input, string startCharacter, string endCharacter, Func replacer) @@ -169,15 +179,15 @@ private static string ReplaceBetween(string input, { // ignore because of preceding \ int length = startIndex - 1; - if (length > 0) // на случай, если startIndex == 1 - buffer.Append(curr, 0, length); // перегрузка StringBuilder.Append(string, int, int) + if (length > 0) // на случай, если startIndex == 1 + buffer.Append(curr, 0, length); // перегрузка StringBuilder.Append(string, int, int) buffer.Append(startCharacter); if (startIndex + 1 < curr.Length) { int offset = startIndex + startCharacterLength; curr = offset < curr.Length - ? curr.Substring(offset) // начиная с offset до конца строки - : string.Empty; // защитимся от выхода за пределы + ? curr.Substring(offset) // начиная с offset до конца строки + : string.Empty; // защитимся от выхода за пределы continue; } @@ -190,7 +200,7 @@ private static string ReplaceBetween(string input, { // ignore because of preceding @: which indicates an escaped, linked translation buffer.Append(curr.Substring(0, startIndex + 1)); - + if (startIndex + 1 < curr.Length) { curr = curr.Substring(startIndex + startCharacterLength); @@ -203,7 +213,7 @@ private static string ReplaceBetween(string input, if (startIndex != 0) { // add prefix - if (startIndex > 0) // чтобы не бросить ArgumentOutOfRangeException + if (startIndex > 0) // чтобы не бросить ArgumentOutOfRangeException buffer.Append(curr, 0, startIndex); } @@ -211,13 +221,13 @@ private static string ReplaceBetween(string input, if (endIndex == -1) { int count = curr.Length - startIndex; - if (count > 0) // защищаемся от пустого хвоста + if (count > 0) // защищаемся от пустого хвоста buffer.Append(curr, startIndex, count); break; } int length2 = (endIndex + endCharacterLength) - startIndex; - if (length2 > 0) // защита от отрицательной / нулевой длины + if (length2 > 0) // защита от отрицательной / нулевой длины { string slice = curr.Substring(startIndex, length2); buffer.Append(replacer(slice)); @@ -225,8 +235,8 @@ private static string ReplaceBetween(string input, int offset2 = endIndex + endCharacterLength; curr = offset2 < curr.Length - ? curr.Substring(offset2) // вся строка с offset до конца - : string.Empty; + ? curr.Substring(offset2) // вся строка с offset до конца + : string.Empty; } while (!string.IsNullOrEmpty(curr)); return buffer.ToString(); diff --git a/Slang.Net/NodesData/NodesDataRepository.cs b/Slang.Net/NodesData/NodesDataRepository.cs index 0797d85..f121d19 100644 --- a/Slang.Net/NodesData/NodesDataRepository.cs +++ b/Slang.Net/NodesData/NodesDataRepository.cs @@ -25,9 +25,15 @@ public static class NodesDataRepository /// /// After this method call, information about the namespace is lost. /// It will be just a normal parent. - public static List GetNodesData(CultureInfo baseLocale, TranslationComposition composition, PluralAutoEntity pluralAutoEntity, string pluralParameter) + public static List GetNodesData( + CultureInfo baseLocale, + TranslationComposition composition, + PluralAutoEntity pluralAutoEntity, + string pluralParameter, + string startCharacter, + string endCharacter) { - var buildConfig = NodesRepository.ToBuildModelConfig(pluralAutoEntity, pluralParameter); + var buildConfig = NodesRepository.ToBuildModelConfig(pluralAutoEntity, pluralParameter, startCharacter, endCharacter); KeyValuePair>? baseEntry = composition .FirstOrDefault(entry => Equals(entry.Key, baseLocale)); diff --git a/Slang.Net/Slang.Common.props b/Slang.Net/Slang.Common.props index fbfdd2d..0a424c8 100644 --- a/Slang.Net/Slang.Common.props +++ b/Slang.Net/Slang.Common.props @@ -4,7 +4,7 @@ netstandard2.0; preview enable - 10.0.0 + 10.0.1 $(ReleaseVersion) Slang diff --git a/Slang.Net/Slang.Net.csproj b/Slang.Net/Slang.Net.csproj index 52a8f3f..56c6806 100644 --- a/Slang.Net/Slang.Net.csproj +++ b/Slang.Net/Slang.Net.csproj @@ -50,6 +50,12 @@ + + + + + + diff --git a/Slang.Net/TranslationsGenerator.Roslyn4.cs b/Slang.Net/TranslationsGenerator.Roslyn4.cs index 70d860a..f0b5fae 100644 --- a/Slang.Net/TranslationsGenerator.Roslyn4.cs +++ b/Slang.Net/TranslationsGenerator.Roslyn4.cs @@ -10,7 +10,9 @@ public record TranslationsParam( string? InputFileName, PluralAutoEntity? PluralAuto, string? PluralParameter, - string? RootPropertyName + string? RootPropertyName, + string? StartCharacter, + string? EndCharacter ); public record struct ProjectParam( @@ -126,6 +128,7 @@ public void Initialize(IncrementalGeneratorInitializationContext ctx) // }); ctx.RegisterPostInitializationOutput(c => { c.AddSource("Attributes.cs", ReadAttributesFile()); }); + } } } diff --git a/Slang.Net/TranslationsGenerator.cs b/Slang.Net/TranslationsGenerator.cs index 6e3016f..1578c42 100644 --- a/Slang.Net/TranslationsGenerator.cs +++ b/Slang.Net/TranslationsGenerator.cs @@ -34,6 +34,8 @@ private void Execute(GeneratorContext context) string pluralParameter = string.IsNullOrEmpty(info?.PluralParameter) ? "n" : info!.PluralParameter!; string rootPropertyName = string.IsNullOrEmpty(info?.RootPropertyName) ? "Root" : info!.RootPropertyName!; + string startCharacter = string.IsNullOrEmpty(info?.StartCharacter) ? "{" : info!.StartCharacter!; + string endCharacter = string.IsNullOrEmpty(info?.EndCharacter) ? "}" : info!.EndCharacter!; string? baseLocale = string.IsNullOrEmpty(globalConfig.BaseCulture) ? "en" : globalConfig.BaseCulture; @@ -49,7 +51,9 @@ private void Execute(GeneratorContext context) InputFileName: info?.InputFileName!, PluralAutoEntity: PluralAutoEntity.Cardinal, PluralParameter: pluralParameter, - RootPropertyName: rootPropertyName + RootPropertyName: rootPropertyName, + StartCharacter: startCharacter, + EndCharacter: endCharacter ); // @@ -69,10 +73,10 @@ private void Execute(GeneratorContext context) _ = TranslationsCodeBuilder.Generate(context, config, fileCollection); } - catch (Exception) + catch (Exception e) { - // context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.UnexpectedErrorDescriptor, Location.None, - // e.ToString().Replace("\n", " "),e.StackTrace)); + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.UnexpectedErrorDescriptor, Location.None, + e.ToString().Replace("\n", " "), e.StackTrace)); } } @@ -115,12 +119,16 @@ private static TranslationsParam ValidateTargetTypeAndGetInfo(AttributeData attr var pluralAuto = attributeData.GetNamedArgument("PluralAuto"); string? pluralParameter = attributeData.GetNamedArgument("PluralParameter"); string? rootPropertyName = attributeData.GetNamedArgument("RootPropertyName"); + string? startCharacter = attributeData.GetNamedArgument("StartCharacter"); + string? endCharacter = attributeData.GetNamedArgument("EndCharacter"); return new TranslationsParam( InputFileName: inputFileName, PluralAuto: pluralAuto, PluralParameter: pluralParameter, - RootPropertyName: rootPropertyName + RootPropertyName: rootPropertyName, + StartCharacter: startCharacter, + EndCharacter: endCharacter ); } } \ No newline at end of file diff --git a/Slang.Runner/Program.cs b/Slang.Runner/Program.cs index 44b3b7c..c1f78a3 100644 --- a/Slang.Runner/Program.cs +++ b/Slang.Runner/Program.cs @@ -10,33 +10,45 @@ Console.WriteLine("Start"); -var config = Test.GetConfig(); +//var config = Test.GetConfig(); const string sourceFilesDirectory = "../../../../Examples/Slang.Console/i18n"; -Generator builder = new(config, sourceFilesDirectory); - -await builder.Generate(); - -var config2 = ConfigRepository.Create( - inputFileName: "feature1", - @namespace: "Slang.Console.MyNamespace", - className: "Feature1", - baseLocale: "ru-RU"); - -Generator builder2 = new(config2, sourceFilesDirectory); - -await builder2.Generate(); - -var config3 = ConfigRepository.Create( - inputFileName: "feature2", - @namespace: "Slang.Console.MyNamespace", - className: "Feature2", - baseLocale: "ru-RU"); - -Generator builder3 = new(config3, sourceFilesDirectory); - -await builder3.Generate(); +//Generator builder = new(config, sourceFilesDirectory); + +//await builder.Generate(); +// +// var config2 = ConfigRepository.Create( +// inputFileName: "feature1", +// @namespace: "Slang.Console.MyNamespace", +// className: "Feature1", +// baseLocale: "ru-RU"); +// +// Generator builder2 = new(config2, sourceFilesDirectory); +// +// await builder2.Generate(); +// +// var config3 = ConfigRepository.Create( +// inputFileName: "feature2", +// @namespace: "Slang.Console.MyNamespace", +// className: "Feature2", +// baseLocale: "ru-RU"); +// +// Generator builder3 = new(config3, sourceFilesDirectory); +// +// await builder3.Generate(); + +var config4 = ConfigRepository.Create( + inputFileName: "feature3", + @namespace: "Slang.Console.Features.Feature3", + className: "Feature3", + baseLocale: "ru-RU", + startCharacter: "{{", + endCharacter: "}}"); + +Generator builder4 = new(config4, sourceFilesDirectory); + +await builder4.Generate(); Console.WriteLine("End"); diff --git a/Slang.Runner/Slang.Runner.csproj b/Slang.Runner/Slang.Runner.csproj index 03d233a..4e80d4b 100644 --- a/Slang.Runner/Slang.Runner.csproj +++ b/Slang.Runner/Slang.Runner.csproj @@ -11,6 +11,7 @@ + diff --git a/Slang.Tests/Helpers/TextNodeBuilder.cs b/Slang.Tests/Helpers/TextNodeBuilder.cs index a67e9fc..379b676 100644 --- a/Slang.Tests/Helpers/TextNodeBuilder.cs +++ b/Slang.Tests/Helpers/TextNodeBuilder.cs @@ -19,7 +19,9 @@ public static StringTextNode TextNode( KeyMapCase: CaseStyle.Camel, ParamCase: paramCase, PluralAutoEntity: PluralAutoEntity.Off, - PluralParameter: "n"), + PluralParameter: "n", + StartCharacter: "{", + EndCharacter: "}"), raw, modifiers: new Dictionary() ); diff --git a/Slang.Tests/Integration/Main/YamlJsonFixTests.cs b/Slang.Tests/Integration/Main/YamlJsonFixTests.cs new file mode 100644 index 0000000..0a311eb --- /dev/null +++ b/Slang.Tests/Integration/Main/YamlJsonFixTests.cs @@ -0,0 +1,43 @@ +using System.Globalization; +using Slang.Generator.Core; +using Slang.Generator.Core.Data; + +namespace Slang.Tests.Integration.Main; + +public class YamlJsonTests +{ + private string _enInput; + private string _expectedOutputEn; + + [SetUp] + public void Setup() + { + _enInput = EmbeddedLoader.LoadResource("Slang.Tests.Integration.Resources.yaml_en_json.yaml"); + _expectedOutputEn = EmbeddedLoader.LoadResource("Slang.Tests.Integration.Resources._expected_en_json.output"); + } + + [Test] + public void Yaml() + { + CultureInfo en = new("en"); + + var result = GeneratorFacade.Generate( + rawConfig: ConfigRepository.Create( + inputFileName: "yaml", + @namespace: "Slang.Tests", + className: "TestLocales", + startCharacter: "{{", + endCharacter: "}}" + ), + new TranslationComposition + { + { en, TranslationsDecoder.DecodeWithFileType(_enInput, "yaml") } + }, + new DateTime(2024, 1, 1, 12, 0, 0) + ); + + Console.Write(result.Translations[en]); + + Assert.That(result.Translations[en], Is.EqualTo(_expectedOutputEn)); + } +} \ No newline at end of file diff --git a/Slang.Tests/Integration/Resources/_expected_en_json.output b/Slang.Tests/Integration/Resources/_expected_en_json.output new file mode 100644 index 0000000..3526e1b --- /dev/null +++ b/Slang.Tests/Integration/Resources/_expected_en_json.output @@ -0,0 +1,33 @@ +#nullable enable + +using Slang; +using Slang.Tests; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; + +namespace Slang.Tests +{ + partial class TestLocales + { + protected virtual TestLocales _root { get; } // ignore: unused_field + + public TestLocales() + { + _root = this; + } + + // Translations + + /// In en, this message translates to: + /// **"[{"Some":17,"Json":"1234"}]"** + public virtual string SomeJson => "[{\"Some\":17,\"Json\":\"1234\"}]"; + + /// In en, this message translates to: + /// **"asd {some}"** + public virtual string SomeExt(int some) => $"asd {some}"; + + } +} \ No newline at end of file diff --git a/Slang.Tests/Integration/Resources/yaml_en_json.yaml b/Slang.Tests/Integration/Resources/yaml_en_json.yaml new file mode 100644 index 0000000..41eb07b --- /dev/null +++ b/Slang.Tests/Integration/Resources/yaml_en_json.yaml @@ -0,0 +1,2 @@ +SomeJson: '[{"Some":17,"Json":"1234"}]' +SomeExt: 'asd {{some: int}}' \ No newline at end of file diff --git a/Slang.Tests/Unit/Utils/ParseInterpolationTests.cs b/Slang.Tests/Unit/Utils/ParseInterpolationTests.cs new file mode 100644 index 0000000..dd7b9a0 --- /dev/null +++ b/Slang.Tests/Unit/Utils/ParseInterpolationTests.cs @@ -0,0 +1,37 @@ +using Slang.Generator.Core.Entities; +using Slang.Generator.Core.Nodes.Utils; + +namespace Slang.Tests.Unit.Utils; + +public class ParseInterpolationTests +{ + [Test] + public void CustomPlaceholderInterpolation() + { + var interpolation = NodeHelpers.ParseInterpolation( + raw: """[{"Some":17,"Json":"1234"}]""", + defaultType: "object", + paramCase: CaseStyle.Camel, + "{{", + "}}" + ); + + Assert.IsEmpty(interpolation.Params); + Assert.AreEqual("""[{"Some":17,"Json":"1234"}]""", interpolation.ParsedContent); + } + + [Test] + public void CustomPlaceholderInterpolationTest() + { + var interpolation = NodeHelpers.ParseInterpolation( + raw: """some {{xxx: int}}""", + defaultType: "object", + paramCase: CaseStyle.Camel, + "{{", + "}}" + ); + + Assert.AreEqual(new Dictionary { { "xxx", "int" } }, interpolation.Params); + Assert.AreEqual("""some {xxx}""", interpolation.ParsedContent); + } +} \ No newline at end of file diff --git a/Slang.slnx b/Slang.slnx index ceff80d..f356df5 100644 --- a/Slang.slnx +++ b/Slang.slnx @@ -1,8 +1,8 @@  - - - + + + @@ -11,27 +11,28 @@ - + - + - + - - + + - - + + + - - - - + + + + \ No newline at end of file