← all demos
xsl:stylesheet(
    default-mode="m:docbook",
    exclude-result-prefixes="#all",
    version="3.0",
    xmlns="http://www.w3.org/1999/xhtml",
    xmlns:db="http://docbook.org/ns/docbook",
    xmlns:f="http://docbook.org/ns/docbook/functions",
    xmlns:fp="http://docbook.org/ns/docbook/functions/private",
    xmlns:h="http://www.w3.org/1999/xhtml",
    xmlns:l="http://docbook.org/ns/docbook/l10n",
    xmlns:lt="http://docbook.org/ns/docbook/l10n/templates",
    xmlns:m="http://docbook.org/ns/docbook/modes",
    xmlns:map="http://www.w3.org/2005/xpath-functions/map",
    xmlns:mp="http://docbook.org/ns/docbook/modes/private",
    xmlns:tp="http://docbook.org/ns/docbook/templates/private",
    xmlns:v="http://docbook.org/ns/docbook/variables",
    xmlns:vp="http://docbook.org/ns/docbook/variables/private",
    xmlns:xs="http://www.w3.org/2001/XMLSchema",
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform")
  match l:template(context as element(), label as item()*, content as item()*) (
      mode="mp:localization"):
    apply *
  match lt:text (mode="mp:localization"):
    <-- node()
  match lt:separator(context as element()) (mode="mp:localization"):
    span(class="sep")
      apply $context
  match lt:percent (mode="mp:localization"):
    "%"
  match lt:label(label as item()*) (mode="mp:localization"):
    span(class="label")
      <-- $label
  match lt:content(content as item()*) (mode="mp:localization"):
    xsl:try
      <-- $content
      xsl:catch(errors="err:XTDE0410", xmlns:err="http://www.w3.org/2005/xqt-errors")
        span as element() :=
          span
            <-- $content
        <-- $span/node()
  match lt:ref(context as element()) (mode="mp:localization"):
    ref as element()? :=
      xsl:evaluate(context-item="$context", xpath="@select")
    apply $ref
  match lt:token(context as element(), lang) (mode="mp:localization"):
    <-- f:l10n-token($context, $lang, @key)
  function f:l10n-token(context as element(), key as string) -> item()* (cache="yes"):
    lang := f:l10n-language($context)
    <-- fp:l10n-token($lang, $key)
  function f:l10n-token(context as element(), lang as string, key as string) -> item()* (
      cache="yes"):
    <-- fp:l10n-token($lang, $key)
  function fp:l10n-token(lang as string, key as string) -> item()* (cache="yes"):
    l10n := fp:localization($lang)
    choose:
      when exists($l10n/l:gentext/l:token[@key=$key]):
        <-- ($l10n/l:gentext/l:token[@key=$key])[1]/node()
      when $lang != 'en':
        l10n := fp:localization('en')
        choose:
          when exists($l10n/l:gentext/l:token[@key=$key]):
            <-- ($l10n/l:gentext/l:token[@key=$key])[1]/node()
          else:
            xsl:message(select="'No gentext for ' || $key || ' in ' || $lang || ' or en'")
            "MISSING"
      else:
        xsl:message(select="'No gentext for ' || $key || ' in ' || $lang")
        "MISSING"
  match h:* (mode="mp:localization"):
    element {local-name(.)}:
      copy @*
      apply
  match lt:pagenum (mode="mp:localization"):
    <-- .
  match lt:* (mode="mp:localization"):
    xsl:message(select="'Unexpected localization element: ' || node-name(.)", terminate="yes")
  match *(list as element()+, name := local-name(.)) (mode="m:gentext-list"):
    template := fp:localization-list(., $name)
    items as element(l:items)? :=
      xsl:iterate(select="$template/l:items")
        choose:
          when count(.//lt:content) = count($list):
            xsl:break(select=".")
          when count(.//lt:content) le count($list) and .//l:repeat:
            xsl:break(select=".")
          else:
            xsl:next-iteration
    if empty($items):
      xsl:message(expand-text="yes")
        "Failed to select items for {count($list)} element list"
        "in {local-name(.)}."
    choose:
      when empty($items/l:repeat):
        call tp:process-list:
          context := .
          lang := f:l10n-language(.)
          list := $list
          template := $items/*
      else:
        before := $items/l:repeat/preceding-sibling::*
        after := $items/l:repeat/following-sibling::*
        bc := count($before/ancestor-or-self::lt:content)
        ac := count($after/ancestor-or-self::lt:content)
        rt := count($items/l:repeat//lt:content)
        ric := count($list) - ($ac + $bc)
        rc := ($ric + $rt - 1) idiv $rt
        expanded-list as element()+ :=
          <-- $before
          foreach 1 to $rc:
            <-- $items/l:repeat/*
          <-- $after
        call tp:process-list:
          context := .
          lang := f:l10n-language(.)
          list := $list
          template := $expanded-list
  template tp:process-list:
    param context as element()
    param lang as string
    param list as element()*
    param template as element()*
    xsl:iterate(select="$template")
      param list := $list
      choose:
        when self::lt:text:
          <-- node()
          xsl:next-iteration
            list := $list
        when self::lt:content:
          <-- $list[1]
          xsl:next-iteration
            list := $list[position() gt 1]
        when self::lt:token:
          <-- f:l10n-token($context, $lang, @key)
          xsl:next-iteration
            list := $list
        else:
          xsl:message(select="'Unexpected element in list template:', node-name(.)")
          xsl:next-iteration
            list := $list