Skip to content

List consecutive translate books rather than ranges#3700

Merged
RaymondLuong3 merged 3 commits intomasterfrom
fix/summarize-books
Feb 20, 2026
Merged

List consecutive translate books rather than ranges#3700
RaymondLuong3 merged 3 commits intomasterfrom
fix/summarize-books

Conversation

@RaymondLuong3
Copy link
Copy Markdown
Collaborator

@RaymondLuong3 RaymondLuong3 commented Feb 19, 2026

Testers found that on the summary page the books were listed as ranges in the training step but the translate step books were listed individually. This may be on purpose to make it explicit which books were being drafted. However, the draft history view shows the books as ranges also. This PR makes it more consistent to show books are ranges when they are consecutive.

I also refactored the code to use the i18n.service to localize and format books as ranges. I labeled this as testing not required since it only affect displaying the elements on the page which can be confirmed easily.

Before
Drafting summary before

After
Drafting summary after


This change is Reviewable


Open with Devin

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 19, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.73%. Comparing base (595481b) to head (bfd0af3).
⚠️ Report is 2 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ate/draft-generation/draft-generation.component.ts 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3700      +/-   ##
==========================================
- Coverage   81.74%   81.73%   -0.01%     
==========================================
  Files         619      619              
  Lines       38663    38651      -12     
  Branches     6297     6312      +15     
==========================================
- Hits        31606    31593      -13     
+ Misses       6096     6084      -12     
- Partials      961      974      +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Nateowami
Copy link
Copy Markdown
Collaborator

This may be on purpose to make it explicit which books were being drafted.

Yes, this was intentional, as it should be a most a handful of books, not a large number like with training data.

the draft history view shows the books as ranges also

I think that should be changed.

Copy link
Copy Markdown
Collaborator Author

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I agree, that seems to be the right approach

I think that should be changed.
I see why it was implemented that way. It gets the data from the ScriptureRange. There is a ScriptureRangeParser from SIL.Scripture namespace but that has not been ported to typescript. I am going to leave this as-is since it has such a small impact overall and not worth the work.

@RaymondLuong3 made 1 comment.
Reviewable status: 0 of 5 files reviewed, all discussions resolved.

Copy link
Copy Markdown
Collaborator Author

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, formatting got messed up

@RaymondLuong3 made 1 comment.
Reviewable status: 0 of 5 files reviewed, all discussions resolved.

Copy link
Copy Markdown
Collaborator Author

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it looks like in other parts of SF we assume that the scripture range will just be the book IDs separated by semi-colons. So it is possible to show the translation books individually based on a scripture range. It would be better to port the ScriptureRangeParser into typescript but not sure if we will prioritize that.

@RaymondLuong3 made 1 comment.
Reviewable status: 0 of 9 files reviewed, all discussions resolved.

