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 +26/−0
- citeproc.cabal +1/−1
- src/Citeproc/CaseTransform.hs +77/−3
- src/Citeproc/Eval.hs +76/−3
- src/Citeproc/Types.hs +18/−1
- test/csl/flipflop_OrphanQuote.expected +1/−1
- test/csl/textcase_StopWordBeforeHyphen.expected +1/−0
- test/extra/collapse_NamelessCitesWithDifferentTitlesDoNotGroup.txt +52/−0
- test/extra/collapse_TmpRealStyle.txt +285/−0
- test/extra/collapse_YearWithIdenticalEtAlNames.txt +76/−0
- test/extra/date_LiteralOnlyWhereYearIsRendered.txt +45/−0
- test/extra/etal_AndOthersWithEtAlUseLast.txt +101/−0
- test/extra/flipflop_NoItalicsOnCjkText.txt +38/−0
- test/extra/label_DigitGroupedNumberIsSingular.txt +43/−0
- test/extra/name_InitializeArabicGivenName.txt +39/−0
- test/extra/name_NoInitialsForCjkGivenName.txt +39/−0
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 =====<<