Skip to content

feat(fr-bnf): French locale (fr_FR) + BNF SRU/UNIMARC integration#131

Open
fabiodalez-dev wants to merge 16 commits into
feat/all-prs-mergedfrom
feat/fr-bnf-integration
Open

feat(fr-bnf): French locale (fr_FR) + BNF SRU/UNIMARC integration#131
fabiodalez-dev wants to merge 16 commits into
feat/all-prs-mergedfrom
feat/fr-bnf-integration

Conversation

@fabiodalez-dev
Copy link
Copy Markdown
Owner

Summary

  • Locale fr_FR completa: locale/fr_FR.json (4080 chiavi), locale/routes_fr_FR.json (slug inglesi), installer/database/data_fr_FR.sql (generi, CMS, email in francese), radio tricolore nell'installer step0
  • Backfill in tutte le data_XX.sql e in migrate_0.7.4.sql (INSERT IGNORE) per installazioni esistenti
  • BNF SRU/UNIMARC: SruClient.php — metodo parseMarcxchangeXml() per campi UNIMARC (200 titolo, 214 editore/anno, 700/701/702 autori, 010 ISBN, 073 EAN, 215 pagine, 461 collana, 676 Dewey, 101 lingua, 330 abstract, 600-608 soggetti); namespace MARCXchange info:lc/xmlns/marcxchange-v2; flag quote_search_terms per quoting CQL BNF; fallback local-name() su numberOfRecords
  • Admin UI Z39: preset BNF (unimarcxchange, v1.2, quote_search_terms=true), campo version, checkbox quote, dropdown syntax con opzione unimarcxchange
  • Book form: hidden inputs scraped_numero_serie e scraped_dimensions
  • 71 test E2E riutilizzabili: install-french.spec.js (48 test in 6 fasi) + bnf-sru-features.spec.js (23 test in 4 fasi); PHPStan level 5: OK

Test Plan

  • install-french.spec.js — 44 passati, 4 saltati (installer skip corretto), 0 falliti
  • bnf-sru-features.spec.js — 23/23 passati
  • PHPStan level 5: nessun errore
  • Verifica BNF SRU reale: ISBN 2070360024 (Le Grand Meaulnes) restituisce record UNIMARC
  • Fresh install in francese: step0 mostra radio fr_FR con bandiera tricolore
  • Upgrade da versione precedente: migrate_0.7.4.sql inserisce fr_FR nella tabella languages

- Add fr_FR locale: locale/fr_FR.json (4080 keys), routes_fr_FR.json
- Add installer/database/data_fr_FR.sql with French genres, CMS, emails
- Backfill fr_FR in data_it_IT.sql, data_en_US.sql, data_de_DE.sql
- Backfill fr_FR in migrate_0.7.4.sql (existing installs upgrade path)
- Add French language option in installer step0.php (tricolor SVG)
- Register fr_FR in Installer.php, installer/index.php, config/default_texts.php
- SruClient.php: add parseMarcxchangeXml() for UNIMARC fields (200/214/700/701/702/010/073/215/461/676/101/330/600-608)
- SruClient.php: register mxc namespace (info:lc/xmlns/marcxchange-v2)
- SruClient.php: add quote_search_terms server flag for BNF CQL quoting
- SruClient.php: add local-name() fallback for numberOfRecords XPath
- plugins.php: add BNF preset (unimarcxchange, v1.2, quote_search_terms)
- plugins.php: add version and quote_search_terms fields to Z39 server rows
- book_form.php: add scraped_numero_serie and scraped_dimensions hidden inputs
- 71 E2E tests: install-french.spec.js (48) + bnf-sru-features.spec.js (23)
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9455e70d-4b41-4ab7-ae5c-b203a79deb01

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fr-bnf-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Code review fixes:
- SruClient: catch \Throwable (not \Exception) in retry loop — TypeError from strict_types would bypass retry
- SruClient: restore libxml_use_internal_errors state after XML parse; use \RuntimeException for parse errors
- plugins.php: BNF preset URL changed from http:// to https://
- data_fr_FR.sql: hero button_link changed from /catalogue to /catalog (matches routes_fr_FR.json)
- routes_fr_FR.json: add missing bibframe.book route (parity with it_IT/en_US)

New test file install-compat-standard.spec.js (59 tests, 8 phases):
- Phase 1: all seed files include all 4 supported languages
- Phase 2: fr_FR route keys are compatible with it_IT (count, presence, no duplicates)
- Phase 3: migrate_0.7.4.sql backfills fr_FR with correct column names
- Phase 4: installer code registers fr_FR in all required locations
- Phase 5: DB state after upgrade (fr_FR present, no duplicate defaults)
- Phase 6: BNF preset uses HTTPS
- Phase 7: SruClient \Throwable catch, libxml state, RuntimeException
- Phase 8: data_fr_FR button links match routes_fr_FR.json (no Italian/German slugs)
- Restore `novalidate` on #bookForm (removed in 2c0264e), which caused
  browser HTML5 validation to intercept submit before the SweetAlert2
  handler could fire — breaking book create/edit confirmation dialog
- Fix smoke-install.spec.js search parameter: the /api/libri endpoint
  uses `search_text` not `search[value]` (DataTables format), so the
  wrong parameter silently returned the first 5 alphabetical books
  instead of the searched title, making createdBookId wrong and
  the edit-title verification fail
- fr_FR.json: +65 translated strings for LibraryThing plugin sections
  (Recensione e Valutazione, Visibilità nel Frontend, Condizione Fisica, etc.)
- en_US.json, de_DE.json: add missing "Valore Corrente" translation
- book_form.php: wrap LibraryThing field labels with __() so they
  respect the active locale (previously always rendered in Italian)