@marksvc marksvc assigned marksvc and unassigned marksvc Feb 19, 2026
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +355 to +357
getScriptureRangeAsLocalizedBooks(scriptureRange: string): string {
return this.i18n.enumerateList(booksFromScriptureRange(scriptureRange).map(b => this.i18n.localizeBook(b)));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 getScriptureRangeAsLocalizedBooks does not sort books, causing non-canonical display order

The new getScriptureRangeAsLocalizedBooks method in draft-history-entry.component.ts:355-357 does not sort book numbers before localizing and enumerating them. The old code used i18n.formatAndLocalizeScriptureRange, which explicitly sorted books by canonical order (i18n.service.ts:244: .sort((a, b) => a - b)). The new code calls booksFromScriptureRange which returns books in whatever order they appear in the input string — no sorting is performed.

Root Cause

The _scriptureRange is built at draft-history-entry.component.ts:158 by joining multiple translationScriptureRanges items' scriptureRange values with ;. If the API returns ranges in non-canonical order (e.g., "EXO;GEN"), booksFromScriptureRange at utils.ts:172-178 simply splits on ; and maps to book numbers without sorting.

For example, with a scripture range of "EXO;GEN", the old code would display "Genesis - Exodus" (sorted and grouped), while the new code displays "Exodus and Genesis" (unsorted).

Impact: Draft history entries may display translation books in an arbitrary, non-canonical order, which is a regression from previous behavior and confusing for users.

Suggested change
getScriptureRangeAsLocalizedBooks(scriptureRange: string): string {
return this.i18n.enumerateList(booksFromScriptureRange(scriptureRange).map(b => this.i18n.localizeBook(b)));
}
getScriptureRangeAsLocalizedBooks(scriptureRange: string): string {
return this.i18n.enumerateList(
booksFromScriptureRange(scriptureRange)
.sort((a, b) => a - b)
.map(b => this.i18n.localizeBook(b))
);
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I'm not sure we need to be concerned about this.

📌 Just saying.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(It looks like my comment was not shown in context, which is a concern Devin raised about sorting. I was expressing that I'm not sure we need to be concerned about what Devin had raised there.)

Comment on lines +410 to 414
return this.i18n.enumerateList(
booksFromScriptureRange(
job.additionalInfo.translationScriptureRanges.map(item => item.scriptureRange).join(';')
).map(b => this.i18n.localizeBook(b))
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 getTranslationScriptureRange does not sort books, causing non-canonical display order

The new getTranslationScriptureRange method in draft-generation.component.ts:408-415 does not sort book numbers before localizing and enumerating them. The old code called i18n.formatAndLocalizeScriptureRange, which sorted books canonically before display. The new code passes booksFromScriptureRange(...) output directly to localizeBook and enumerateList without sorting.

Root Cause

The booksFromScriptureRange utility at utils.ts:172-178 returns book numbers in the order they appear in the semicolon-delimited input string. The old formatAndLocalizeScriptureRange at i18n.service.ts:242-244 applied .sort((a, b) => a - b) before processing, but the replacement code omits this step.

For a translationScriptureRanges value like [{scriptureRange: 'LEV'}, {scriptureRange: 'GEN'}], the joined string "LEV;GEN" would display as "Leviticus and Genesis" instead of "Genesis and Leviticus".

Impact: The in-progress draft card title may display translation books in an arbitrary, non-canonical order, which is a regression from previous behavior.

Suggested change
return this.i18n.enumerateList(
booksFromScriptureRange(
job.additionalInfo.translationScriptureRanges.map(item => item.scriptureRange).join(';')
).map(b => this.i18n.localizeBook(b))
);
return this.i18n.enumerateList(
booksFromScriptureRange(
job.additionalInfo.translationScriptureRanges.map(item => item.scriptureRange).join(';')
)
.sort((a, b) => a - b)
.map(b => this.i18n.localizeBook(b))
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Collaborator

marksvc commented Feb 20, 2026

I was confused from the discussion. But I think it makes sense if

This PR makes it more consistent to show books are ranges when they are consecutive.

was actually a mistake and what was meant was

This PR makes it more consistent to list books individually, even when they are consecutive.

}

getScriptureRangeAsLocalizedBooks(scriptureRange: string): string {
return this.i18n.enumerateList(booksFromScriptureRange(scriptureRange).map(b => this.i18n.localizeBook(b)));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. Though I notice that this converts from book ID strings, to book numbers, and back to book ID strings (before getting a localized string) in its process.

📌 Just saying.

Comment on lines +355 to +357
getScriptureRangeAsLocalizedBooks(scriptureRange: string): string {
return this.i18n.enumerateList(booksFromScriptureRange(scriptureRange).map(b => this.i18n.localizeBook(b)));
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I'm not sure we need to be concerned about this.

📌 Just saying.

Comment on lines +355 to +357
getScriptureRangeAsLocalizedBooks(scriptureRange: string): string {
return this.i18n.enumerateList(booksFromScriptureRange(scriptureRange).map(b => this.i18n.localizeBook(b)));
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(It looks like my comment was not shown in context, which is a concern Devin raised about sorting. I was expressing that I'm not sure we need to be concerned about what Devin had raised there.)

Copy link
Copy Markdown
Collaborator

@marksvc marksvc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not merge so you can consider the various non-blocking comments first.

@marksvc made 1 comment.
Reviewable status: 0 of 9 files reviewed, 3 unresolved discussions (waiting on RaymondLuong3).

Copy link
Copy Markdown
Collaborator Author

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comments. I am satisfied the discussions.

@RaymondLuong3 made 1 comment and resolved 3 discussions.
Reviewable status: 0 of 9 files reviewed, all discussions resolved (waiting on marksvc).

@RaymondLuong3 RaymondLuong3 changed the title Collapse consecutive translate books to ranges List consecutive translate books rather than ranges Feb 20, 2026
@Nateowami
Copy link
Copy Markdown
Collaborator

@RaymondLuong3 Is the PR title not describing the original behavior now?

@RaymondLuong3
Copy link
Copy Markdown
Collaborator Author

@Nateowami This is the original behaviour for the drafting summary step, but it was not the original for the draft history which is the change made in this PR.

@RaymondLuong3 RaymondLuong3 merged commit df721c0 into master Feb 20, 2026
20 of 22 checks passed
@RaymondLuong3 RaymondLuong3 deleted the fix/summarize-books branch February 20, 2026 20:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants