Add a citation help partial with corpus, KorAP and annotation parts

Add a citation help partial that lists how to cite a KorAP instance: the
corpus it serves, KorAP itself, and the annotation and classification
tools its data was prepared with. Instances include it wherever they
present citation information, e.g. on a documentation page.

It is assembled from three parts -- corpus, korap and annotation -- so
that an instance can replace individual parts and inherit the rest.
Kalamar ships no corpus of its own, so the corpus part is a placeholder
that instances replace by the citations for their corpus, while the
korap and annotation parts are normally inherited unchanged.

Every entry carries a COinS (Z3988) span, which lets reference managers
such as Zotero import it with one click. The section headings are
localized in kalamar.dict and kept free of corpus-specific names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Change-Id: Ib9ccc607fe5387027b8d151901d364f6589b62ac
diff --git a/t/citation.t b/t/citation.t
new file mode 100644
index 0000000..fc8c086
--- /dev/null
+++ b/t/citation.t
@@ -0,0 +1,26 @@
+use Mojo::Base -strict;
+use Test::More;
+use Test::Mojo;
+use Mojo::File qw/curfile/;
+
+my $t = Test::Mojo->new('Kalamar');
+push @{$t->app->renderer->paths}, curfile->dirname;
+
+my $c = $t->app->build_controller;
+
+# By default all three shipped parts are used; Kalamar ships no corpus, so
+# the corpus part is a placeholder.
+my $default = $c->include('partial/citation');
+like($default, qr/Under Construction/, 'corpus part is a placeholder by default');
+like($default, qr/KorAP architecture/, 'korap part is shipped');
+like($default, qr/MaltParser/, 'annotation part is shipped');
+
+# Each part can be replaced by an instance template.
+my $custom = $c->include('partial/citation',
+  corpus_citation => 'custom/partial/citation/corpus');
+like($custom, qr/The Example Corpus/, 'corpus part replaced by the instance template');
+unlike($custom, qr/Under Construction/, 'placeholder no longer rendered');
+like($custom, qr/KorAP architecture/, 'korap part still inherited');
+like($custom, qr/MaltParser/, 'annotation part still inherited');
+
+done_testing;
diff --git a/t/custom/partial/citation.html.ep b/t/custom/partial/citation.html.ep
index bc92d8f..d47c05d 100644
--- a/t/custom/partial/citation.html.ep
+++ b/t/custom/partial/citation.html.ep
@@ -1,5 +1,5 @@
 <section>
-  <h4><%= loc 'recentCorpusPub' %></h4>
+  <h4><%= loc 'corpusPub' %></h4>
 
   <p class="bibentry">IDS (2023): <cite><a href="https://www.ids-mannheim.de/digspra/kl/projekte/korpora/">Deutsches Referenzkorpus / Archiv der Korpora geschriebener Gegenwartssprache</a></cite> 2023-I (Release vom 31.03.2023).<br />Mannheim: Leibniz-Institut für Deutsche Sprache.
   </p>
@@ -18,7 +18,7 @@
 </section>
 
 <section>
-  <h4><%= loc 'recentToolPub' %></h4>
+  <h4><%= loc 'korapPub' %></h4>
 
   <p class="bibentry">Diewald, Nils/Bodmer, Franck/Harders, Peter/Irimia, Elena/Kupietz, Marc/Margaretha, Eliza/Stallkamp, Helge (2021): <cite><a class="extern" href="https://nbn-resolving.org/urn:nbn:de:bsz:mh39-103342">KorAP und EuReCo – Recherchieren in mehrsprachigen vergleichbaren Korpora</a></cite><br />In: Lobin, Henning/Witt, Andreas/Wöllstein, Angelika (Hrsg.): Deutsch in Europa. Sprachpolitisch, grammatisch, methodisch. Jahrbuch des Instituts für Deutsche Sprache 2020. (= Jahrbuch des Instituts für Deutsche Sprache 2020). Berlin/Boston: de Gruyter, 287-294.
     <span class='Z3988' title='url_ver=Z39.88-2004&amp;ctx_ver=Z39.88-2004&amp;rfr_id=info%3Asid%2Fzotero.org%3A2&amp;rft_id=urn%3Aisbn%3A978-3-11-073519-2&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=bookitem&amp;rft.atitle=KorAP%20und%20EuReCo%20%E2%80%93%20Recherchieren%20in%20mehrsprachigen%20vergleichbaren%20Korpora&amp;rft.btitle=Deutsch%20in%20Europa.%20Sprachpolitisch%2C%20grammatisch%2C%20methodisch.&amp;rft.place=Berlin%20%5Bu.a.%5D&amp;rft.publisher=de%20Gruyter&amp;rft.series=Jahrbuch%20%2F%20Leibniz-Institut%20f%C3%BCr%20Deutsche%20Sprache%20(IDS)%20-%202020%20-&amp;rft.aufirst=Nils&amp;rft.aulast=Diewald&amp;rft.au=Nils%20Diewald&amp;rft.au=Franck%20Bodmer&amp;rft.au=Peter%20Harders&amp;rft.au=Elena%20Irimia&amp;rft.au=Marc%20Kupietz&amp;rft.au=Eliza%20Margaretha&amp;rft.au=Helge%20Stallkamp&amp;rft.au=Henning%20Lobin&amp;rft.au=Andreas%20Witt&amp;rft.au=Angelika%20W%C3%B6llstein&amp;rft.date=2021&amp;rft.pages=287-293&amp;rft.spage=287&amp;rft.epage=293&amp;rft.isbn=978-3-11-073519-2'></span>
diff --git a/t/custom/partial/citation/corpus.html.ep b/t/custom/partial/citation/corpus.html.ep
new file mode 100644
index 0000000..54000b2
--- /dev/null
+++ b/t/custom/partial/citation/corpus.html.ep
@@ -0,0 +1,5 @@
+<section>
+  <h4><%= loc 'corpusPub' %></h4>
+
+  <p class="bibentry">Example Institute (2026): <cite>The Example Corpus</cite>.</p>
+</section>
diff --git a/t/page.t b/t/page.t
index 123a720..6edbe85 100644
--- a/t/page.t
+++ b/t/page.t
@@ -42,13 +42,16 @@
 # Test additions
 $t = Test::Mojo->new('Kalamar' => {
   'Localize' => {
+    # Behave like a real instance conf: let these entries override the
+    # defaults now shipped in kalamar.dict (corpusPub etc.).
+    override => 1,
     dict => {
       en_howToCite => 'Citation Help',
       de_howToCite => 'Zitierhilfe',
-      en_recentCorpusPub => 'Recent publications to refer to DeReKo as linguistic research data',
-      de_recentCorpusPub => 'Neuere Publikationen zu DeReKo als linguistische Forschungsdatengrundlage',
-      en_recentToolPub => 'Recent publications to refer to KorAP as a research tool',
-      de_recentToolPub => 'Neuere Publikationen zu KorAP als Forschungswerkzeug',
+      en_corpusPub => 'Recent publications to refer to DeReKo as linguistic research data',
+      de_corpusPub => 'Neuere Publikationen zu DeReKo als linguistische Forschungsdatengrundlage',
+      en_korapPub => 'Recent publications to refer to KorAP as a research tool',
+      de_korapPub => 'Neuere Publikationen zu KorAP als Forschungswerkzeug',
       title_addon => 'Test',
     }
   },