Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- **Names with particles sort where each style puts them.** A surname that opens with a lowercase particle, such as "van Duijnhoven", "de la Cruz", "von Neumann", or "d'Alembert", was sorted under the particle in every style. Chicago (both styles) and Vancouver sort by the surname proper, so Chicago lists "Duijnhoven, Elza van" under D, and the formatter now renders it that way. APA, MLA, Harvard, and the other styles keep the particle in front and still sort under V. A capitalized form ("Van Buren") is part of the surname and always sorts under V. Names are stored as entered, so exports and Edit fields are unchanged. Bibliographies saved earlier stay valid in the editor and take the new order the next time they are saved. ([#142](https://github.com/dknauss/Borges/issues/142))
- **No period after a title that ends in "?" or "!".** Chicago (both styles) and MLA rendered a quoted title such as "What Are Reduced Penetrance and Variable Expressivity?" as `“…Expressivity?.”`, with a period inside the closing quote. The title now closes with its own mark: `“…Expressivity?”`. A comma after such a title is kept, as Chicago allows. Bibliographies saved earlier keep the old text until they are reformatted or saved again.

## [1.11.0] - 2026-10-14

Expand Down
5 changes: 5 additions & 0 deletions bibliography-builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,11 @@ function bibliography_builder_normalize_formatted_text( $text, $style ) {
$text = str_replace( array( '”,', '”.' ), array( ',”', '.”' ), $text );
}

// A title ending in "?" or "!" takes no period after it, but citeproc-php
// adds the title's "." suffix anyway ("What Is It?.”"). Commas stay: CMOS
// keeps one after a question-mark title (“Are You a Doctor?,” in notes).
$text = preg_replace( '/([?!])([”’]?)\./u', '$1$2', $text );

// citeproc-php initializes only capitalized parts of a hyphenated given
// name, so "Ji-woo" comes out "J.- woo". Initialize the second part too.
$text = preg_replace_callback(
Expand Down
4 changes: 2 additions & 2 deletions docs/current-metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ Source and LOC figures last verified: **2026-10-06** against the heading-level b

| Metric | Value | Re-derivation command |
|---|---|---|
| Main plugin file (`bibliography-builder.php`) | **2,208** | `wc -l bibliography-builder.php` |
| All first-party PHP (excl. vendor, tests, scripts, playground, packages, output, node_modules, generated `build/`) | **10,084** | `find . -name '*.php' -not -path './vendor/*' -not -path './node_modules/*' -not -path './tests/*' -not -path './packages/*' -not -path './scripts/*' -not -path './playground/*' -not -path './output/*' -not -path './build/*' -print0 \| xargs -0 wc -l \| tail -1` |
| Main plugin file (`bibliography-builder.php`) | **2,213** | `wc -l bibliography-builder.php` |
| All first-party PHP (excl. vendor, tests, scripts, playground, packages, output, node_modules, generated `build/`) | **10,089** | `find . -name '*.php' -not -path './vendor/*' -not -path './node_modules/*' -not -path './tests/*' -not -path './packages/*' -not -path './scripts/*' -not -path './playground/*' -not -path './output/*' -not -path './build/*' -print0 \| xargs -0 wc -l \| tail -1` |
| JS source (`src/`, excl. `*.test.js`) | **11,381** | `find ./src -name '*.js' -not -name '*.test.js' -print0 \| xargs -0 wc -l \| tail -1` |
| Shipped frontend runtime (`build/view.js`, minified) | **1,449 bytes** | `npm run build` then `wc -c < build/view.js` |

Expand Down
61 changes: 61 additions & 0 deletions tests/phpunit/CslStyleGoldenTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,67 @@ public function test_current_edition_rules_outside_the_corpus() {
);
}

/**
* A title ending in "?" or "!" already closes its element, so no period
* follows it (CMOS 18 6.133; MLA 9 1.3). citeproc-php moves the title's
* "." suffix inside the closing quote without checking, giving "?.”".
*/
public function test_title_ending_in_question_or_exclamation_mark_takes_no_period() {
$webpage = array(
'type' => 'webpage',
'title' => 'What Are Reduced Penetrance and Variable Expressivity?',
'container-title' => 'MedlinePlus',
'URL' => 'https://example.org/q',
'issued' => array( 'date-parts' => array( array( 2021, 7, 28 ) ) ),
);
$article = array(
'type' => 'article-journal',
'title' => 'Diabetes Technology: What’s New in 2025?',
'author' => array(
array(
'family' => 'Moss',
'given' => 'Ann',
),
),
'container-title' => 'Diabetes Care',
'volume' => '48',
'page' => '1-10',
'issued' => array( 'date-parts' => array( array( 2025 ) ) ),
);
$exclaim = array(
'type' => 'webpage',
'title' => 'Stop the Presses!',
'container-title' => 'Site',
'URL' => 'https://example.org/b',
'issued' => array( 'date-parts' => array( array( 2024 ) ) ),
);
$items = array( $webpage, $article, $exclaim );

$this->assertSame(
array(
'“What Are Reduced Penetrance and Variable Expressivity?” MedlinePlus. July 28, 2021. https://example.org/q.',
'Moss, Ann. “Diabetes Technology: What’s New in 2025?” Diabetes Care 48 (2025): 1–10.',
'“Stop the Presses!” Site. 2024. https://example.org/b.',
),
bibliography_builder_format_csl_items( $items, 'chicago-notes-bibliography' )
);

$this->assertSame(
array(
'“What Are Reduced Penetrance and Variable Expressivity?” MedlinePlus, 28 July 2021, https://example.org/q.',
'Moss, Ann. “Diabetes Technology: What’s New in 2025?” Diabetes Care, vol. 48, 2025, pp. 1–10.',
'“Stop the Presses!” Site, 2024, https://example.org/b.',
),
bibliography_builder_format_csl_items( $items, 'mla-9' )
);

foreach ( array_keys( bibliography_builder_get_formatter_style_definitions() ) as $style_key ) {
foreach ( bibliography_builder_format_csl_items( $items, $style_key ) as $text ) {
$this->assertDoesNotMatchRegularExpression( '/[?!][”’]?\./u', $text, $style_key );
}
}
}

/**
* The formatter reads vendor/citation-style-language/, a Composer copy of
* packages/. A stale copy once hid a style regression; fail on any drift.
Expand Down
Loading