diff --git a/src/Serval/src/Serval.Translation/Services/UsfmGenerationService.cs b/src/Serval/src/Serval.Translation/Services/UsfmGenerationService.cs index a59746c4..c75e1ea9 100644 --- a/src/Serval/src/Serval.Translation/Services/UsfmGenerationService.cs +++ b/src/Serval/src/Serval.Translation/Services/UsfmGenerationService.cs @@ -1,4 +1,4 @@ -using SIL.Machine.Corpora; +using SIL.Machine.Corpora; using SIL.Machine.PunctuationAnalysis; using SIL.Machine.Translation; using SIL.Scripture; @@ -17,7 +17,7 @@ ContractMapper contractMapper private readonly IRepository _builds = builds; private readonly ContractMapper _contractMapper = contractMapper; private const string AIDisclaimerRemark = - "This draft of {0} was generated using AI on {1}. It should be reviewed and edited carefully."; + "This draft of {0} was generated by AI from {1} on {2}. It should be reviewed carefully for errors before use. {3}"; public async Task GetUsfmAsync( string engineId, @@ -76,24 +76,61 @@ public async Task GetUsfmAsync( Build? build = (await _builds.GetAllAsync(b => b.EngineRef == engineId, cancellationToken)) .OrderByDescending(b => b.DateFinished) .FirstOrDefault(); - if (build is null || build.DateFinished is null) + if (build?.DateFinished is null) throw new InvalidOperationException($"Could not find any completed builds for engine '{engineId}'."); - string disclaimerRemark = string.Format( - CultureInfo.InvariantCulture, - AIDisclaimerRemark, - textId, - build.DateFinished.Value.ToUniversalTime().ToString("u") - ); string markerPlacementRemark = GenerateMarkerPlacementRemark( paragraphMarkerBehavior, embedBehavior, styleMarkerBehavior ); - List remarks = [disclaimerRemark, markerPlacementRemark]; + ParallelCorpusContract[] parallelCorpora = [.. _contractMapper.Map(build, engine)]; + + // Get the versification for the project + CorpusBundle corpusBundle = new(parallelCorpora); + ParallelCorpusContract corpusContract = corpusBundle.ParallelCorpora.Single(c => c.Id == corpusId); + CorpusFileContract sourceFile = corpusContract.SourceCorpora[0].Files[0]; + ParatextProjectSettings? sourceSettings = corpusBundle.GetSettings(sourceFile.Location); + ScrVers versification = sourceSettings?.Versification ?? ScrVers.Original; + var scriptureRangeParser = new ScriptureRangeParser(versification); + + // Generate remarks for every chapter in the book + List<(int, string)> remarks = []; + List? chapters = + build + .Pretranslate?.SelectMany(p => p.SourceFilters ?? []) + .SelectMany(s => + scriptureRangeParser + .GetChapters(s.ScriptureRange) + .TryGetValue(textId, out List? filterChapters) + ? filterChapters + : [] + ) + .ToList() + ?? []; + + // If there are no chapters, we need to set it to null so that the USFM updater + if (chapters.Count == 0) + chapters = null; - ParallelCorpusContract[] parallelCorpora = _contractMapper.Map(build, engine).ToArray(); + // Get all the chapters needing remarks + IEnumerable chaptersNeedingRemarks = + chapters ?? Enumerable.Range(1, versification.GetLastChapter(Canon.BookIdToNumber(textId))); + + // Add remarks to each chapter + foreach (int chapterNum in chaptersNeedingRemarks) + { + string disclaimerRemark = string.Format( + CultureInfo.InvariantCulture, + AIDisclaimerRemark, + $"{textId} {chapterNum}", + sourceSettings?.Name ?? "Unknown", + build.DateFinished.Value.ToUniversalTime().ToString("u"), + markerPlacementRemark + ); + remarks.Add((chapterNum, disclaimerRemark)); + } IReadOnlyList pretranslations = await _pretranslations.GetAllAsync( pt => @@ -126,6 +163,7 @@ public async Task GetUsfmAsync( corpusId, textId, textOrigin == PretranslationUsfmTextOrigin.OnlyExisting ? [] : pretranslations, + chapters, textBehavior, Map(paragraphMarkerBehavior), Map(embedBehavior), @@ -146,6 +184,7 @@ public async Task GetUsfmAsync( corpusId, textId, textOrigin == PretranslationUsfmTextOrigin.OnlyExisting ? [] : pretranslations, + chapters, Map(paragraphMarkerBehavior), Map(embedBehavior), Map(styleMarkerBehavior), @@ -163,11 +202,12 @@ private static string UpdateSourceUsfm( string corpusId, string bookId, IReadOnlyList pretranslations, + IReadOnlyList? chapters, UpdateUsfmMarkerBehavior paragraphBehavior, UpdateUsfmMarkerBehavior embedBehavior, UpdateUsfmMarkerBehavior styleBehavior, bool placeParagraphMarkers, - IEnumerable? remarks, + IEnumerable<(int, string)> remarks, string? targetQuoteConvention ) { @@ -176,6 +216,7 @@ private static string UpdateSourceUsfm( corpusId, bookId, pretranslations, + chapters, UpdateUsfmTextBehavior.StripExisting, paragraphBehavior, embedBehavior, @@ -192,11 +233,12 @@ private static string UpdateTargetUsfm( string corpusId, string bookId, IReadOnlyList pretranslations, + IReadOnlyList? chapters, UpdateUsfmTextBehavior textBehavior, UpdateUsfmMarkerBehavior paragraphBehavior, UpdateUsfmMarkerBehavior embedBehavior, UpdateUsfmMarkerBehavior styleBehavior, - IEnumerable? remarks, + IEnumerable<(int, string)> remarks, string? targetQuoteConvention ) { @@ -205,6 +247,7 @@ private static string UpdateTargetUsfm( corpusId, bookId, pretranslations, + chapters, textBehavior, paragraphBehavior, embedBehavior, @@ -221,12 +264,13 @@ private static string UpdateUsfm( string corpusId, string bookId, IEnumerable pretranslations, + IReadOnlyList? chapters, UpdateUsfmTextBehavior textBehavior, UpdateUsfmMarkerBehavior paragraphBehavior, UpdateUsfmMarkerBehavior embedBehavior, UpdateUsfmMarkerBehavior styleBehavior, IEnumerable? updateBlockHandlers, - IEnumerable? remarks, + IEnumerable<(int, string)> remarks, string? targetQuoteConvention, bool isSource ) @@ -244,33 +288,36 @@ bool isSource string usfm = updater.UpdateUsfm( bookId, - pretranslations - .Select(p => - Map( - p, - isSource, - sourceSettings?.Versification, - targetSettings?.Versification, - paragraphBehavior, - styleBehavior + [ + .. pretranslations + .Select(p => + Map( + p, + isSource, + sourceSettings?.Versification, + targetSettings?.Versification, + paragraphBehavior, + styleBehavior + ) ) - ) - .Where(row => row.Refs.Any()) - .OrderBy(row => row.Refs[0]) - .ToArray(), + .Where(row => row.Refs.Any()) + .OrderBy(row => row.Refs[0]), + ], + chapters: chapters, fullName: isSource ? sourceSettings?.FullName : targetSettings?.FullName, textBehavior: textBehavior, paragraphBehavior: paragraphBehavior, embedBehavior: embedBehavior, styleBehavior: styleBehavior, + preserveParagraphStyles: null, updateBlockHandlers: updateBlockHandlers, - remarks: remarks?.Select(r => (0, r)), + !string.IsNullOrEmpty(targetQuoteConvention) ? null : remarks, // Ensure we only add remarks once errorHandler: (_) => true, compareSegments: isSource ) ?? ""; if (!string.IsNullOrEmpty(targetQuoteConvention)) - usfm = DenormalizeQuotationMarks(usfm, targetQuoteConvention); + usfm = DenormalizeQuotationMarks(usfm, targetQuoteConvention, remarks); return usfm; } @@ -351,7 +398,11 @@ pretranslation.Alignment is null return matrix; } - private static string DenormalizeQuotationMarks(string usfm, string quoteConvention) + private static string DenormalizeQuotationMarks( + string usfm, + string quoteConvention, + IEnumerable<(int, string)> remarks + ) { QuoteConvention targetQuoteConvention = QuoteConventions.Standard.GetQuoteConventionByName(quoteConvention); if (targetQuoteConvention is null) @@ -372,35 +423,28 @@ private static string DenormalizeQuotationMarks(string usfm, string quoteConvent int denormalizableChapterCount = bestChapterStrategies.Count(tup => tup.Strategy != QuotationMarkUpdateStrategy.Skip ); - List remarks = []; - string quotationDenormalizationRemark; - if (denormalizableChapterCount == bestChapterStrategies.Count) - { - quotationDenormalizationRemark = - "The quote style in all chapters has been automatically adjusted to match the rest of the project."; - } - else if (denormalizableChapterCount > 0) - { - quotationDenormalizationRemark = - "The quote style in the following chapters has been automatically adjusted to match the rest of the project: " - + GetChapterRangesString( - bestChapterStrategies - .Where(tuple => tuple.Strategy != QuotationMarkUpdateStrategy.Skip) - .Select(tuple => tuple.ChapterNumber) - .ToList() - ) - + "."; - } - else + const string QuotationDenormalizationRemark = + "Quotation marks have been adjusted automatically to match the rest of the project."; + List<(int Chapter, string Remark)> combinedRemarks = [.. remarks]; + for (int i = 1; i <= denormalizableChapterCount; i++) { - quotationDenormalizationRemark = - "The quote style was not automatically adjusted to match the rest of your project in any chapters."; + int index = combinedRemarks.FindLastIndex(r => r.Chapter == i); + if (index > -1) + { + combinedRemarks[index] = combinedRemarks[index] with + { + Remark = $"{combinedRemarks[index].Remark} {QuotationDenormalizationRemark}", + }; + } + else + { + combinedRemarks.Add((i, QuotationDenormalizationRemark)); + } } - remarks.Add(quotationDenormalizationRemark); var updater = new UpdateUsfmParserHandler( updateBlockHandlers: [quotationMarkDenormalizer], - remarks: remarks.Select(r => (0, r)) + remarks: combinedRemarks ); UsfmParser.Parse(usfm, updater); @@ -408,43 +452,6 @@ private static string DenormalizeQuotationMarks(string usfm, string quoteConvent return usfm; } - internal static string GetChapterRangesString(List chapterNumbers) - { - chapterNumbers = chapterNumbers.Order().ToList(); - int start = chapterNumbers[0]; - int end = chapterNumbers[0]; - List chapterRangeStrings = []; - foreach (int chapterNumber in chapterNumbers[1..]) - { - if (chapterNumber == end + 1) - { - end = chapterNumber; - } - else - { - if (start == end) - { - chapterRangeStrings.Add(start.ToString(CultureInfo.InvariantCulture)); - } - else - { - chapterRangeStrings.Add($"{start}-{end}"); - } - start = chapterNumber; - end = chapterNumber; - } - } - if (start == end) - { - chapterRangeStrings.Add(start.ToString(CultureInfo.InvariantCulture)); - } - else - { - chapterRangeStrings.Add($"{start}-{end}"); - } - return string.Join(", ", chapterRangeStrings); - } - /// /// Generate a natural sounding remark/comment describing marker placement. /// @@ -456,13 +463,13 @@ internal static string GetChapterRangesString(List chapterNumbers) /// Remarks are generated in the format: /// /// - /// Paragraph breaks, embed markers, and style markers were moved to the end of the verse. + /// Paragraph breaks, embed markers, and style marker positions were moved to the end of the verse. /// /// - /// Paragraph breaks were moved to the end of the verse. Embed markers have positions preserved. Style markers were removed. + /// Paragraph break positions were moved to the end of the verse. Embed marker positions have been preserved. Style markers were removed. /// /// - /// Paragraph breaks and style markers were moved to the end of the verse. Embed markers were removed. + /// Paragraph break and style marker positions were moved to the end of the verse. Embed markers were removed. /// /// /// @@ -479,25 +486,29 @@ PretranslationUsfmMarkerBehavior styleMarkerBehavior { PretranslationUsfmMarkerBehavior.Strip, [] }, }; - behaviorMap[paragraphMarkerBehavior].Add("paragraph breaks"); - behaviorMap[embedBehavior].Add("embed markers"); - behaviorMap[styleMarkerBehavior].Add("style markers"); + behaviorMap[paragraphMarkerBehavior].Add("paragraph break"); + behaviorMap[embedBehavior].Add("embed marker"); + behaviorMap[styleMarkerBehavior].Add("style marker"); IEnumerable sentences = behaviorMap .Where(kvp => kvp.Value.Count > 0) .Select(kvp => { - string markers = + string markersSingular = kvp.Value.Count == 1 ? kvp.Value[0] : string.Join(", ", kvp.Value[..^1]) + " and " + kvp.Value[^1]; - markers = char.ToUpperInvariant(markers[0]) + markers[1..]; - string behavior = kvp.Key switch + string markersPlural = + kvp.Value.Count == 1 + ? kvp.Value[0] + "s" + : string.Join(", ", kvp.Value[..^1] + "s") + " and " + kvp.Value[^1] + "s"; + string sentence = kvp.Key switch { - PretranslationUsfmMarkerBehavior.Preserve => "were moved to the end of the verse", - PretranslationUsfmMarkerBehavior.PreservePosition => "have positions preserved", - PretranslationUsfmMarkerBehavior.Strip => "were removed", - _ => "have unknown behavior", + PretranslationUsfmMarkerBehavior.Preserve => + $"{markersSingular} positions were moved to the end of the verse.", + PretranslationUsfmMarkerBehavior.PreservePosition => $"{markersSingular} positions were preserved.", + PretranslationUsfmMarkerBehavior.Strip => $"{markersPlural} were removed.", + _ => $"{markersPlural} have unknown behavior.", }; - return $"{markers} {behavior}."; + return char.ToUpperInvariant(sentence[0]) + sentence[1..]; }); return string.Join(" ", sentences); diff --git a/src/Serval/test/Serval.ApiServer.IntegrationTests/TranslationEngineTests.cs b/src/Serval/test/Serval.ApiServer.IntegrationTests/TranslationEngineTests.cs index cd306399..2d114bf7 100644 --- a/src/Serval/test/Serval.ApiServer.IntegrationTests/TranslationEngineTests.cs +++ b/src/Serval/test/Serval.ApiServer.IntegrationTests/TranslationEngineTests.cs @@ -2291,10 +2291,9 @@ await _env.Builds.InsertAsync( usfm.Replace("\r\n", "\n"), Is.EqualTo( @"\id MAT - Test1 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \h \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \p \v 1 translation \v 2 diff --git a/src/Serval/test/Serval.Translation.Tests/Services/UsfmGenerationServiceTests.cs b/src/Serval/test/Serval.Translation.Tests/Services/UsfmGenerationServiceTests.cs index 2b7acd41..8fadff87 100644 --- a/src/Serval/test/Serval.Translation.Tests/Services/UsfmGenerationServiceTests.cs +++ b/src/Serval/test/Serval.Translation.Tests/Services/UsfmGenerationServiceTests.cs @@ -1,8 +1,11 @@ namespace Serval.Translation.Services; [TestFixture] -public class UsfmGenerationServiceTests +public partial class UsfmGenerationServiceTests { + [GeneratedRegex(@"\\rem.+")] + private static partial Regex RemarkRegex(); + [Test] public async Task GetUsfmAsync_Source_PreferExisting() { @@ -17,9 +20,8 @@ public async Task GetUsfmAsync_Source_PreferExisting() usfm, Is.EqualTo( @"\id MAT - Test1 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 Chapter 1, verse 1. ""Translated new paragraph"" \p \v 2 Chapter 1, verse 2. @@ -44,9 +46,8 @@ public async Task GetUsfmAsync_Source_PreferPretranslated() usfm, Is.EqualTo( @"\id MAT - Test1 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 Chapter 1, verse 1. ""Translated new paragraph"" \p \v 2 Chapter 1, verse 2. @@ -71,9 +72,8 @@ public async Task GetUsfmAsync_Source_OnlyExisting() usfm, Is.EqualTo( @"\id MAT - Test1 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 \p \v 2 @@ -98,9 +98,8 @@ public async Task GetUsfmAsync_Source_OnlyPretranslated() usfm, Is.EqualTo( @"\id MAT - Test1 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 Chapter 1, verse 1. ""Translated new paragraph"" \p \v 2 Chapter 1, verse 2. @@ -126,9 +125,8 @@ public async Task GetUsfmAsync_Source_PlaceMarkers() usfm, Is.EqualTo( @"\id MAT - Test1 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Embed markers were moved to the end of the verse. Paragraph breaks have positions preserved. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Embed marker positions were moved to the end of the verse. Paragraph break positions were preserved. Style markers were removed. \v 1 Chapter 1, verse 1. \p ""Translated new paragraph"" \v 2 Chapter 1, verse 2. @@ -153,9 +151,8 @@ public async Task GetUsfmAsync_Target_PreferExisting() usfm, Is.EqualTo( @"\id MAT - TRG -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 TRG - Chapter one, verse one. \v 2 Chapter 1, verse 2. \v 3 TRG - Chapter one, verse three. @@ -179,9 +176,8 @@ public async Task GetUsfmAsync_Target_PreferPretranslated() usfm, Is.EqualTo( @"\id MAT - Test3 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 Chapter 1, verse 1. ""Translated new paragraph"" \v 2 Chapter 1, verse 2. \v 3 TRG - Chapter one, verse three. @@ -218,9 +214,8 @@ public async Task GetUsfmAsync_Auto_TargetBookDoesNotExist() usfm, Is.EqualTo( @"\id MAT - Test1 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 Chapter 1, verse 1. ""Translated new paragraph"" \p \v 2 Chapter 1, verse 2. @@ -245,9 +240,8 @@ public async Task GetUsfmAsync_Auto_TargetBookExists() usfm, Is.EqualTo( @"\id MAT - Test3 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 Chapter 1, verse 1. ""Translated new paragraph"" \v 2 Chapter 1, verse 2. \v 3 TRG - Chapter one, verse three. @@ -277,13 +271,9 @@ public async Task GetUsfmAsync_Target_OnlyExisting() List lines = [.. targetUsfm.Split('\n')]; - lines.Insert( - 1, - @"\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully." - ); lines.Insert( 2, - @"\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed." + @"\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed." ); Assert.That(usfm, Is.EqualTo(string.Join('\n', lines)).IgnoreLineEndings()); } @@ -302,9 +292,8 @@ public async Task GetUsfmAsync_Target_OnlyPretranslated() usfm, Is.EqualTo( @"\id MAT - Test3 -\rem This draft of MAT was generated using AI on 1970-01-01 00:00:00Z. It should be reviewed and edited carefully. -\rem Paragraph breaks and embed markers were moved to the end of the verse. Style markers were removed. \c 1 +\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. It should be reviewed carefully for errors before use. Paragraph break and embed marker positions were moved to the end of the verse. Style markers were removed. \v 1 Chapter 1, verse 1. ""Translated new paragraph"" \v 2 Chapter 1, verse 2. \v 3 @@ -337,7 +326,18 @@ public async Task GetUsfmAsync_DenormalizeQuotationMarks() quotationMarkBehavior: PretranslationNormalizationBehavior.Denormalized ); Assert.That(usfm, Does.Contain("“Translated new paragraph”")); - Assert.That(Regex.Matches(usfm, @"\\rem"), Has.Count.EqualTo(3)); + MatchCollection remarks = RemarkRegex().Matches(usfm); + Assert.That(remarks, Has.Count.EqualTo(1)); + Assert.That( + remarks.First().Value, + Is.EqualTo( + @"\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. " + + "It should be reviewed carefully for errors before use. " + + "Paragraph break and embed marker positions were moved to the end of the verse. " + + "Style markers were removed. " + + "Quotation marks have been adjusted automatically to match the rest of the project." + ) + ); usfm = await env.GetUsfmAsync( PretranslationUsfmTextOrigin.PreferExisting, @@ -345,7 +345,17 @@ public async Task GetUsfmAsync_DenormalizeQuotationMarks() quotationMarkBehavior: PretranslationNormalizationBehavior.Normalized ); Assert.That(usfm, Does.Contain("\"Translated new paragraph\"")); - Assert.That(Regex.Matches(usfm, @"\\rem"), Has.Count.EqualTo(2)); + remarks = RemarkRegex().Matches(usfm); + Assert.That(remarks, Has.Count.EqualTo(1)); + Assert.That( + remarks.First().Value, + Is.EqualTo( + @"\rem This draft of MAT 1 was generated by AI from Te1 on 1970-01-01 00:00:00Z. " + + "It should be reviewed carefully for errors before use. " + + "Paragraph break and embed marker positions were moved to the end of the verse. " + + "Style markers were removed." + ) + ); } [Test] @@ -433,18 +443,6 @@ await env.Service.GetUsfmAsync( ); } - [Test] - [TestCase(new int[] { 1, 2, 3 }, "1-3")] - [TestCase(new int[] { 1, 3, 4 }, "1, 3-4")] - [TestCase(new int[] { 2, 3, 4, 6, 8, 9 }, "2-4, 6, 8-9")] - [TestCase(new int[] { 1, 3, 2 }, "1-3")] - [TestCase(new int[] { 1 }, "1")] - public void GetChapterRanges(int[] chapterNumbers, string expectedRangeString) - { - string actualRangeString = UsfmGenerationService.GetChapterRangesString([.. chapterNumbers]); - Assert.That(actualRangeString, Is.EqualTo(expectedRangeString)); - } - private class TestEnvironment : IDisposable { private static readonly string TestDataPath = Path.Combine("..", "..", "..", "data");