- full-test.spec.js: Phase 21 (4 serial tests) verifies fr/en/de/it
  language switching including LibraryThing French strings assertion
- Minor fixes: archives plugin.json trailing comma, CI workflow tweaks,
  phpstan baseline cleanup, test file updates
Upgrades from 0.7.4 → 0.7.5 now execute migrate_0.7.5.sql which
inserts the fr_FR language row (INSERT IGNORE, idempotent).
0.7.4 installs that already have the row will see a no-op.
…activate fr_FR

INSERT IGNORE left pre-existing fr_FR rows with is_active=0 untouched.
ON DUPLICATE KEY UPDATE now forces is_active=1 on upgrade, so the language
bootstrap (step 3: WHERE is_default=1 AND is_active=1) can find the French
locale after the admin sets it as default.
setDefault() now sets is_active=1 alongside is_default=1, preventing the
inconsistent state where a language is marked default but inactive.
I18n::bootstrap() step 3 requires is_active=1, so an inactive default
language was silently ignored and the app stayed in the previous locale.

Also adds missing bibframe.book route key to routes_de_DE.json.
… dev schema detected

Pre-release dev builds used source_code/source_id column names. The current
migration expects source/authority_id. CREATE TABLE IF NOT EXISTS skipped the
DDL on installations that had the dev table, causing ADD KEY idx_authority
(source, authority_id) to fail with 'Key column source does not exist'.

Add a guard that detects the old schema via source_code column presence and
drops the empty dev table before recreating with the correct schema.
…sue #130)

On Windows, RecursiveDirectoryIterator::getPathname() returns backslash paths,
breaking str_replace prefix-stripping and mkdir target construction in
copyDirectoryRecursive() and copyDirectory(). All paths now normalized to
forward slashes at function entry; realpath() returns also normalized.

Also remove unused SEED_FILES variable from install-compat-standard.spec.js
(CodeQL js/unused-local-variable #20) and harden test selectors.
…ition

archives-upload-assets.spec.js ensureUploadFixtures():
- Remove existsSync+writeFileSync TOCTOU pattern (race condition fix)
- Replace hardcoded /tmp/ paths with path.join(os.tmpdir(), ...) for
  cross-platform safety and to satisfy CodeQL js/insecure-tmp rule
__() uses Italian as the native fallback — keys ARE Italian text, so
if a key is missing from it_IT.json the function returns the key itself.
it_IT.json is intentionally sparse (overrides only).

Only en_US ↔ de_DE requires exact key-set parity.
CodeQL js/insecure-temporary-file:
  - Add binary fixtures to tests/fixtures/ (jpg/pdf/mp3, csv/tsv) and
    allow the directory in .gitignore (mirrors tests/seeds/ pattern)
  - archives-upload-assets.spec.js reads from tests/fixtures/ instead of
    writing predictable filenames to os.tmpdir()

OAI-PMH:
  - Catch remaining \Throwable in record serialisation loop to skip
    malformed metadata with a warning log instead of crashing

E2E test stability:
  - bibframe-persistent-uri: check @id contains /id/work/{id}, not suffix /work
  - book-form-comprehensive: simplified Choices.js check (no XPath)
  - bulk-enrich: nullify ISBN before DELETE to avoid UK constraint on re-run
  - multisource-scraping: accept 'Z39.50/SRU' as valid source for Nevermind
  - smoke-install: make author input fill conditional (visible guard)
Fix groups (committed):
- [FG-1] F002, F007 — storage/plugins/archives/ArchivesPlugin.php: verified
- [FG-2] F003 — storage/plugins/bibframe-linked-data/BibframeLinkedDataPlugin.php: verified
- [FG-3] F005 — storage/plugins/oai-pmh-server/OaiPmhServerPlugin.php: verified
- [FG-4] F010 — app/Controllers/FrontendController.php: verified
- [FG-5] F020 — storage/plugins/oai-pmh-server/views/book-digital-assets.php: verified
- [FG-6] F023 — storage/plugins/archives/views/authorities/show.php: verified
- [FG-7] F027 — app/Views/admin/plugins.php: verified
- [FG-8] F028 — storage/plugins/archives/views/show.php: verified

Post-fix review: 8/8 groups verified complete; 0 group(s) partial; 0 group(s) reverted.
Fix groups (committed):
- [FG-1] F003 — storage/plugins/z39-server/classes/SRUServer.php: verified
- [FG-2] F018 — storage/plugins/z39-server/classes/SruClient.php: verified
- [FG-3] F019 — storage/plugins/openurl-resolver/OpenUrlResolverPlugin.php: verified
- [FG-4] F020 — storage/plugins/resource-sync/ResourceSyncPlugin.php: verified
- [FG-5] F023 — app/Controllers/ScrapeController.php: verified
- [FG-6] F024, F025, F045 — storage/plugins/ncip-server/NcipServerPlugin.php: verified
- [FG-7] F027 — storage/plugins/viaf-authority/ViafAuthorityPlugin.php: verified
- [FG-8] F028 — storage/plugins/archives/ArchivesPlugin.php: verified
- [FG-9] F029 — storage/plugins/oai-pmh-server/OaiPmhServerPlugin.php: verified

Post-fix review: 9/9 groups verified complete; 0 group(s) partial; 0 group(s) reverted.
Fix groups (committed):
- [FG-1] F007, F041, F012 — storage/plugins/oai-pmh-server/OaiPmhServerPlugin.php: verified
- [FG-2] F031 — app/Views/frontend/layout.php, locale/en_US.json, locale/de_DE.json, locale/fr_FR.json, locale/it_IT.json: verified
- [FG-3] F034 — app/Views/admin/plugins.php: verified

Post-fix review: 3/3 groups verified complete; 0 group(s) partial; 0 group(s) reverted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant