diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatReadOptions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatReadOptions.cs index 2928b06..cb52564 100644 --- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatReadOptions.cs +++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatReadOptions.cs @@ -5,7 +5,7 @@ namespace Ashampoo.Translation.Systems.Formats.Abstractions; /// /// This class is used to provide options for reading a from a file. /// -public class FormatReadOptions +public record FormatReadOptions { /// /// The target language of the format. diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilder.cs index f575c11..3cda0fe 100644 --- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilder.cs @@ -13,7 +13,7 @@ public interface IFormatBuilder where T : class, IFormat /// /// The instance of . /// - T Build(); + T Build(IFormatBuilderOptions? options = null); /// /// Set the header information. All information will be added to the header and will overwrite diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderOptions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderOptions.cs new file mode 100644 index 0000000..5177b78 --- /dev/null +++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderOptions.cs @@ -0,0 +1,6 @@ +namespace Ashampoo.Translation.Systems.Formats.Abstractions; + +/// +/// An interface to add optional options to a format builder to manipulate its behaviour. +/// +public interface IFormatBuilderOptions; \ No newline at end of file diff --git a/src/Ashampoo.Translation.Systems.Formats.PO/tests/FormatTest.cs b/src/Ashampoo.Translation.Systems.Formats.PO/tests/FormatTest.cs index 3af11d7..cff14ea 100644 --- a/src/Ashampoo.Translation.Systems.Formats.PO/tests/FormatTest.cs +++ b/src/Ashampoo.Translation.Systems.Formats.PO/tests/FormatTest.cs @@ -40,6 +40,69 @@ public void ReadFromFile() .Be("Vielen Dank, dass Sie die Umfrage abgeschlossen haben!"); } + [Fact] + public void CreateBuilderWithDisabledPipeSplittingTest() + { + var format = CreateAndReadFromFile("translation_de.po", + new FormatReadOptions { SourceLanguage = new Language("en-US") }); + + + var poHeader = format.Header as POHeader; + poHeader.Should().NotBeNull(); + + var poBuilder = new POFormatBuilder(); + foreach (var unit in format.TranslationUnits) + { + foreach (var translation in unit.Translations) + { + poBuilder.Add(unit.Id, translation.Value); + } + } + poBuilder.SetTargetLanguage(format.Header.TargetLanguage); + var newFormat = poBuilder.Build(new PoBuilderOptions { SplitContextAndId = false }); + var memoryStream = new MemoryStream(); + newFormat.Write(memoryStream); + memoryStream.Seek(0, SeekOrigin.Begin); + var streamReader = new StreamReader(memoryStream); + var result = streamReader.ReadToEnd(); + result.Should().NotContain("msgctxt "); + } + + [Fact] + public void ReadWithoutMsgCtxtText() + { + var format = CreateAndReadFromFile("test.po", + new FormatReadOptions { SourceLanguage = new Language("en-US") }); + + var poHeader = format.Header as POHeader; + poHeader.Should().NotBeNull(); + + format.TranslationUnits.Count.Should().Be(2); + format.Header.TargetLanguage.Should().Be(new Language("de-DE")); + + const string id = "testid2"; + + format.TranslationUnits.GetTranslationUnit(id).Translations.GetTranslation(new Language("de-DE")).Value.Should() + .Be("deutscher testid2 Text"); + } + + [Fact] + public async Task ReadAndWriteWithoutMsgCtxtText() + { + var format = await CreateAndReadFromFileAsync("test.po", + new FormatReadOptions { SourceLanguage = new Language("en-US") }); + + var outStream = new MemoryStream(); + await format.WriteAsync(outStream); + await outStream.FlushAsync(); + outStream.Seek(0, SeekOrigin.Begin); + + var reader = new StreamReader(outStream); + var result = await reader.ReadToEndAsync(); + result.Should().NotBeEmpty(); + result.Should().NotContain("msgctxt "); + } + [Fact] public async Task ReadAndWriteAsync() { @@ -66,7 +129,7 @@ public async Task ReadAndWriteAsync() //fs.MustBeEqualTo(ms); File.Delete($"{temp}normalized_translation_de.po"); } - + [Fact] public void ReadAndWrite() { @@ -105,7 +168,7 @@ public void WriteFormatLeavesStreamOpen() memoryStream.CanRead.Should().BeTrue(); memoryStream.CanWrite.Should().BeTrue(); } - + [Fact] public async Task WriteFormatLeavesStreamOpenAsync() { diff --git a/src/Ashampoo.Translation.Systems.Formats.PO/tests/_TestFiles_/test.po b/src/Ashampoo.Translation.Systems.Formats.PO/tests/_TestFiles_/test.po new file mode 100644 index 0000000..869900a --- /dev/null +++ b/src/Ashampoo.Translation.Systems.Formats.PO/tests/_TestFiles_/test.po @@ -0,0 +1,9 @@ +msgid "" +msgstr "" +"Language: de-DE\n" + +msgid "testid1" +msgstr "deutscher testid1 Text" + +msgid "testid2" +msgstr "deutscher testid2 Text" diff --git a/src/Ashampoo.Translation.Systems.Formats/src/AshLang/AshLangFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/AshLang/AshLangFormatBuilder.cs index 59add0d..7667350 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/AshLang/AshLangFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/AshLang/AshLangFormatBuilder.cs @@ -17,7 +17,7 @@ public class AshLangFormatBuilder : IFormatBuilderWithSourceAndTarget _information = new(); /// - public AshLangFormat Build() + public AshLangFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_targetLanguage.Value, nameof(_targetLanguage)); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/CSV/CsvFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/CSV/CsvFormatBuilder.cs index 670cdd4..45bd43e 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/CSV/CsvFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/CSV/CsvFormatBuilder.cs @@ -17,7 +17,7 @@ public sealed class CsvFormatBuilder : IFormatBuilderWithSourceAndTarget CustomHeaderInformation { get; set; } = new(); /// - public CsvFormat Build() + public CsvFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_targetLanguage.Value); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/DependencyInjection.cs b/src/Ashampoo.Translation.Systems.Formats/src/DependencyInjection.cs index 69e3556..6cc5ce9 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/DependencyInjection.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/DependencyInjection.cs @@ -6,6 +6,7 @@ using Ashampoo.Translation.Systems.Formats.Json; using Ashampoo.Translation.Systems.Formats.NLang; using Ashampoo.Translation.Systems.Formats.PO; +using Ashampoo.Translation.Systems.Formats.QT; using Ashampoo.Translation.Systems.Formats.ResX; using Ashampoo.Translation.Systems.Formats.TsProj; using Microsoft.Extensions.DependencyInjection; @@ -34,13 +35,14 @@ public static IServiceCollection RegisterFormats(this IServiceCollection service services.AddSingleton(); services .AddAshLangFormatFeatures() + .AddCsvFormat() .AddGengoFormatFeatures() .AddJavaPropertiesFormatFeatures() .AddJsonFormatFeatures() .AddNLangFormatFeatures() .AddPOFormatFeatures() + .AddQtFormat() .AddResXFormatFeatures() - .AddCsvFormat() .AddTsProjFormatFeatures(); return services; diff --git a/src/Ashampoo.Translation.Systems.Formats/src/Gengo/GengoFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/Gengo/GengoFormatBuilder.cs index b4553df..fd2013b 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/Gengo/GengoFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/Gengo/GengoFormatBuilder.cs @@ -21,7 +21,7 @@ public void Add(string id, string source, string target) } /// - public GengoFormat Build() + public GengoFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_sourceLanguage?.Value, nameof(_sourceLanguage)); Guard.IsNotNullOrWhiteSpace(_targetLanguage?.Value, nameof(_targetLanguage)); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/JavaProperties/JavaPropertiesFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/JavaProperties/JavaPropertiesFormatBuilder.cs index 1f5d9c3..2e849aa 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/JavaProperties/JavaPropertiesFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/JavaProperties/JavaPropertiesFormatBuilder.cs @@ -14,7 +14,7 @@ public class JavaPropertiesFormatBuilder : IFormatBuilderWithTarget _translations = new(); /// - public JavaPropertiesFormat Build() + public JavaPropertiesFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_targetLanguage.Value); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/Json/JsonFormat.cs b/src/Ashampoo.Translation.Systems.Formats/src/Json/JsonFormat.cs index 2983b56..9cd2743 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/Json/JsonFormat.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/Json/JsonFormat.cs @@ -181,13 +181,13 @@ public async Task WriteAsync(Stream stream) var root = new JsonObject(); CreateJsonObjects(root); // Create JSON objects from TranslationUnits - var options = new JsonSerializerOptions // Create JSON serializer options + var serializerOptions = new JsonSerializerOptions // Create JSON serializer options { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - await JsonSerializer.SerializeAsync(stream, root, options); + await JsonSerializer.SerializeAsync(stream, root, serializerOptions); await stream.FlushAsync(); } diff --git a/src/Ashampoo.Translation.Systems.Formats/src/Json/JsonFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/Json/JsonFormatBuilder.cs index b6e4e85..1fc652f 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/Json/JsonFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/Json/JsonFormatBuilder.cs @@ -20,7 +20,7 @@ public void Add(string id, string target) } /// - public JsonFormat Build() + public JsonFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_targetLanguage?.Value, nameof(_targetLanguage)); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/NLang/NLangFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/NLang/NLangFormatBuilder.cs index 35a176f..2fef4ed 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/NLang/NLangFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/NLang/NLangFormatBuilder.cs @@ -19,7 +19,7 @@ public void Add(string id, string target) } /// - public NLangFormat Build() + public NLangFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_targetLanguage?.Value, nameof(_targetLanguage)); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/PO/MessageString.cs b/src/Ashampoo.Translation.Systems.Formats/src/PO/MessageString.cs index a2e9346..7ab9177 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/PO/MessageString.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/PO/MessageString.cs @@ -21,8 +21,10 @@ public class MessageString : ITranslation /// /// Provides the id for the ITranslation interface. /// - public string Id => !string.IsNullOrWhiteSpace(MsgCtxt) ? $"{MsgCtxt}{POConstants.Divider}{MsgId}" : MsgId; - + public string Id => !string.IsNullOrWhiteSpace(MsgCtxt) + ? $"{MsgCtxt}{POConstants.Divider}{MsgId}" + : MsgId; + /// /// Provides the comment for the ITranslation interface. /// @@ -30,7 +32,7 @@ public class MessageString : ITranslation /// public Language Language { get; set; } - + /// /// Message string of the po format. /// @@ -53,6 +55,7 @@ public string Value /// /// /// + /// public MessageString(string id, string value, Language language, IList comments, string msgCtxt = "") { MsgId = id; @@ -88,6 +91,7 @@ public virtual async Task WriteAsync(TextWriter writer) await writer.WriteLineAsync($"{Escape(comment)}"); } } + if (!string.IsNullOrWhiteSpace(MsgCtxt)) await writer.WriteLineAsync($"{POConstants.TypeMsgCtxt}\"{Escape(MsgCtxt)}\""); await writer.WriteLineAsync($"{POConstants.TypeMsgId}\"{Escape(MsgId)}\""); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/PO/POFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/PO/POFormatBuilder.cs index 61a6df1..b0f6d63 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/PO/POFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/PO/POFormatBuilder.cs @@ -19,7 +19,7 @@ public void Add(string id, string target) } /// - public POFormat Build() + public POFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_targetLanguage?.Value, nameof(_targetLanguage)); @@ -32,11 +32,12 @@ public POFormat Build() } }; + var builderOptions = options as PoBuilderOptions ?? new PoBuilderOptions(); foreach (var translation in _translations) { var translationUnit = new TranslationUnit(translation.Key); var index = translation.Key.IndexOf(POConstants.Divider, StringComparison.InvariantCulture); - if (index > 0) // if divider exists, then a message context is used + if (builderOptions.SplitContextAndId && index > 0) // if divider exists, then a message context is used { var ctxt = translation.Key[..index]; var msgId = translation.Key[(index + POConstants.Divider.Length)..]; diff --git a/src/Ashampoo.Translation.Systems.Formats/src/PO/PoBuilderOptions.cs b/src/Ashampoo.Translation.Systems.Formats/src/PO/PoBuilderOptions.cs new file mode 100644 index 0000000..f75a2c1 --- /dev/null +++ b/src/Ashampoo.Translation.Systems.Formats/src/PO/PoBuilderOptions.cs @@ -0,0 +1,14 @@ +using Ashampoo.Translation.Systems.Formats.Abstractions; + +namespace Ashampoo.Translation.Systems.Formats.PO; + +/// +/// IFormatBuilderOptions for POFormat +/// +public sealed record PoBuilderOptions : IFormatBuilderOptions +{ + /// + /// Disables splitting of the id into msgctxt and msgid if a pipe separator is detected. + /// + public bool SplitContextAndId { get; init; } = true; +}; \ No newline at end of file diff --git a/src/Ashampoo.Translation.Systems.Formats/src/QT/QTFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/QT/QTFormatBuilder.cs index 4fbcb3f..f702228 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/QT/QTFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/QT/QTFormatBuilder.cs @@ -14,7 +14,7 @@ public class QtFormatBuilder : IFormatBuilderWithTarget private readonly Dictionary _translations = new(); /// - public QtFormat Build() + public QtFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_targetLanguage.Value); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/ResX/ResXFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/ResX/ResXFormatBuilder.cs index 343fd7a..a498b2d 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/ResX/ResXFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/ResX/ResXFormatBuilder.cs @@ -15,7 +15,7 @@ public class ResXFormatBuilder : IFormatBuilderWithTarget private readonly Dictionary _translations = new(); /// - public ResXFormat Build() + public ResXFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_targetLanguage?.Value, nameof(_targetLanguage)); diff --git a/src/Ashampoo.Translation.Systems.Formats/src/TsProj/TsProjFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats/src/TsProj/TsProjFormatBuilder.cs index c8ee2f6..8cc0664 100644 --- a/src/Ashampoo.Translation.Systems.Formats/src/TsProj/TsProjFormatBuilder.cs +++ b/src/Ashampoo.Translation.Systems.Formats/src/TsProj/TsProjFormatBuilder.cs @@ -23,7 +23,7 @@ public void Add(string id, string source, string target) } /// - public TsProjFormat Build() + public TsProjFormat Build(IFormatBuilderOptions? options = null) { Guard.IsNotNullOrWhiteSpace(_sourceLanguage?.Value, nameof(_sourceLanguage)); // sourceLanguage is required Guard.IsNotNullOrWhiteSpace(_targetLanguage?.Value, nameof(_targetLanguage)); // targetLanguage is required