1- using SIL . Machine . Corpora ;
1+ using SIL . Machine . Corpora ;
22using SIL . Machine . PunctuationAnalysis ;
33using SIL . Machine . Translation ;
44using SIL . Scripture ;
@@ -17,7 +17,7 @@ ContractMapper contractMapper
1717 private readonly IRepository < Build > _builds = builds ;
1818 private readonly ContractMapper _contractMapper = contractMapper ;
1919 private const string AIDisclaimerRemark =
20- "This draft of {0} was generated using AI on {1}. It should be reviewed and edited carefully. " ;
20+ "This draft of {0} was generated by AI from {1} on {2} . It should be reviewed carefully for errors before use. {3} " ;
2121
2222 public async Task < string > GetUsfmAsync (
2323 string engineId ,
@@ -76,24 +76,61 @@ public async Task<string> GetUsfmAsync(
7676 Build ? build = ( await _builds . GetAllAsync ( b => b . EngineRef == engineId , cancellationToken ) )
7777 . OrderByDescending ( b => b . DateFinished )
7878 . FirstOrDefault ( ) ;
79- if ( build is null || build . DateFinished is null )
79+ if ( build ? . DateFinished is null )
8080 throw new InvalidOperationException ( $ "Could not find any completed builds for engine '{ engineId } '.") ;
8181
82- string disclaimerRemark = string . Format (
83- CultureInfo . InvariantCulture ,
84- AIDisclaimerRemark ,
85- textId ,
86- build . DateFinished . Value . ToUniversalTime ( ) . ToString ( "u" )
87- ) ;
8882 string markerPlacementRemark = GenerateMarkerPlacementRemark (
8983 paragraphMarkerBehavior ,
9084 embedBehavior ,
9185 styleMarkerBehavior
9286 ) ;
9387
94- List < string > remarks = [ disclaimerRemark , markerPlacementRemark ] ;
88+ ParallelCorpusContract [ ] parallelCorpora = [ .. _contractMapper . Map ( build , engine ) ] ;
89+
90+ // Get the versification for the project
91+ CorpusBundle corpusBundle = new ( parallelCorpora ) ;
92+ ParallelCorpusContract corpusContract = corpusBundle . ParallelCorpora . Single ( c => c . Id == corpusId ) ;
93+ CorpusFileContract sourceFile = corpusContract . SourceCorpora [ 0 ] . Files [ 0 ] ;
94+ ParatextProjectSettings ? sourceSettings = corpusBundle . GetSettings ( sourceFile . Location ) ;
95+ ScrVers versification = sourceSettings ? . Versification ?? ScrVers . Original ;
96+ var scriptureRangeParser = new ScriptureRangeParser ( versification ) ;
97+
98+ // Generate remarks for every chapter in the book
99+ List < ( int , string ) > remarks = [ ] ;
100+ List < int > ? chapters =
101+ build
102+ . Pretranslate ? . SelectMany ( p => p . SourceFilters ?? [ ] )
103+ . SelectMany ( s =>
104+ scriptureRangeParser
105+ . GetChapters ( s . ScriptureRange )
106+ . TryGetValue ( textId , out List < int > ? filterChapters )
107+ ? filterChapters
108+ : [ ]
109+ )
110+ . ToList ( )
111+ ?? [ ] ;
112+
113+ // If there are no chapters, we need to set it to null so that the USFM updater
114+ if ( chapters . Count == 0 )
115+ chapters = null ;
95116
96- ParallelCorpusContract [ ] parallelCorpora = _contractMapper . Map ( build , engine ) . ToArray ( ) ;
117+ // Get all the chapters needing remarks
118+ IEnumerable < int > chaptersNeedingRemarks =
119+ chapters ?? Enumerable . Range ( 1 , versification . GetLastChapter ( Canon . BookIdToNumber ( textId ) ) ) ;
120+
121+ // Add remarks to each chapter
122+ foreach ( int chapterNum in chaptersNeedingRemarks )
123+ {
124+ string disclaimerRemark = string . Format (
125+ CultureInfo . InvariantCulture ,
126+ AIDisclaimerRemark ,
127+ $ "{ textId } { chapterNum } ",
128+ sourceSettings ? . Name ?? "Unknown" ,
129+ build . DateFinished . Value . ToUniversalTime ( ) . ToString ( "u" ) ,
130+ markerPlacementRemark
131+ ) ;
132+ remarks . Add ( ( chapterNum , disclaimerRemark ) ) ;
133+ }
97134
98135 IReadOnlyList < Pretranslation > pretranslations = await _pretranslations . GetAllAsync (
99136 pt =>
@@ -126,6 +163,7 @@ public async Task<string> GetUsfmAsync(
126163 corpusId ,
127164 textId ,
128165 textOrigin == PretranslationUsfmTextOrigin . OnlyExisting ? [ ] : pretranslations,
166+ chapters ,
129167 textBehavior ,
130168 Map ( paragraphMarkerBehavior ) ,
131169 Map ( embedBehavior ) ,
@@ -146,6 +184,7 @@ public async Task<string> GetUsfmAsync(
146184 corpusId ,
147185 textId ,
148186 textOrigin == PretranslationUsfmTextOrigin . OnlyExisting ? [ ] : pretranslations,
187+ chapters ,
149188 Map ( paragraphMarkerBehavior ) ,
150189 Map ( embedBehavior ) ,
151190 Map ( styleMarkerBehavior ) ,
@@ -163,11 +202,12 @@ private static string UpdateSourceUsfm(
163202 string corpusId ,
164203 string bookId ,
165204 IReadOnlyList < Pretranslation > pretranslations ,
205+ IReadOnlyList < int > ? chapters ,
166206 UpdateUsfmMarkerBehavior paragraphBehavior ,
167207 UpdateUsfmMarkerBehavior embedBehavior ,
168208 UpdateUsfmMarkerBehavior styleBehavior ,
169209 bool placeParagraphMarkers ,
170- IEnumerable < string > ? remarks ,
210+ IEnumerable < ( int , string ) > remarks ,
171211 string ? targetQuoteConvention
172212 )
173213 {
@@ -176,6 +216,7 @@ private static string UpdateSourceUsfm(
176216 corpusId ,
177217 bookId ,
178218 pretranslations ,
219+ chapters ,
179220 UpdateUsfmTextBehavior . StripExisting ,
180221 paragraphBehavior ,
181222 embedBehavior ,
@@ -192,11 +233,12 @@ private static string UpdateTargetUsfm(
192233 string corpusId ,
193234 string bookId ,
194235 IReadOnlyList < Pretranslation > pretranslations ,
236+ IReadOnlyList < int > ? chapters ,
195237 UpdateUsfmTextBehavior textBehavior ,
196238 UpdateUsfmMarkerBehavior paragraphBehavior ,
197239 UpdateUsfmMarkerBehavior embedBehavior ,
198240 UpdateUsfmMarkerBehavior styleBehavior ,
199- IEnumerable < string > ? remarks ,
241+ IEnumerable < ( int , string ) > remarks ,
200242 string ? targetQuoteConvention
201243 )
202244 {
@@ -205,6 +247,7 @@ private static string UpdateTargetUsfm(
205247 corpusId ,
206248 bookId ,
207249 pretranslations ,
250+ chapters ,
208251 textBehavior ,
209252 paragraphBehavior ,
210253 embedBehavior ,
@@ -221,12 +264,13 @@ private static string UpdateUsfm(
221264 string corpusId ,
222265 string bookId ,
223266 IEnumerable < Pretranslation > pretranslations ,
267+ IReadOnlyList < int > ? chapters ,
224268 UpdateUsfmTextBehavior textBehavior ,
225269 UpdateUsfmMarkerBehavior paragraphBehavior ,
226270 UpdateUsfmMarkerBehavior embedBehavior ,
227271 UpdateUsfmMarkerBehavior styleBehavior ,
228272 IEnumerable < IUsfmUpdateBlockHandler > ? updateBlockHandlers ,
229- IEnumerable < string > ? remarks ,
273+ IEnumerable < ( int , string ) > remarks ,
230274 string ? targetQuoteConvention ,
231275 bool isSource
232276 )
@@ -244,33 +288,36 @@ bool isSource
244288 string usfm =
245289 updater . UpdateUsfm (
246290 bookId ,
247- pretranslations
248- . Select ( p =>
249- Map (
250- p ,
251- isSource ,
252- sourceSettings ? . Versification ,
253- targetSettings ? . Versification ,
254- paragraphBehavior ,
255- styleBehavior
291+ [
292+ .. pretranslations
293+ . Select ( p =>
294+ Map (
295+ p ,
296+ isSource ,
297+ sourceSettings ? . Versification ,
298+ targetSettings ? . Versification ,
299+ paragraphBehavior ,
300+ styleBehavior
301+ )
256302 )
257- )
258- . Where ( row => row . Refs . Any ( ) )
259- . OrderBy ( row => row . Refs [ 0 ] )
260- . ToArray ( ) ,
303+ . Where ( row => row . Refs . Any ( ) )
304+ . OrderBy ( row => row . Refs [ 0 ] ) ,
305+ ] ,
306+ chapters : chapters ,
261307 fullName : isSource ? sourceSettings ? . FullName : targetSettings ? . FullName ,
262308 textBehavior : textBehavior ,
263309 paragraphBehavior : paragraphBehavior ,
264310 embedBehavior : embedBehavior ,
265311 styleBehavior : styleBehavior ,
312+ preserveParagraphStyles : null ,
266313 updateBlockHandlers : updateBlockHandlers ,
267- remarks : remarks ? . Select ( r => ( 0 , r ) ) ,
314+ ! string . IsNullOrEmpty ( targetQuoteConvention ) ? null : remarks , // Ensure we only add remarks once
268315 errorHandler : ( _ ) => true ,
269316 compareSegments : isSource
270317 ) ?? "" ;
271318
272319 if ( ! string . IsNullOrEmpty ( targetQuoteConvention ) )
273- usfm = DenormalizeQuotationMarks ( usfm , targetQuoteConvention ) ;
320+ usfm = DenormalizeQuotationMarks ( usfm , targetQuoteConvention , remarks ) ;
274321 return usfm ;
275322 }
276323
@@ -351,7 +398,11 @@ pretranslation.Alignment is null
351398 return matrix ;
352399 }
353400
354- private static string DenormalizeQuotationMarks ( string usfm , string quoteConvention )
401+ private static string DenormalizeQuotationMarks (
402+ string usfm ,
403+ string quoteConvention ,
404+ IEnumerable < ( int , string ) > remarks
405+ )
355406 {
356407 QuoteConvention targetQuoteConvention = QuoteConventions . Standard . GetQuoteConventionByName ( quoteConvention ) ;
357408 if ( targetQuoteConvention is null )
@@ -372,79 +423,35 @@ private static string DenormalizeQuotationMarks(string usfm, string quoteConvent
372423 int denormalizableChapterCount = bestChapterStrategies . Count ( tup =>
373424 tup . Strategy != QuotationMarkUpdateStrategy . Skip
374425 ) ;
375- List < string > remarks = [ ] ;
376- string quotationDenormalizationRemark ;
377- if ( denormalizableChapterCount == bestChapterStrategies . Count )
378- {
379- quotationDenormalizationRemark =
380- "The quote style in all chapters has been automatically adjusted to match the rest of the project." ;
381- }
382- else if ( denormalizableChapterCount > 0 )
383- {
384- quotationDenormalizationRemark =
385- "The quote style in the following chapters has been automatically adjusted to match the rest of the project: "
386- + GetChapterRangesString (
387- bestChapterStrategies
388- . Where ( tuple => tuple . Strategy != QuotationMarkUpdateStrategy . Skip )
389- . Select ( tuple => tuple . ChapterNumber )
390- . ToList ( )
391- )
392- + "." ;
393- }
394- else
426+ const string QuotationDenormalizationRemark =
427+ "Quotation marks have been adjusted automatically to match the rest of the project." ;
428+ List < ( int Chapter , string Remark ) > combinedRemarks = [ .. remarks ] ;
429+ for ( int i = 1 ; i <= denormalizableChapterCount ; i ++ )
395430 {
396- quotationDenormalizationRemark =
397- "The quote style was not automatically adjusted to match the rest of your project in any chapters." ;
431+ int index = combinedRemarks . FindLastIndex ( r => r . Chapter == i ) ;
432+ if ( index > - 1 )
433+ {
434+ combinedRemarks [ index ] = combinedRemarks [ index ] with
435+ {
436+ Remark = $ "{ combinedRemarks [ index ] . Remark } { QuotationDenormalizationRemark } ",
437+ } ;
438+ }
439+ else
440+ {
441+ combinedRemarks . Add ( ( i , QuotationDenormalizationRemark ) ) ;
442+ }
398443 }
399- remarks . Add ( quotationDenormalizationRemark ) ;
400444
401445 var updater = new UpdateUsfmParserHandler (
402446 updateBlockHandlers : [ quotationMarkDenormalizer ] ,
403- remarks : remarks . Select ( r => ( 0 , r ) )
447+ remarks : combinedRemarks
404448 ) ;
405449 UsfmParser . Parse ( usfm , updater ) ;
406450
407451 usfm = updater . GetUsfm ( ) ;
408452 return usfm ;
409453 }
410454
411- internal static string GetChapterRangesString ( List < int > chapterNumbers )
412- {
413- chapterNumbers = chapterNumbers . Order ( ) . ToList ( ) ;
414- int start = chapterNumbers [ 0 ] ;
415- int end = chapterNumbers [ 0 ] ;
416- List < string > chapterRangeStrings = [ ] ;
417- foreach ( int chapterNumber in chapterNumbers [ 1 ..] )
418- {
419- if ( chapterNumber == end + 1 )
420- {
421- end = chapterNumber ;
422- }
423- else
424- {
425- if ( start == end )
426- {
427- chapterRangeStrings . Add ( start . ToString ( CultureInfo . InvariantCulture ) ) ;
428- }
429- else
430- {
431- chapterRangeStrings . Add ( $ "{ start } -{ end } ") ;
432- }
433- start = chapterNumber ;
434- end = chapterNumber ;
435- }
436- }
437- if ( start == end )
438- {
439- chapterRangeStrings . Add ( start . ToString ( CultureInfo . InvariantCulture ) ) ;
440- }
441- else
442- {
443- chapterRangeStrings . Add ( $ "{ start } -{ end } ") ;
444- }
445- return string . Join ( ", " , chapterRangeStrings ) ;
446- }
447-
448455 /// <summary>
449456 /// Generate a natural sounding remark/comment describing marker placement.
450457 /// </summary>
@@ -456,13 +463,13 @@ internal static string GetChapterRangesString(List<int> chapterNumbers)
456463 /// <para>Remarks are generated in the format:</para>
457464 /// <list type="bullet">
458465 /// <item><description>
459- /// Paragraph breaks, embed markers, and style markers were moved to the end of the verse.
466+ /// Paragraph breaks, embed markers, and style marker positions were moved to the end of the verse.
460467 /// </description></item>
461468 /// <item><description>
462- /// Paragraph breaks were moved to the end of the verse. Embed markers have positions preserved. Style markers were removed.
469+ /// Paragraph break positions were moved to the end of the verse. Embed marker positions have been preserved. Style markers were removed.
463470 /// </description></item>
464471 /// <item><description>
465- /// Paragraph breaks and style markers were moved to the end of the verse. Embed markers were removed.
472+ /// Paragraph break and style marker positions were moved to the end of the verse. Embed markers were removed.
466473 /// </description></item>
467474 /// </list>
468475 /// </remarks>
@@ -479,25 +486,29 @@ PretranslationUsfmMarkerBehavior styleMarkerBehavior
479486 { PretranslationUsfmMarkerBehavior . Strip , [ ] } ,
480487 } ;
481488
482- behaviorMap [ paragraphMarkerBehavior ] . Add ( "paragraph breaks " ) ;
483- behaviorMap [ embedBehavior ] . Add ( "embed markers " ) ;
484- behaviorMap [ styleMarkerBehavior ] . Add ( "style markers " ) ;
489+ behaviorMap [ paragraphMarkerBehavior ] . Add ( "paragraph break " ) ;
490+ behaviorMap [ embedBehavior ] . Add ( "embed marker " ) ;
491+ behaviorMap [ styleMarkerBehavior ] . Add ( "style marker " ) ;
485492
486493 IEnumerable < string > sentences = behaviorMap
487494 . Where ( kvp => kvp . Value . Count > 0 )
488495 . Select ( kvp =>
489496 {
490- string markers =
497+ string markersSingular =
491498 kvp . Value . Count == 1 ? kvp . Value [ 0 ] : string . Join ( ", " , kvp . Value [ ..^ 1 ] ) + " and " + kvp . Value [ ^ 1 ] ;
492- markers = char . ToUpperInvariant ( markers [ 0 ] ) + markers [ 1 ..] ;
493- string behavior = kvp . Key switch
499+ string markersPlural =
500+ kvp . Value . Count == 1
501+ ? kvp . Value [ 0 ] + "s"
502+ : string . Join ( ", " , kvp . Value [ ..^ 1 ] + "s" ) + " and " + kvp . Value [ ^ 1 ] + "s" ;
503+ string sentence = kvp . Key switch
494504 {
495- PretranslationUsfmMarkerBehavior . Preserve => "were moved to the end of the verse" ,
496- PretranslationUsfmMarkerBehavior . PreservePosition => "have positions preserved" ,
497- PretranslationUsfmMarkerBehavior . Strip => "were removed" ,
498- _ => "have unknown behavior" ,
505+ PretranslationUsfmMarkerBehavior . Preserve =>
506+ $ "{ markersSingular } positions were moved to the end of the verse.",
507+ PretranslationUsfmMarkerBehavior . PreservePosition => $ "{ markersSingular } positions were preserved.",
508+ PretranslationUsfmMarkerBehavior . Strip => $ "{ markersPlural } were removed.",
509+ _ => $ "{ markersPlural } have unknown behavior.",
499510 } ;
500- return $ " { markers } { behavior } ." ;
511+ return char . ToUpperInvariant ( sentence [ 0 ] ) + sentence [ 1 .. ] ;
501512 } ) ;
502513
503514 return string . Join ( " " , sentences ) ;
0 commit comments