2222use anyhow:: { anyhow, Context } ;
2323use mdbook:: renderer:: RenderContext ;
2424use mdbook:: BookItem ;
25- use mdbook_i18n_helpers:: extract_messages;
25+ use mdbook_i18n_helpers:: { extract_events , extract_messages, reconstruct_markdown } ;
2626use polib:: catalog:: Catalog ;
2727use polib:: message:: Message ;
2828use polib:: metadata:: CatalogMetadata ;
29+ use pulldown_cmark:: { Event , Tag } ;
2930use std:: { fs, io} ;
3031
32+ /// Strip an optional link from a Markdown string.
33+ fn strip_link ( text : & str ) -> String {
34+ let events = extract_events ( text, None )
35+ . into_iter ( )
36+ . filter_map ( |( _, event) | match event {
37+ Event :: Start ( Tag :: Link ( ..) ) => None ,
38+ Event :: End ( Tag :: Link ( ..) ) => None ,
39+ _ => Some ( ( 0 , event) ) ,
40+ } )
41+ . collect :: < Vec < _ > > ( ) ;
42+ let ( without_link, _) = reconstruct_markdown ( & events, None ) ;
43+ without_link
44+ }
45+
3146fn add_message ( catalog : & mut Catalog , msgid : & str , source : & str ) {
3247 let wrap_options = textwrap:: Options :: new ( 76 )
3348 . break_words ( false )
@@ -59,31 +74,17 @@ fn create_catalog(ctx: &RenderContext) -> anyhow::Result<Catalog> {
5974 let mut catalog = Catalog :: new ( metadata) ;
6075
6176 // First, add all chapter names and part titles from SUMMARY.md.
62- // The book items are in order of the summary, so we can assign
63- // correct line numbers for duplicate lines by tracking the index
64- // of our last search.
6577 let summary_path = ctx. config . book . src . join ( "SUMMARY.md" ) ;
6678 let summary = std:: fs:: read_to_string ( ctx. root . join ( & summary_path) )
6779 . with_context ( || anyhow ! ( "Failed to read {}" , summary_path. display( ) ) ) ?;
68- let mut last_idx = 0 ;
69- for item in ctx. book . iter ( ) {
70- let line = match item {
71- BookItem :: Chapter ( chapter) => & chapter. name ,
72- BookItem :: PartTitle ( title) => title,
73- BookItem :: Separator => continue ,
74- } ;
75-
76- let idx = summary[ last_idx..] . find ( line) . ok_or_else ( || {
77- anyhow ! (
78- "Could not find {line:?} in SUMMARY.md after line {} -- \
79- please remove any formatting from SUMMARY.md",
80- summary[ ..last_idx] . lines( ) . count( )
81- )
82- } ) ?;
83- last_idx += idx;
84- let lineno = summary[ ..last_idx] . lines ( ) . count ( ) ;
80+ for ( lineno, msgid) in extract_messages ( & summary) {
8581 let source = format ! ( "{}:{}" , summary_path. display( ) , lineno) ;
86- add_message ( & mut catalog, line, & source) ;
82+ // The summary is mostly links like "[Foo *Bar*](foo-bar.md)".
83+ // We strip away the link to get "Foo *Bar*". The formatting
84+ // is stripped away by mdbook when it sends the book to
85+ // mdbook-gettext -- we keep the formatting here in case the
86+ // same text is used for the page title.
87+ add_message ( & mut catalog, & strip_link ( & msgid) , & source) ;
8788 }
8889
8990 // Next, we add the chapter contents.
@@ -147,6 +148,22 @@ mod tests {
147148 Ok ( ( ctx, tmpdir) )
148149 }
149150
151+ #[ test]
152+ fn test_strip_link_empty ( ) {
153+ assert_eq ! ( strip_link( "" ) , "" ) ;
154+ }
155+
156+ #[ test]
157+ fn test_strip_link_text ( ) {
158+ assert_eq ! ( strip_link( "Summary" ) , "Summary" ) ;
159+ }
160+
161+ #[ test]
162+ fn test_strip_link_with_formatting ( ) {
163+ // The formatting is automatically normalized.
164+ assert_eq ! ( strip_link( "[foo *bar* `baz`](foo.md)" ) , "foo _bar_ `baz`" ) ;
165+ }
166+
150167 #[ test]
151168 fn test_create_catalog_defaults ( ) -> anyhow:: Result < ( ) > {
152169 let ( ctx, _tmp) =
@@ -183,29 +200,63 @@ mod tests {
183200
184201 #[ test]
185202 fn test_create_catalog_summary_formatting ( ) -> anyhow:: Result < ( ) > {
186- // It is an error to include formatting in the summary file:
187- // it is stripped by mdbook and we cannot find it later when
188- // trying to translate the book.
189203 let ( ctx, _tmp) = create_render_context ( & [
190204 ( "book.toml" , "[book]" ) ,
191- ( "src/SUMMARY.md" , "- [foo *bar* baz]()" ) ,
205+ (
206+ "src/SUMMARY.md" ,
207+ "# Summary\n \
208+ \n \
209+ [Prefix Chapter](prefix.md)\n \
210+ \n \
211+ # Part Title\n \
212+ \n \
213+ - [Foo *Bar*](foo.md)\n \
214+ \n \
215+ ----------\n \
216+ \n \
217+ - [Baz `Quux`](baz.md)\n \
218+ \n \
219+ [Suffix Chapter](suffix.md)",
220+ ) ,
221+ // Without this, mdbook would automatically create the
222+ // files based on the summary above. This would add
223+ // unnecessary headings below.
224+ ( "src/prefix.md" , "" ) ,
225+ ( "src/foo.md" , "" ) ,
226+ ( "src/baz.md" , "" ) ,
227+ ( "src/suffix.md" , "" ) ,
192228 ] ) ?;
193229
194- assert ! ( create_catalog( & ctx) . is_err( ) ) ;
230+ let catalog = create_catalog ( & ctx) ?;
231+ assert_eq ! (
232+ catalog
233+ . messages( )
234+ . map( |msg| msg. msgid( ) )
235+ . collect:: <Vec <& str >>( ) ,
236+ & [
237+ "Summary" ,
238+ "Prefix Chapter" ,
239+ "Part Title" ,
240+ "Foo _Bar_" ,
241+ "Baz `Quux`" ,
242+ "Suffix Chapter" ,
243+ ]
244+ ) ;
245+
195246 Ok ( ( ) )
196247 }
197248
198249 #[ test]
199250 fn test_create_catalog ( ) -> anyhow:: Result < ( ) > {
200251 let ( ctx, _tmp) = create_render_context ( & [
201252 ( "book.toml" , "[book]" ) ,
202- ( "src/SUMMARY.md" , "- [The Foo Chapter](foo.md)" ) ,
253+ ( "src/SUMMARY.md" , "- [The * Foo* Chapter](foo.md)" ) ,
203254 (
204255 "src/foo.md" ,
205256 "# How to Foo\n \
206257 \n \
207- The first paragraph about Foo .\n \
208- Still the first paragraph.\n ",
258+ First paragraph.\n \
259+ Same paragraph.\n ",
209260 ) ,
210261 ] ) ?;
211262
@@ -218,12 +269,12 @@ mod tests {
218269 assert_eq ! (
219270 catalog
220271 . messages( )
221- . map( |msg| msg. msgid( ) )
222- . collect:: <Vec <& str >>( ) ,
272+ . map( |msg| ( msg. source ( ) , msg . msgid( ) ) )
273+ . collect:: <Vec <_ >>( ) ,
223274 & [
224- " The Foo Chapter",
225- " How to Foo",
226- "The first paragraph about Foo. Still the first paragraph.",
275+ ( "src/SUMMARY.md:1" , " The _Foo_ Chapter") ,
276+ ( "src/foo.md:1" , " How to Foo") ,
277+ ( "src/foo.md:3" , "First paragraph. Same paragraph.") ,
227278 ]
228279 ) ;
229280
0 commit comments