packages feed

citeproc 0.14.1 → 0.14.1.1

raw patch · 16 files changed

+918/−9 lines, 16 filesPVP: minor bump suggested

API additions: PVP suggests at least a minor version bump

API changes (from Hackage documentation)

+ Citeproc.Types: isByzantine :: Text -> Bool

Files

CHANGELOG.md view
@@ -1,5 +1,31 @@ # citeproc changelog +## 0.14.1.1++  * Group name-less cites only when the text before their dates matches+    (#184, Daniel Gregg).++  * Initialize given names in caseless romanesque scripts (#186,+    Daniel Gregg).++  * Do not initialize given names in non-romanesque scripts (#189,+    Daniel Gregg).++  * Render a literal date only where the date element shows the year+    (#188, Daniel Gregg).++  * Fix "et al.others" with et-al-use-last and a final "others" name+    (Don Yin).++  * A single number with digit grouping or a word takes the singular+    label (#185, Daniel Gregg).++  * Do not italicize text with no romanesque character (#190,+    Daniel Gregg).++  * Update stop words from canonical list upstream. This affects+    title case.+ ## 0.14.1    * Stop extracting particles from given and family names.
citeproc.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.2 name:                citeproc-version:             0.14.1+version:             0.14.1.1 synopsis:            Generates citations and bibliography from CSL styles. description:         citeproc parses CSL style files and uses them to                      generate a list of formatted citations and bibliography
src/Citeproc/CaseTransform.hs view
@@ -120,39 +120,113 @@     Just (c,x') -> Unicode.toUpper mblang (T.singleton c) <> x'     Nothing     -> x +-- Source: https://raw.githubusercontent.com/citation-style-language/schema/master/schemas/styles/stop-words.json .+-- We have omitted the ones containing spaces, like "ahead of". isStopWord :: Text -> Bool isStopWord "a" = True+isStopWord "across" = True+isStopWord "afore" = True+isStopWord "after" = True+isStopWord "against" = True+isStopWord "along" = True+isStopWord "alongside" = True+isStopWord "amid" = True+isStopWord "amidst" = True+isStopWord "among" = True+isStopWord "amongst" = True isStopWord "an" = True isStopWord "and" = True+isStopWord "anenst" = True+isStopWord "apropos" = True+isStopWord "apud" = True+isStopWord "around" = True isStopWord "as" = True+isStopWord "aside" = True+isStopWord "astride" = True isStopWord "at" = True+isStopWord "athwart" = True+isStopWord "atop" = True+isStopWord "barring" = True+isStopWord "before" = True+isStopWord "behind" = True+isStopWord "below" = True+isStopWord "beneath" = True+isStopWord "beside" = True+isStopWord "besides" = True+isStopWord "between" = True+isStopWord "beyond" = True isStopWord "but" = True isStopWord "by" = True+isStopWord "c" = True+isStopWord "ca" = True+isStopWord "circa" = True+isStopWord "close to" = True+isStopWord "d'" = True+isStopWord "de" = True+isStopWord "despite" = True isStopWord "down" = True+isStopWord "during" = True+isStopWord "et" = True+isStopWord "except" = True isStopWord "for" = True+isStopWord "forenenst" = True isStopWord "from" = True+isStopWord "given" = True isStopWord "in" = True+isStopWord "inside" = True isStopWord "into" = True+isStopWord "lest" = True+isStopWord "like" = True+isStopWord "modulo" = True+isStopWord "near" = True+isStopWord "next" = True isStopWord "nor" = True+isStopWord "notwithstanding" = True isStopWord "of" = True+isStopWord "off" = True isStopWord "on" = True isStopWord "onto" = True isStopWord "or" = True+isStopWord "out" = True isStopWord "over" = True+isStopWord "per" = True+isStopWord "plus" = True+isStopWord "pro" = True+isStopWord "qua" = True+isStopWord "sans" = True+isStopWord "since" = True isStopWord "so" = True+isStopWord "than" = True isStopWord "the" = True+isStopWord "through" = True+isStopWord "throughout" = True+isStopWord "thru" = True+isStopWord "thruout" = True isStopWord "till" = True isStopWord "to" = True+isStopWord "toward" = True+isStopWord "towards" = True+isStopWord "under" = True+isStopWord "underneath" = True+isStopWord "until" = True+isStopWord "unto" = True isStopWord "up" = True+isStopWord "upon" = True+isStopWord "v." = True+isStopWord "van" = True+isStopWord "versus" = True isStopWord "via" = True+isStopWord "vis-à-vis" = True+isStopWord "von" = True+isStopWord "vs." = True+isStopWord "where as" = True isStopWord "with" = True+isStopWord "within" = True+isStopWord "without" = True isStopWord "yet" = True -- not in original list but seems required by test flipflop_Apostrophes -- and textcase_SkipNameParticlesInTitleCase isStopWord "about" = True-isStopWord "van" = True-isStopWord "von" = True-isStopWord "de" = True isStopWord "d" = True isStopWord "l" = True isStopWord _ = False
src/Citeproc/Eval.hs view
@@ -901,6 +901,9 @@   , giHasPrefix :: Bool   , giHasSuffix :: Bool   , giTagged    :: Maybe (Output a)+  , giLead      :: Text+    -- ^ the text the item renders before its first date (prefix+    -- excluded); without names it stands in the names' place   }  groupAndCollapseCitations :: forall a . CiteprocOutput a@@ -937,6 +940,7 @@     , giHasPrefix = hasPrefix x     , giHasSuffix = hasSuffix x     , giTagged    = extractTagged x+    , giLead      = leadingText x     }    --   Note that we cannot assume we've sorted by name,@@ -1104,12 +1108,33 @@                            then not (null ns1) || ws1 == ws2                            else ws1 == ws2)       -- case where it's just a date with no name or anything;-      -- we treat this as same name e.g. (1955a,b)+      -- we treat this as same name e.g. (1955a,b), but only when+      -- whatever precedes the dates (e.g. a title standing in for+      -- the author) is the same       (Just (Tagged TagDate{} _), Just (Tagged TagDate{} _))-        -> True+        -> giLead x == giLead y           -- case where title is substituted       _ -> False +  -- the text an item renders before its first date, prefix excluded+  leadingText :: Output a -> Text+  leadingText = T.unwords . fst . leading+   where+    leading :: Output a -> ([Text], Bool)+    leading (Tagged TagDate{} _) = ([], True)+    leading (Tagged TagPrefix _) = ([], False)+    leading (Tagged _ y)         = leading y+    leading (Formatted _ ys)     = leadingList ys+    leading (Linked _ ys)        = leadingList ys+    leading (InNote y)           = leading y+    leading (Literal t)          = ([toText t], False)+    leading NullOutput           = ([], False)+    leadingList [] = ([], False)+    leadingList (y:ys) =+      case leading y of+        (ts, True)  -> (ts, True)+        (ts, False) -> let (us, d) = leadingList ys in (ts ++ us, d)+   extractTagged :: Output a -> Maybe (Output a)   extractTagged x =     let items = [y | y@(Tagged (TagItem ty _) _) <- universe x@@ -1905,6 +1930,33 @@ isSepPunct '\x2013' = True isSepPunct _   = False +-- | One number, possibly with a word around it ("US 9876543"): no+-- separator punctuation, no "&" or "and", and exactly one word that+-- holds a digit.  CSL pluralizes a label only for a range or several+-- numbers.+isOneIdentifier :: Text -> Bool+isOneIdentifier t =+  not (T.any isSepPunct t) &&+  not (T.any (== '&') t) &&+  "and" `notElem` ws &&+  length (filter (T.any isDigit) ws) == 1+ where+  ws = T.words t++-- | Drop the commas of thousands grouping ("9,876,543" -> "9876543"):+-- a comma between a digit and exactly three digits not followed by+-- another digit.+dropDigitGrouping :: Text -> Text+dropDigitGrouping = T.pack . go . T.unpack+ where+  go (a:',':b:c:d:rest)+    | isDigit a, isDigit b, isDigit c, isDigit d+    , not (startsWithDigit rest) = a : go (b:c:d:rest)+  go (x:xs) = x : go xs+  go [] = []+  startsWithDigit (e:_) = isDigit e+  startsWithDigit [] = False+ eLabel :: CiteprocOutput a        => Variable        -> TermForm@@ -1920,9 +1972,15 @@              | var == "number-of-volumes"              , t /= "1" && t /= "0"      = Plural              | "\\-" `T.isInfixOf` t     = Singular+             | isOneIdentifier (ungroup t) = Singular              | length (splitNums t) > 1  = Plural                -- see label_CollapsedPageNumberPluralDetection.txt              | otherwise                 = Singular+            -- a digit-grouped number ("US 9,876,543") is one number,+            -- not a list; pages and locators keep the comma-list reading+            ungroup t+             | var == "page" || var == "locator" = t+             | otherwise = dropDigitGrouping t         let number = case pluralize of                          AlwaysPluralize     -> Plural                          NeverPluralize      -> Singular@@ -2007,7 +2065,14 @@         return NullOutput       Just (DateVal date) ->         case dateLiteral date of-          Just t -> return $ formatted formatting' [Literal $ fromText t]+          -- a literal stands in for the year: a date element that+          -- prints only month or day leaves it out (as citeproc-js)+          Just t+            | null dps' || any ((== DPYear) . dpName) dps'+              -> return $ formatted formatting' [Literal $ fromText t]+            | otherwise -> do+              updateVarCount 0 (-1)+              return NullOutput           Nothing -> do             let dateparts = case dateSeason date of                               Just i  ->@@ -2441,6 +2506,7 @@             else etAl        | idx == numnames        , etAlUseLast+       , not finalNameIsOthers        , maybe False (idx - 1 >=) etAlThreshold          = name        | maybe False (idx - 1 >) etAlThreshold = NullOutput@@ -2577,6 +2643,8 @@                | isUpper c -> Unicode.toUpper mblang (T.singleton c)              _ -> mempty  -- e.g. Ji-ping -> J. not J.-p.          Just (c, t')+           -- a caseless romanesque script (Arabic, Hebrew, Thai)+           | isCaseless c -> T.singleton c            | isUpper c ->              case T.uncons t' of                Just (d, t'')@@ -2586,12 +2654,17 @@                  -> T.singleton c <> Unicode.toLower mblang (T.singleton d)                _ -> T.singleton c          _ -> t+   isCaseless c = isLetter c && not (isUpper c) && not (isLower c)+                  && isByzantine (T.singleton c)    initializeWord (Left t) -- Left values already initialized      = addSuffix t    initializeWord (Right t) -- Right values not already initialized      | T.all isLower t = if endsWithSpace initializeWith                             then t <> " "                             else " " <> t <> " "+       -- a word in a non-romanesque script (e.g. CJK) is not+       -- initialized, and takes no initialize-with suffix+     | not (isByzantine t) = t <> " "      | makeInitials    = (addSuffix . toInitial) t      | otherwise       = t <> " " 
src/Citeproc/Types.hs view
@@ -42,6 +42,7 @@   , Name(..)   , extractParticles   , isByzantineName+  , isByzantine   , DelimiterPrecedes(..)   , Condition(..)   , Position(..)@@ -1621,10 +1622,26 @@     anchor = mconcat (map outputToText xs)     prefix = fromMaybe "" (formatPrefix f) renderOutput opts locale (Formatted formatting xs) =-  addFormatting locale formatting . mconcat . fixPunct .+  addFormatting locale formatting' . mconcat . fixPunct .     (case formatDelimiter formatting of        Just d  -> addDelimiters (fromText d)        Nothing -> id) . filter (/= mempty) $ map (renderOutput opts locale) xs+  where+    -- italics/oblique set on an element that renders text directly+    -- are not applied when that text has no romanesque character+    -- (e.g. CJK), as citeproc-js does+    formatting'+      | formatFontStyle formatting `elem` [Just ItalicFont, Just ObliqueFont]+      , all isTextLeaf xs+      , txt <- mconcat (map outputToText xs)+      , T.any isLetter txt+      , not (isByzantine txt)+      = formatting{ formatFontStyle = Nothing }+      | otherwise = formatting+    -- only text rendered directly by this element, not a nested group+    isTextLeaf (Literal _)  = True+    isTextLeaf (Tagged _ y) = isTextLeaf y+    isTextLeaf _            = False renderOutput opts locale (Linked url xs)   = (if linkBibliography opts        then addHyperlink url
test/csl/flipflop_OrphanQuote.expected view
@@ -1,1 +1,1 @@-Nation of “Positive Obligations ” of State Under the European Convention on Human Rights (1)+Nation of “Positive Obligations ” of State under the European Convention on Human Rights (1)
+ test/csl/textcase_StopWordBeforeHyphen.expected view
@@ -0,0 +1,1 @@+Employee pro-Environmental Behavior
+ test/extra/collapse_NamelessCitesWithDifferentTitlesDoNotGroup.txt view
@@ -0,0 +1,52 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<+++>>===== RESULT =====>>+(Smith v Jones 1998; Water Act 2007)+<<===== RESULT =====<<+++>>===== CSL =====>>+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0">+  <info>+    <id />+    <title />+    <updated>2026-10-11T00:00:00+00:00</updated>+  </info>+  <citation collapse="year">+    <layout prefix="(" suffix=")" delimiter="; ">+      <group delimiter=" ">+        <text variable="title"/>+        <date variable="issued">+          <date-part name="year"/>+        </date>+      </group>+    </layout>+  </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {+    "id": "ITEM-1",+    "type": "legal_case",+    "title": "Smith v Jones",+    "issued": {"date-parts": [[1998]]}+  },+  {+    "id": "ITEM-2",+    "type": "legislation",+    "title": "Water Act",+    "issued": {"date-parts": [[2007]]}+  }+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/extra/collapse_TmpRealStyle.txt view
@@ -0,0 +1,285 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<+++>>===== RESULT =====>>+(Hennelly et al., 2023, 2025)+<<===== RESULT =====<<+++>>===== CSL =====>>+<?xml version="1.0" encoding="utf-8"?>+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0" demote-non-dropping-particle="never" default-locale="en-US">+  <info>+    <title>Elsevier (author-date/Harvard, with titles)</title>+    <id>http://www.zotero.org/styles/elsevier-harvard</id>+    <link href="http://www.zotero.org/styles/elsevier-harvard" rel="self"/>+    <link href="http://www.zotero.org/styles/ecology-letters" rel="template"/>+    <link href="http://www.elsevier.com/journals/biological-conservation/0006-3207/guide-for-authors#68000" rel="documentation"/>+    <author>+      <name>David Kaplan</name>+      <email>david.kaplan@ird.fr</email>+    </author>+    <contributor>+      <name>Simon Kornblith</name>+      <email>simon@simonster.com</email>+    </contributor>+    <contributor>+      <name>Bruce D'Arcus</name>+    </contributor>+    <contributor>+      <name>Curtis M. Humphrey</name>+    </contributor>+    <contributor>+      <name>Richard Karnesky</name>+      <email>karnesky+zotero@gmail.com</email>+      <uri>http://arc.nucapt.northwestern.edu/Richard_Karnesky</uri>+    </contributor>+    <contributor>+      <name>Sebastian Karcher</name>+    </contributor>+    <category citation-format="author-date"/>+    <category field="biology"/>+    <updated>2014-03-04T00:09:00+00:00</updated>+    <rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>+  </info>+  <macro name="container">+    <choose>+      <if type="chapter paper-conference" match="any">+        <text term="in" prefix=", " suffix=": "/>+        <names variable="editor translator" delimiter=", " suffix=", ">+          <name name-as-sort-order="all" sort-separator=", " initialize-with="." delimiter=", " delimiter-precedes-last="always"/>+          <label form="short" text-case="capitalize-first" prefix=" (" suffix=")"/>+        </names>+        <group delimiter=", ">+          <text variable="container-title" text-case="title"/>+          <text variable="collection-title" text-case="title"/>+        </group>+      </if>+      <else-if type="bill book graphic legal_case legislation motion_picture report song" match="any">+        <group prefix=", " delimiter=", ">+          <text variable="container-title"/>+          <text variable="collection-title"/>+        </group>+      </else-if>+      <else>+        <group prefix=". " delimiter=", ">+          <text variable="container-title" form="short"/>+          <text variable="collection-title"/>+        </group>+      </else>+    </choose>+  </macro>+  <macro name="author">+    <names variable="author">+      <name name-as-sort-order="all" sort-separator=", " initialize-with="." delimiter=", " delimiter-precedes-last="always"/>+      <label form="short" prefix=" (" suffix=")" text-case="capitalize-first"/>+      <substitute>+        <names variable="editor"/>+        <names variable="translator"/>+        <text macro="title"/>+      </substitute>+    </names>+  </macro>+  <macro name="author-short">+    <names variable="author">+      <name form="short" and="text" delimiter=", " initialize-with=". "/>+      <substitute>+        <names variable="editor"/>+        <names variable="translator"/>+        <choose>+          <if type="bill book graphic legal_case legislation motion_picture report song" match="any">+            <text variable="title" form="short" font-style="italic"/>+          </if>+          <else>+            <text variable="title" form="short" quotes="true"/>+          </else>+        </choose>+      </substitute>+    </names>+  </macro>+  <macro name="access">+    <choose>+      <if variable="DOI">+        <text variable="DOI" prefix="https://doi.org/"/>+      </if>+      <else-if type="webpage post-weblog" match="any">+        <group delimiter=" ">+          <text value="URL"/>+          <text variable="URL"/>+          <group prefix="(" suffix=").">+            <text term="accessed" suffix=" "/>+            <date variable="accessed">+              <date-part name="month" form="numeric" suffix="."/>+              <date-part name="day" suffix="."/>+              <date-part name="year" form="short"/>+            </date>+          </group>+        </group>+      </else-if>+    </choose>+  </macro>+  <macro name="title">+    <choose>+      <if type="report thesis" match="any">+        <text variable="title"/>+        <group prefix=" (" suffix=")" delimiter=" ">+          <text variable="genre"/>+          <text variable="number" prefix="No. "/>+        </group>+      </if>+      <else-if type="bill book graphic legal_case legislation motion_picture report song speech" match="any">+        <text variable="title"/>+        <text macro="edition" prefix=", "/>+      </else-if>+      <else-if type="webpage">+        <text variable="title"/>+        <text value="WWW Document" prefix=" [" suffix="]"/>+      </else-if>+      <else>+        <text variable="title"/>+      </else>+    </choose>+  </macro>+  <macro name="publisher">+    <group delimiter=", ">+      <text variable="publisher"/>+      <text variable="publisher-place"/>+    </group>+  </macro>+  <macro name="event">+    <choose>+      <if variable="event">+        <text term="presented at" text-case="capitalize-first" suffix=" "/>+        <text variable="event"/>+      </if>+    </choose>+  </macro>+  <macro name="issued">+    <choose>+      <if variable="issued">+        <date variable="issued">+          <date-part name="year"/>+        </date>+      </if>+      <else>+        <text term="no date" form="short"/>+      </else>+    </choose>+  </macro>+  <macro name="edition">+    <group delimiter=" ">+      <choose>+        <if is-numeric="edition">+          <number variable="edition" form="ordinal"/>+        </if>+        <else>+          <text variable="edition" suffix="."/>+        </else>+      </choose>+      <text value="ed"/>+    </group>+  </macro>+  <macro name="locators">+    <choose>+      <if type="article-journal article-magazine article-newspaper" match="any">+        <group prefix=" " delimiter=", ">+          <group>+            <text variable="volume"/>+          </group>+          <text variable="page"/>+        </group>+      </if>+      <else-if type="bill book graphic legal_case legislation motion_picture report song thesis" match="any">+        <group delimiter=", " prefix=". ">+          <text macro="event"/>+          <text macro="publisher"/>+        </group>+      </else-if>+      <else-if type="chapter paper-conference" match="any">+        <group delimiter=", " prefix=". ">+          <text macro="event"/>+          <text macro="publisher"/>+          <group>+            <label variable="page" form="short" suffix=" "/>+            <text variable="page"/>+          </group>+        </group>+      </else-if>+      <else-if type="patent">+        <text variable="number" prefix=". "/>+      </else-if>+    </choose>+  </macro>+  <citation et-al-min="3" et-al-use-first="1" disambiguate-add-givenname="true" disambiguate-add-year-suffix="true" collapse="year" cite-group-delimiter=", ">+    <sort>+      <key macro="author" names-min="3" names-use-first="1"/>+      <key macro="issued"/>+    </sort>+    <layout prefix="(" suffix=")" delimiter="; ">+      <group delimiter=", ">+        <text macro="author-short"/>+        <text macro="issued"/>+        <group delimiter=" ">+          <label variable="locator" form="short"/>+          <text variable="locator"/>+        </group>+      </group>+    </layout>+  </citation>+  <bibliography hanging-indent="true" entry-spacing="0" line-spacing="1">+    <sort>+      <key macro="author" names-min="3" names-use-first="1"/>+      <key macro="issued"/>+    </sort>+    <layout>+      <group suffix=".">+        <text macro="author" suffix=","/>+        <text macro="issued" prefix=" "/>+        <group prefix=". ">+          <text macro="title"/>+          <text macro="container"/>+          <text macro="locators"/>+        </group>+      </group>+      <text macro="access" prefix=". "/>+    </layout>+  </bibliography>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {+    "id": "hennelly2023",+    "type": "article-journal",+    "author": [+      {"family": "Hennelly", "given": "Lauren M."},+      {"family": "Sarwar", "given": "Ghulam"},+      {"family": "Fatima", "given": "Hira"}+    ],+    "title": "Genomic analysis of wolves from Pakistan",+    "container-title": "Journal of Heredity",+    "issued": {"date-parts": [[2023]]}+  },+  {+    "id": "hennelly2025",+    "type": "article-journal",+    "author": [+      {"family": "Hennelly", "given": "Lauren M."},+      {"family": "Parreira", "given": "Bárbara R."},+      {"family": "Noble", "given": "Ash"}+    ],+    "title": "Continent-wide view of genomic diversity in the wolves of Asia",+    "container-title": "Communications Biology",+    "issued": {"date-parts": [[2025]]}+  }+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/extra/collapse_YearWithIdenticalEtAlNames.txt view
@@ -0,0 +1,76 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<+++>>===== RESULT =====>>+(Hennelly et al., 2023, 2025)+<<===== RESULT =====<<+++>>===== CSL =====>>+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0">+  <info>+    <id />+    <title />+    <updated>2026-10-21T00:00:00+00:00</updated>+  </info>+  <macro name="author">+    <names variable="author">+      <name form="short" and="text" delimiter=", " delimiter-precedes-et-al="never"/>+    </names>+  </macro>+  <macro name="issued">+    <date variable="issued">+      <date-part name="year"/>+    </date>+  </macro>+  <citation et-al-min="3" et-al-use-first="1" disambiguate-add-givenname="true" disambiguate-add-year-suffix="true" collapse="year" cite-group-delimiter=", ">+    <sort>+      <key macro="author" names-min="3" names-use-first="1"/>+      <key macro="issued"/>+    </sort>+    <layout prefix="(" suffix=")" delimiter="; ">+      <group delimiter=", ">+        <text macro="author"/>+        <text macro="issued"/>+      </group>+    </layout>+  </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {+    "id": "hennelly2023",+    "type": "article-journal",+    "author": [+      {"family": "Hennelly", "given": "Lauren M."},+      {"family": "Sarwar", "given": "Ghulam"},+      {"family": "Fatima", "given": "Hira"}+    ],+    "title": "Genomic analysis of wolves from Pakistan",+    "container-title": "Journal of Heredity",+    "issued": {"date-parts": [[2023]]}+  },+  {+    "id": "hennelly2025",+    "type": "article-journal",+    "author": [+      {"family": "Hennelly", "given": "Lauren M."},+      {"family": "Parreira", "given": "Bárbara R."},+      {"family": "Noble", "given": "Ash"}+    ],+    "title": "Continent-wide view of genomic diversity in the wolves of Asia",+    "container-title": "Communications Biology",+    "issued": {"date-parts": [[2025]]}+  }+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/extra/date_LiteralOnlyWhereYearIsRendered.txt view
@@ -0,0 +1,45 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<+++>>===== RESULT =====>>+in press Forthcoming results; 2020 Earlier results (May)+<<===== RESULT =====<<+++>>===== CSL =====>>+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0">+  <info>+    <id />+    <title />+    <updated>2026-10-11T00:00:00+00:00</updated>+  </info>+  <citation>+    <layout delimiter="; ">+      <group delimiter=" ">+        <date variable="issued">+          <date-part name="year"/>+        </date>+        <text variable="title"/>+        <date variable="issued" prefix="(" suffix=")">+          <date-part name="month"/>+        </date>+      </group>+    </layout>+  </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {"id": "ITEM-1", "type": "article-journal", "title": "Forthcoming results", "issued": {"literal": "in press"}},+  {"id": "ITEM-2", "type": "article-journal", "title": "Earlier results", "issued": {"date-parts": [[2020, 5]]}}+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/extra/etal_AndOthersWithEtAlUseLast.txt view
@@ -0,0 +1,101 @@+Literal "others" as the last name, which is what BibTeX's "and others"+becomes, is rendered as "et al." with et-al-use-last="true", whether the+list is shorter than et-al-min, exactly as long, or longer.  It is not+a real last name: it must not be printed after "et al." and must not+turn the "et al." into an ellipsis.  A complete list still gets the+ellipsis and its real last name.++>>===== MODE =====>>+bibliography+<<===== MODE =====<<+++>>===== RESULT =====>>+<div class="csl-bib-body">+  <div class="csl-entry">Alice Adams, Bob Brown, Carol Clark, et al.</div>+  <div class="csl-entry">Alice Adams, Bob Brown, Carol Clark, et al.</div>+  <div class="csl-entry">Alice Adams, Bob Brown, et al.</div>+  <div class="csl-entry">Alice Adams, Bob Brown, Carol Clark, … Frank Foster</div>+</div>+<<===== RESULT =====<<+++>>===== CSL =====>>+<style+      xmlns="http://purl.org/net/xbiblio/csl"+      class="note"+      version="1.0">+  <info>+    <id />+    <title />+    <updated>2009-08-10T04:49:00+09:00</updated>+  </info>+  <citation>+    <layout>+      <text value="Oops"/>+    </layout>+  </citation>+  <bibliography>+    <layout>+      <names variable="author">+        <name et-al-min="5" et-al-use-first="3" et-al-use-last="true"/>+      </names>+    </layout>+  </bibliography>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {+    "id": "others-above-min",+    "type": "article-journal",+    "author": [+      {"family": "Adams", "given": "Alice"},+      {"family": "Brown", "given": "Bob"},+      {"family": "Clark", "given": "Carol"},+      {"family": "Davis", "given": "Dan"},+      {"family": "Evans", "given": "Eve"},+      {"literal": "others"}+    ]+  },+  {+    "id": "others-at-min",+    "type": "article-journal",+    "author": [+      {"family": "Adams", "given": "Alice"},+      {"family": "Brown", "given": "Bob"},+      {"family": "Clark", "given": "Carol"},+      {"family": "Davis", "given": "Dan"},+      {"literal": "others"}+    ]+  },+  {+    "id": "others-below-min",+    "type": "article-journal",+    "author": [+      {"family": "Adams", "given": "Alice"},+      {"family": "Brown", "given": "Bob"},+      {"literal": "others"}+    ]+  },+  {+    "id": "complete-above-min",+    "type": "article-journal",+    "author": [+      {"family": "Adams", "given": "Alice"},+      {"family": "Brown", "given": "Bob"},+      {"family": "Clark", "given": "Carol"},+      {"family": "Davis", "given": "Dan"},+      {"family": "Evans", "given": "Eve"},+      {"family": "Foster", "given": "Frank"}+    ]+  }+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/extra/flipflop_NoItalicsOnCjkText.txt view
@@ -0,0 +1,38 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<+++>>===== RESULT =====>>+农业经济; <i>Seed Science</i>; <i>مجلة الزراعة</i>+<<===== RESULT =====<<+++>>===== CSL =====>>+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0">+  <info>+    <id />+    <title />+    <updated>2026-10-11T00:00:00+00:00</updated>+  </info>+  <citation>+    <layout delimiter="; ">+      <text variable="container-title" font-style="italic"/>+    </layout>+  </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {"id": "ITEM-1", "type": "article-journal", "container-title": "农业经济"},+  {"id": "ITEM-2", "type": "article-journal", "container-title": "Seed Science"},+  {"id": "ITEM-3", "type": "article-journal", "container-title": "مجلة الزراعة"}+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/extra/label_DigitGroupedNumberIsSingular.txt view
@@ -0,0 +1,43 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<+++>>===== RESULT =====>>+no. US 9,876,543; no. 9,876,543; no. US 123; nos. 5, 6; nos. 5-7+<<===== RESULT =====<<+++>>===== CSL =====>>+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0">+  <info>+    <id />+    <title />+    <updated>2026-10-11T00:00:00+00:00</updated>+  </info>+  <citation>+    <layout delimiter="; ">+      <group delimiter=" ">+        <label variable="number" form="short"/>+        <text variable="number"/>+      </group>+    </layout>+  </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {"id": "ITEM-1", "type": "patent", "number": "US 9,876,543"},+  {"id": "ITEM-2", "type": "patent", "number": "9,876,543"},+  {"id": "ITEM-3", "type": "patent", "number": "US 123"},+  {"id": "ITEM-4", "type": "patent", "number": "5, 6"},+  {"id": "ITEM-5", "type": "patent", "number": "5-7"}+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/extra/name_InitializeArabicGivenName.txt view
@@ -0,0 +1,39 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<+++>>===== RESULT =====>>+الحسن, أ.; Doe, J.+<<===== RESULT =====<<+++>>===== CSL =====>>+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0">+  <info>+    <id />+    <title />+    <updated>2026-10-11T00:00:00+00:00</updated>+  </info>+  <citation>+    <layout delimiter="; ">+      <names variable="author">+        <name initialize-with=". " name-as-sort-order="all"/>+      </names>+    </layout>+  </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {"id": "ITEM-1", "type": "book", "author": [{"family": "الحسن", "given": "أحمد"}]},+  {"id": "ITEM-2", "type": "book", "author": [{"family": "Doe", "given": "John"}]}+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/extra/name_NoInitialsForCjkGivenName.txt view
@@ -0,0 +1,39 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<+++>>===== RESULT =====>>+王小明; J. Doe+<<===== RESULT =====<<+++>>===== CSL =====>>+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0">+  <info>+    <id />+    <title />+    <updated>2026-10-11T00:00:00+00:00</updated>+  </info>+  <citation>+    <layout delimiter="; ">+      <names variable="author">+        <name initialize-with=". "/>+      </names>+    </layout>+  </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+  {"id": "ITEM-1", "type": "book", "author": [{"family": "王", "given": "小明"}]},+  {"id": "ITEM-2", "type": "book", "author": [{"family": "Doe", "given": "John"}]}+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<