diff --git a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Layout.kt b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Layout.kt index e8814354..4965a051 100644 --- a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Layout.kt +++ b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Layout.kt @@ -1,8 +1,10 @@ package eu.iamgio.quarkdown.stdlib +import eu.iamgio.quarkdown.ast.InlineContent import eu.iamgio.quarkdown.ast.InlineMarkdownContent import eu.iamgio.quarkdown.ast.MarkdownContent import eu.iamgio.quarkdown.ast.base.block.Table +import eu.iamgio.quarkdown.ast.dsl.buildInline import eu.iamgio.quarkdown.ast.quarkdown.block.Aligned import eu.iamgio.quarkdown.ast.quarkdown.block.Box import eu.iamgio.quarkdown.ast.quarkdown.block.Clipped @@ -187,7 +189,9 @@ fun clip( /** * Inserts content in a box. - * @param title box title. If unset, the box is untitled + * @param title box title. If unset: + * - If the locale ([docLanguage]) is set and supported, the title is localized according to the box [type] + * - Otherwise, the box is untitled * @param type box type. If unset, it defaults to a callout box * @param padding padding around the box. If unset, the box uses the default padding * @param backgroundColor background color. If unset, the box uses the default color @@ -196,13 +200,30 @@ fun clip( * @return the new box node */ fun box( + @Injected context: Context, title: InlineMarkdownContent? = null, type: Box.Type = Box.Type.CALLOUT, padding: Size? = null, @Name("background") backgroundColor: Color? = null, @Name("foreground") foregroundColor: Color? = null, body: MarkdownContent, -) = Box(title?.children, type, padding, backgroundColor, foregroundColor, body.children).wrappedAsValue() +): NodeValue { + // Localizes the title according to the box type, + // if the title is not manually set. + fun localizedTitle(): InlineContent? = + Stdlib.localizeOrNull(type.name, context)?.let { + buildInline { text(it) } + } + + return Box( + title?.children ?: localizedTitle(), + type, + padding, + backgroundColor, + foregroundColor, + body.children, + ).wrappedAsValue() +} /** * Inserts content in a collapsible block, whose content can be hidden or shown by interacting with it. diff --git a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Stdlib.kt b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Stdlib.kt index 3a00b286..a14c9d10 100644 --- a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Stdlib.kt +++ b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Stdlib.kt @@ -1,5 +1,7 @@ package eu.iamgio.quarkdown.stdlib +import eu.iamgio.quarkdown.context.Context +import eu.iamgio.quarkdown.context.localization.localizeOrNull import eu.iamgio.quarkdown.function.library.Library import eu.iamgio.quarkdown.function.library.LibraryExporter import eu.iamgio.quarkdown.function.library.loader.MultiFunctionLibraryLoader @@ -16,6 +18,11 @@ typealias Module = Set>> * Exporter of Quarkdown's standard library. */ object Stdlib : LibraryExporter { + /** + * The name of the localization table used by this library. + */ + private const val LOCALIZATION_TABLE = "std" + override val library: Library get() = MultiFunctionLibraryLoader(name = "stdlib").load( @@ -43,4 +50,15 @@ object Stdlib : LibraryExporter { }, ), ) + + /** + * Localizes a key from the stdlib table ([LOCALIZATION_TABLE]). + * @param key localization key + * @param context context to localize for + * @return the localized string if the [key] exists in the `std` table, `null` otherwise + */ + fun localizeOrNull( + key: String, + context: Context, + ): String? = context.localizeOrNull(LOCALIZATION_TABLE, key.lowercase()) } diff --git a/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt b/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt index e1b02bc4..e47af6da 100644 --- a/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt +++ b/test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt @@ -846,6 +846,81 @@ class FullPipelineTest { } } + @Test + fun boxes() { + execute(".box {Hello} \n\tHello, **world**!") { + assertEquals( + "

Hello

" + + "

Hello, world!

", + it, + ) + } + + execute(".box {Hello} type:{tip}\n\tHello, world!") { + assertEquals( + "

Hello

" + + "

Hello, world!

", + it, + ) + } + + execute(".box {Hello, *world*} type:{warning}\n\tHello, world!") { + assertEquals( + "

Hello, world

" + + "

Hello, world!

", + it, + ) + } + + execute(".box type:{error}\n\tHello, world!") { + assertEquals( + "
" + + "

Hello, world!

", + it, + ) + } + + // Localized title. + execute( + """ + .doclang {english} + .box type:{error} + Hello, world! + + .box type:{tip} + Hello, world! + + .box + Hello, world! + """.trimIndent(), + ) { + assertEquals( + "

Error

" + + "

Hello, world!

" + + "

Tip

" + + "

Hello, world!

" + + "
" + + "

Hello, world!

", + it, + ) + } + + // Unsupported localization. + execute( + """ + .doclang {japanese} + .box type:{warning} + Hello, world! + """.trimIndent(), + ) { + assertEquals( + "
" + + "

Hello, world!

", + it, + ) + } + } + @Test fun fibonacci() { // Iterative Fibonacci sequence calculation.